summaryrefslogtreecommitdiffstats
path: root/yql/essentials/utils/docs/link.cpp
diff options
context:
space:
mode:
authorvitya-smirnov <[email protected]>2025-07-30 11:26:26 +0300
committervitya-smirnov <[email protected]>2025-07-30 11:38:37 +0300
commitcf9f591e5c90bf964bb922c0f6c3716045972b02 (patch)
tree36d4eb0816606653836399ac32ea58d2eca08b53 /yql/essentials/utils/docs/link.cpp
parentada885655c2e21f6b55e2d3d724e57c9a1fdb843 (diff)
YQL-20112: Improve dramatically yql/utils/docs
Introduced `links.json` format to link names to documentation sections. Implement general links verification framework. Also fixed two small typos. Extended Description: https://nda.ya.ru/t/zR4voivb7GzD9r. commit_hash:e72db0e202b4ff612374c73fa384f70d029f0ef0
Diffstat (limited to 'yql/essentials/utils/docs/link.cpp')
-rw-r--r--yql/essentials/utils/docs/link.cpp119
1 files changed, 119 insertions, 0 deletions
diff --git a/yql/essentials/utils/docs/link.cpp b/yql/essentials/utils/docs/link.cpp
new file mode 100644
index 00000000000..2fd5321ff12
--- /dev/null
+++ b/yql/essentials/utils/docs/link.cpp
@@ -0,0 +1,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;
+ }
+}