summaryrefslogtreecommitdiffstats
path: root/yql/essentials/utils/docs/link.cpp
blob: 2fd5321ff129fd8e2182ce773832e33a761cf255 (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;
    }
}