xee_interpreter/xml/
annotation.rs

1use ahash::{HashMap, HashMapExt};
2use xot::Xot;
3
4#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
5pub(crate) struct DocumentOrder(usize, usize);
6
7impl DocumentOrder {
8    pub(crate) fn generate_id(&self) -> String {
9        // must be alphanumeric and start with alphabetic character, so we
10        // cannot use _ or - as separators
11        format!("id{}s{}", self.0, self.1)
12    }
13}
14
15#[derive(Debug, Clone)]
16pub(crate) struct Annotation {
17    pub(crate) document_order: DocumentOrder,
18}
19
20impl Annotation {
21    pub(crate) fn generate_id(&self) -> String {
22        self.document_order.generate_id()
23    }
24}
25
26#[derive(Debug, Clone)]
27pub struct Annotations {
28    // each document has a different id, so track this
29    document_id: usize,
30    map: HashMap<xot::Node, Annotation>,
31}
32
33impl Annotations {
34    pub(crate) fn new() -> Self {
35        Self {
36            map: HashMap::new(),
37            document_id: 0,
38        }
39    }
40
41    pub(crate) fn clear(&mut self) {
42        self.map.clear();
43        self.document_id = 0;
44    }
45
46    pub(crate) fn add(&mut self, xot: &Xot, doc: xot::Node) {
47        // if we already know this document, we are done
48        if self.map.contains_key(&doc) {
49            return;
50        }
51        let map_init = xot.all_descendants(doc).enumerate().map(|(i, node)| {
52            (
53                node,
54                Annotation {
55                    document_order: DocumentOrder(self.document_id, i),
56                },
57            )
58        });
59        self.map.extend(map_init);
60        self.document_id += 1;
61    }
62
63    pub(crate) fn get(&self, node: xot::Node) -> Option<&Annotation> {
64        self.map.get(&node)
65    }
66
67    pub(crate) fn document_order(&self, node: xot::Node) -> DocumentOrder {
68        self.get(node)
69            .map(|annotation| annotation.document_order)
70            .expect("node not found")
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_single_document() {
80        let mut xot = Xot::new();
81        let doc = xot.parse(r#"<root><a/><b/></root>"#).unwrap();
82        let root = xot.document_element(doc).unwrap();
83        let a = xot.first_child(root).unwrap();
84        let b = xot.next_sibling(a).unwrap();
85
86        let mut annotations = Annotations::new();
87        annotations.add(&xot, doc);
88
89        assert!(annotations.document_order(a) < annotations.document_order(b));
90        assert!(annotations.document_order(root) < annotations.document_order(a));
91    }
92
93    #[test]
94    fn test_multiple_documents() {
95        let mut xot = Xot::new();
96        let doc0 = xot.parse(r#"<root><a/><b/></root>"#).unwrap();
97        let root0 = xot.document_element(doc0).unwrap();
98        let a = xot.first_child(root0).unwrap();
99        let b = xot.next_sibling(a).unwrap();
100
101        let doc1 = xot.parse(r#"<root><c/><d/></root>"#).unwrap();
102        let root1 = xot.document_element(doc1).unwrap();
103        let c = xot.first_child(root1).unwrap();
104        let d = xot.next_sibling(c).unwrap();
105
106        let mut annotations = Annotations::new();
107        annotations.add(&xot, doc0);
108        annotations.add(&xot, doc1);
109
110        assert!(annotations.document_order(a) < annotations.document_order(b));
111        assert!(annotations.document_order(root0) < annotations.document_order(a));
112        assert!(annotations.document_order(c) < annotations.document_order(d));
113        assert!(annotations.document_order(root1) < annotations.document_order(c));
114        assert!(annotations.document_order(root0) < annotations.document_order(root1));
115        assert!(annotations.document_order(a) < annotations.document_order(c));
116    }
117
118    #[test]
119    fn test_attributes() {
120        let mut xot = Xot::new();
121        let doc = xot
122            .parse(r#"<root><x a="1" b="2"/><y c="3"/></root>"#)
123            .unwrap();
124        let root = xot.document_element(doc).unwrap();
125        let x = xot.first_child(root).unwrap();
126        let y = xot.next_sibling(x).unwrap();
127        let a = xot.attributes(x).get_node(xot.name("a").unwrap()).unwrap();
128        let b = xot.attributes(x).get_node(xot.name("b").unwrap()).unwrap();
129        let c = xot.attributes(y).get_node(xot.name("c").unwrap()).unwrap();
130
131        let mut annotations = Annotations::new();
132        annotations.add(&xot, doc);
133
134        assert!(annotations.document_order(a) < annotations.document_order(b));
135        assert!(annotations.document_order(x) < annotations.document_order(a));
136        assert!(annotations.document_order(y) < annotations.document_order(c));
137        assert!(annotations.document_order(b) < annotations.document_order(y));
138    }
139}