summaryrefslogtreecommitdiffstats
path: root/yql/essentials/utils/docs/link.cpp
blob: af3d648db9eafc0d0a4bd631b39da8ec4de7aaa4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include "link.h"

#include "name.h"

#include <yql/essentials/utils/yql_panic.h>

#include <contrib/libs/re2/re2/re2.h>

#include <util/string/builder.h>
#include <util/string/split.h>

namespace NYql::NDocs {

TLinkTarget TLinkTarget::Parse(TStringBuf string) {
    static const RE2 Regex(R"re(([^#?()]*)(#[^?()]*)?)re");

    TString path;
    TString anchor;
    if (RE2::FullMatch(string, Regex, &path, &anchor)) {
        if (!anchor.empty()) {
            YQL_ENSURE(anchor.StartsWith('#'));
            anchor.erase(0, 1);
        }

        return {
            .RelativePath = path,
            .Anchor = !anchor.empty() ? TMaybe<TString>(anchor) : Nothing(),
        };
    }

    throw yexception()
        << "invalid link target '" << string << "': "
        << "does not match regex '" << Regex.pattern() << "'";
}

TMaybe<TLinkTarget> LookupUDF(const TLinks& links, TStringBuf name) {
    const auto udf = SplitUDF(TString(name));
    YQL_ENSURE(udf, "Invalid UDF: " << name);

    const auto [module, function] = *udf;

    if (const TLinkTarget* target = nullptr;
        (target = links.FindPtr(module + "::" + function)) ||
        (target = links.FindPtr(module + "::" + "*"))) {
        return *target;
    }

    return Nothing();
}

TMaybe<TLinkTarget> LookupBasic(const TLinks& links, TStringBuf name) {
    TMaybe<TLinkKey> key = NormalizedName(TString(name));
    if (!key) {
        return Nothing();
    }

    if (const TLinkTarget* target = links.FindPtr(*key)) {
        return *target;
    }

    return Nothing();
}

TMaybe<TLinkTarget> Lookup(const TLinks& links, TStringBuf name) {
    if (IsUDF(name)) {
        return LookupUDF(links, name);
    }

    return LookupBasic(links, name);
}

TLinkKey ParseLinkKey(TStringBuf string) {
    static RE2 UDFRegex(TStringBuilder()
                        << "(" << NormalizedNameRegex.pattern() << ")\\:\\:("
                        << "\\*|" << NormalizedNameRegex.pattern() << ")");

    if (IsNormalizedName(string)) {
        return TString(string);
    }

    if (RE2::FullMatch(string, UDFRegex)) {
        return TString(string);
    }

    ythrow yexception()
        << "invalid link key '" << string << "': "
        << "does not match any regex";
}

TLinks ParseLinks(const NJson::TJsonValue& json) {
    TLinks links;
    for (const auto& [keyString, value] : json.GetMapSafe()) {
        TLinkKey key = ParseLinkKey(keyString);
        TLinkTarget target = TLinkTarget::Parse(value.GetStringSafe());
        links[std::move(key)] = std::move(target);
    }
    return links;
}

TLinks Merge(TLinks&& lhs, TLinks&& rhs) {
    for (auto& [k, v] : rhs) {
        YQL_ENSURE(
            !lhs.contains(k),
            "Duplicate '" << k << "', old '" << lhs[k] << "', new '" << v << "'");

        lhs[k] = std::move(v);
    }
    return lhs;
}

} // namespace NYql::NDocs

template <>
void Out<NYql::NDocs::TLinkTarget>(IOutputStream& out, const NYql::NDocs::TLinkTarget& target) {
    out << target.RelativePath;
    if (target.Anchor) {
        out << "#" << *target.Anchor;
    }
}