Skip to main content

specfold_core/authoring/
refs.rs

1//! Rewriting references to a document when its id changes.
2
3use regex::Regex;
4use serde_json::{Map, Value};
5
6/// Replaces `[[old]]` and `[[old#anchor]]` (and `[[old|label]]`) with the new id in Markdown text.
7pub fn rewrite_body(text: &str, old: &str, new: &str) -> String {
8    let re = Regex::new(&format!(r"\[\[{}(#[^\]|]*)?(\|[^\]]*)?\]\]", regex::escape(old))).unwrap();
9    re.replace_all(text, |c: &regex::Captures| {
10        format!("[[{new}{}{}]]", c.get(1).map_or("", |m| m.as_str()), c.get(2).map_or("", |m| m.as_str()))
11    })
12    .into_owned()
13}
14
15/// Replaces the id inside `rel.*` lists, keeping anchors.
16pub fn rewrite_rel(map: &mut Map<String, Value>, old: &str, new: &str) -> bool {
17    let Some(Value::Object(rel)) = map.get_mut("rel") else { return false };
18    let mut changed = false;
19    for v in rel.values_mut() {
20        let Value::Array(items) = v else { continue };
21        for item in items.iter_mut() {
22            let Some(s) = item.as_str() else { continue };
23            let (target, anchor) = s.split_once('#').map(|(t, a)| (t, Some(a))).unwrap_or((s, None));
24            if target == old {
25                *item = Value::String(match anchor {
26                    Some(a) => format!("{new}#{a}"),
27                    None => new.to_string(),
28                });
29                changed = true;
30            }
31        }
32    }
33    changed
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn rewrites() {
42        let t = "a [[adr:x/1]] b [[adr:x/1#K-1|see]] c [[adr:x/10]]";
43        assert_eq!(rewrite_body(t, "adr:x/1", "adr:y/1"), "a [[adr:y/1]] b [[adr:y/1#K-1|see]] c [[adr:x/10]]");
44        let mut m: Map<String, Value> =
45            serde_json::from_str(r#"{"rel":{"implements":["adr:x/1#A","sot:z/q"]}}"#).unwrap();
46        assert!(rewrite_rel(&mut m, "adr:x/1", "adr:y/1"));
47        assert_eq!(m["rel"]["implements"][0], "adr:y/1#A");
48    }
49}