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 | |
7 | pub struct Visitor(serde_json::Value, pub bool); |
8 | |
9 | impl 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 | |
25 | impl 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 ] |
71 | fn 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 | |