Skip to main content

oxml_drawing/
order.rs

1/// Raw XML children grouped by their schema boundary.
2#[derive(Clone, Debug, Default, Eq, PartialEq)]
3pub struct OrderedRawChildren {
4    children: Vec<RawChild>,
5}
6
7#[derive(Clone, Debug, Eq, PartialEq)]
8struct RawChild {
9    boundary: usize,
10    xml: Vec<u8>,
11}
12
13impl OrderedRawChildren {
14    /// Records a raw child at a caller-defined schema boundary.
15    pub fn push(&mut self, boundary: usize, raw_xml: Vec<u8>) {
16        self.children.push(RawChild {
17            boundary,
18            xml: raw_xml,
19        });
20    }
21
22    /// Returns raw children recorded at a schema boundary.
23    pub fn at(&self, boundary: usize) -> impl Iterator<Item = &[u8]> {
24        self.children
25            .iter()
26            .filter(move |child| child.boundary == boundary)
27            .map(|child| child.xml.as_slice())
28    }
29
30    /// Returns whether no raw children have been recorded.
31    pub fn is_empty(&self) -> bool {
32        self.children.is_empty()
33    }
34
35    /// Moves raw children at and after one boundary to make room for a new
36    /// modelled child without changing their relative order.
37    pub fn shift_boundaries_from(&mut self, boundary: usize) {
38        for child in &mut self.children {
39            if child.boundary >= boundary {
40                child.boundary += 1;
41            }
42        }
43    }
44
45    /// Returns raw children at their effective boundary after edits to a
46    /// public collection. Each original boundary is anchored to the next
47    /// surviving original item, or to the trailing boundary when none remains.
48    pub fn at_reconciled<'a>(
49        &'a self,
50        boundary: usize,
51        offset: usize,
52        original_to_current: &'a [Option<usize>],
53        current_len: usize,
54    ) -> impl Iterator<Item = &'a [u8]> {
55        self.children
56            .iter()
57            .filter(move |child| {
58                if child.boundary < offset {
59                    return false;
60                }
61                let original_index = child.boundary - offset;
62                let effective = original_to_current
63                    .iter()
64                    .skip(original_index)
65                    .flatten()
66                    .copied()
67                    .next()
68                    .unwrap_or(current_len);
69                effective == boundary
70            })
71            .map(|child| child.xml.as_slice())
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use oxml_core::raw_xml::{capture_element, capture_empty_element};
78    use quick_xml::events::{BytesEnd, BytesStart, Event};
79    use quick_xml::{Reader, Writer};
80
81    use super::OrderedRawChildren;
82
83    struct TestParent {
84        has_modelled_child: bool,
85        raw_children: OrderedRawChildren,
86    }
87
88    impl TestParent {
89        fn from_xml(xml: &[u8]) -> Self {
90            let mut reader = Reader::from_reader(xml);
91            let mut raw_children = OrderedRawChildren::default();
92            let mut has_modelled_child = false;
93            let mut boundary = 0;
94            let mut buffer = Vec::new();
95
96            loop {
97                match reader.read_event_into(&mut buffer).unwrap() {
98                    Event::Start(element) if local_name(element.name().as_ref()) == b"known" => {
99                        has_modelled_child = true;
100                        boundary = 1;
101                        reader.read_to_end(element.name()).unwrap();
102                    }
103                    Event::Empty(element) if local_name(element.name().as_ref()) == b"known" => {
104                        has_modelled_child = true;
105                        boundary = 1;
106                    }
107                    Event::Start(element) if local_name(element.name().as_ref()) != b"parent" => {
108                        let raw = capture_element(&mut reader, &element).unwrap();
109                        raw_children.push(boundary, raw);
110                    }
111                    Event::Empty(element) => {
112                        let raw = capture_empty_element(&element).unwrap();
113                        raw_children.push(boundary, raw);
114                    }
115                    Event::Eof => break,
116                    _ => {}
117                }
118                buffer.clear();
119            }
120
121            Self {
122                has_modelled_child,
123                raw_children,
124            }
125        }
126
127        fn to_xml(&self) -> Vec<u8> {
128            let mut writer = Writer::new(Vec::new());
129            writer
130                .write_event(Event::Start(BytesStart::new("a:parent")))
131                .unwrap();
132            emit_raw(&mut writer, self.raw_children.at(0));
133            if self.has_modelled_child {
134                writer
135                    .write_event(Event::Empty(BytesStart::new("a:known")))
136                    .unwrap();
137            }
138            emit_raw(&mut writer, self.raw_children.at(1));
139            writer
140                .write_event(Event::End(BytesEnd::new("a:parent")))
141                .unwrap();
142            writer.into_inner()
143        }
144    }
145
146    fn emit_raw<'a>(writer: &mut Writer<Vec<u8>>, children: impl Iterator<Item = &'a [u8]>) {
147        for child in children {
148            writer.get_mut().extend_from_slice(child);
149        }
150    }
151
152    fn local_name(name: &[u8]) -> &[u8] {
153        name.rsplit(|byte| *byte == b':').next().unwrap_or(name)
154    }
155
156    #[test]
157    fn modelled_child_between_two_unmodelled_children_keeps_all_three_slots() {
158        let parent = TestParent::from_xml(br#"<z:parent><x:first/><z:known/><y:last/></z:parent>"#);
159
160        assert_eq!(
161            parent.to_xml(),
162            br#"<a:parent><x:first/><a:known/><y:last/></a:parent>"#
163        );
164    }
165
166    #[test]
167    fn multiple_raw_children_at_one_slot_preserve_document_order() {
168        let parent =
169            TestParent::from_xml(br#"<z:parent><x:first/><y:second/><z:known/></z:parent>"#);
170
171        assert_eq!(
172            parent.to_xml(),
173            br#"<a:parent><x:first/><y:second/><a:known/></a:parent>"#
174        );
175    }
176
177    #[test]
178    fn raw_subtrees_are_reemitted_byte_for_byte() {
179        let parent = TestParent::from_xml(
180            br#"<z:parent><x:item x:id="7"><x:child>one &amp; two</x:child><!--note--></x:item><z:known/></z:parent>"#,
181        );
182
183        assert_eq!(
184            parent.to_xml(),
185            br#"<a:parent><x:item x:id="7"><x:child>one &amp; two</x:child><!--note--></x:item><a:known/></a:parent>"#
186        );
187    }
188
189    #[test]
190    fn raw_children_after_the_last_modelled_child_are_not_dropped() {
191        let parent =
192            TestParent::from_xml(br#"<z:parent><z:known/><x:last x:value="kept"/></z:parent>"#);
193
194        assert!(!parent.raw_children.is_empty());
195        assert_eq!(
196            parent.to_xml(),
197            br#"<a:parent><a:known/><x:last x:value="kept"/></a:parent>"#
198        );
199    }
200}