1// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
2// Copyright © SixtyFPS GmbH <info@slint.dev>
3// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
4
5// Copyright © SixtyFPS GmbH <info@slint.dev>
6
7pub struct Visitor(serde_json::Value, pub bool);
8
9impl syn::visit_mut::VisitMut for Visitor {
10 fn visit_attribute_mut(&mut self, i: &mut syn::Attribute) {
11 if i.meta.path().is_ident("doc") {
12 if let syn::Meta::NameValue(syn::MetaNameValue {
13 value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit: &mut LitStr), .. }),
14 ..
15 }) = &mut i.meta
16 {
17 let mut doc: String = lit.value();
18 self.process_string(&mut doc);
19 *lit = syn::LitStr::new(&doc, lit.span());
20 }
21 }
22 }
23}
24
25impl Visitor {
26 pub fn new() -> Self {
27 let link_data: serde_json::Value = serde_json::from_str(include_str!(concat!(
28 env!("CARGO_MANIFEST_DIR"),
29 "/link-data.json"
30 )))
31 .expect("Failed to parse link-data.json");
32 Self(link_data, false)
33 }
34
35 pub fn process_string(&mut self, doc: &mut String) {
36 const NEEDLE: &str = "slint:";
37 let mut begin = 0;
38 // search for all occurrences of "slint:foo" and replace it with the link from link-data.json
39 while let Some(pos) = doc[begin..].find(NEEDLE).map(|x| x + begin) {
40 if doc[pos..].starts_with("slint::") {
41 begin = pos + NEEDLE.len();
42 continue;
43 }
44 let end = doc[pos + NEEDLE.len()..]
45 .find([' ', '\n', ']', ')'])
46 .expect("Failed to find end of link");
47 let link = &doc[pos + NEEDLE.len()..][..end];
48 let dst = if let Some(rust_link) = link.strip_prefix("rust:") {
49 format!(
50 "https://releases.slint.dev/{}/docs/rust/{rust_link}",
51 env!("CARGO_PKG_VERSION"),
52 )
53 } else if let Some(dst) = self.0.get(link) {
54 let dst = dst
55 .get("href")
56 .expect("Missing href in link-data.json")
57 .as_str()
58 .expect("invalid string in link-data.json");
59 format!("https://releases.slint.dev/{}/docs/slint/{dst}", env!("CARGO_PKG_VERSION"),)
60 } else {
61 panic!("Unknown link {link}");
62 };
63 doc.replace_range(pos..pos + NEEDLE.len() + link.len(), &dst);
64 begin = pos + dst.len();
65 self.1 = true;
66 }
67 }
68}
69
70#[test]
71fn test_slint_doc() {
72 let mut visitor = Visitor::new();
73
74 let mut string = r"
75 Test [SomeLink](slint:index)
76 Not in a link: slint:index xxx
77 slint::index is not a link
78 slint:index is a link
79 rust link: slint:rust:foobar
80 "
81 .to_owned();
82
83 visitor.process_string(&mut string);
84 assert!(visitor.1);
85 assert_eq!(
86 string,
87 format!(
88 r"
89 Test [SomeLink](https://releases.slint.dev/{0}/docs/slint/)
90 Not in a link: https://releases.slint.dev/{0}/docs/slint/ xxx
91 slint::index is not a link
92 https://releases.slint.dev/{0}/docs/slint/ is a link
93 rust link: https://releases.slint.dev/{0}/docs/rust/foobar
94 ",
95 env!("CARGO_PKG_VERSION")
96 )
97 );
98}
99