| 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 | |