i_slint_core_macros/
slint_doc.rs

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), .. }),
14                ..
15            }) = &mut i.meta
16            {
17                let mut doc = 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}