pub struct Visitor(serde_json::Value, pub bool);
impl syn::visit_mut::VisitMut for Visitor {
fn visit_attribute_mut(&mut self, i: &mut syn::Attribute) {
if i.meta.path().is_ident("doc") {
if let syn::Meta::NameValue(syn::MetaNameValue {
value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }),
..
}) = &mut i.meta
{
let mut doc = lit.value();
self.process_string(&mut doc);
*lit = syn::LitStr::new(&doc, lit.span());
}
}
}
}
impl Visitor {
pub fn new() -> Self {
let link_path = concat!(env!("CARGO_MANIFEST_DIR"), "/link-data.json");
let link_data = std::fs::read_to_string(link_path).expect("Failed to read link-data.json");
let link_data: serde_json::Value =
serde_json::from_str(&link_data).expect("Failed to parse link-data.json");
Self(link_data, false)
}
pub fn process_string(&mut self, doc: &mut String) {
const NEEDLE: &str = "slint:";
let mut begin = 0;
while let Some(pos) = doc[begin..].find(NEEDLE).map(|x| x + begin) {
if doc[pos..].starts_with("slint::") {
begin = pos + NEEDLE.len();
continue;
}
let end = doc[pos + NEEDLE.len()..]
.find([' ', '\n', ']', ')'])
.expect("Failed to find end of link");
let link = &doc[pos + NEEDLE.len()..][..end];
let dst = if let Some(rust_link) = link.strip_prefix("rust:") {
format!(
"https://releases.slint.dev/{}/docs/rust/{rust_link}",
env!("CARGO_PKG_VERSION"),
)
} else if let Some(dst) = self.0.get(link) {
let dst = dst
.get("href")
.expect("Missing href in link-data.json")
.as_str()
.expect("invalid string in link-data.json");
format!("https://releases.slint.dev/{}/docs/slint/{dst}", env!("CARGO_PKG_VERSION"),)
} else {
panic!("Unknown link {}", link);
};
doc.replace_range(pos..pos + NEEDLE.len() + link.len(), &dst);
begin = pos + dst.len();
self.1 = true;
}
}
}
#[test]
fn test_slint_doc() {
let mut visitor = Visitor::new();
let mut string = r"
Test [SomeLink](slint:index)
Not in a link: slint:index xxx
slint::index is not a link
slint:index is a link
rust link: slint:rust:foobar
"
.to_owned();
visitor.process_string(&mut string);
assert!(visitor.1);
assert_eq!(
string,
format!(
r"
Test [SomeLink](https://releases.slint.dev/{0}/docs/slint/)
Not in a link: https://releases.slint.dev/{0}/docs/slint/ xxx
slint::index is not a link
https://releases.slint.dev/{0}/docs/slint/ is a link
rust link: https://releases.slint.dev/{0}/docs/rust/foobar
",
env!("CARGO_PKG_VERSION")
)
);
}