1#[derive(Clone, Debug, Default, PartialEq)]
11pub struct ElementInfo {
12 pub name: Option<String>,
15 pub value: String,
16 pub measure: Option<String>,
17}
18
19#[derive(Clone, Debug, Default)]
20pub struct ElementNode {
21 pub name: String,
22 pub size: u64,
23 pub infos: Vec<ElementInfo>,
24 pub children: Vec<ElementNode>,
25 pub has_error: bool,
26}
27
28impl ElementNode {
29 pub fn new(name: impl Into<String>) -> Self {
30 ElementNode {
31 name: name.into(),
32 size: 0,
33 infos: Vec::new(),
34 children: Vec::new(),
35 has_error: false,
36 }
37 }
38}
39
40pub struct ElementTree {
43 stack: Vec<ElementNode>,
44}
45
46impl ElementTree {
47 pub fn new() -> Self {
48 ElementTree { stack: vec![ElementNode::new("")] }
49 }
50
51 pub fn element_begin(&mut self, name: impl Into<String>) {
52 self.stack.push(ElementNode::new(name));
53 }
54
55 pub fn element_end(&mut self) {
56 if self.stack.len() <= 1 {
57 return;
58 }
59 let child = self.stack.pop().expect("len > 1 checked above");
60 self.stack.last_mut().expect("len > 0 invariant").children.push(child);
61 }
62
63 pub fn element_name(&mut self, name: impl Into<String>) {
64 if let Some(last) = self.stack.last_mut() {
65 last.name = name.into();
66 }
67 }
68
69 pub fn element_info(&mut self, value: impl Into<String>, measure: Option<&str>) {
70 if let Some(last) = self.stack.last_mut() {
71 let value = value.into();
72 if value == "NOK" || measure == Some("Error") {
75 last.has_error = true;
76 }
77 last.infos.push(ElementInfo { name: None, value, measure: measure.map(String::from) });
78 }
79 }
80
81 pub fn param(&mut self, name: impl Into<String>, value: impl Into<String>) {
83 if let Some(last) = self.stack.last_mut() {
84 last.infos.push(ElementInfo {
85 name: Some(name.into()),
86 value: value.into(),
87 measure: None,
88 });
89 }
90 }
91
92 pub fn element_level(&self) -> usize {
93 self.stack.len().saturating_sub(1)
95 }
96
97 pub fn set_current_size(&mut self, size: u64) {
98 if let Some(last) = self.stack.last_mut() {
99 last.size = size;
100 }
101 }
102
103 pub fn root(&self) -> &ElementNode {
106 &self.stack[0]
107 }
108
109 pub fn current_mut(&mut self) -> &mut ElementNode {
112 self.stack.last_mut().expect("stack invariant: root always present")
113 }
114}
115
116impl Default for ElementTree {
117 fn default() -> Self {
118 Self::new()
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn new_tree_has_root_at_level_0() {
128 let t = ElementTree::new();
129 assert_eq!(t.element_level(), 0);
130 assert_eq!(t.root().children.len(), 0);
131 }
132
133 #[test]
134 fn begin_end_pair_appends_child_to_root() {
135 let mut t = ElementTree::new();
136 t.element_begin("atom");
137 assert_eq!(t.element_level(), 1);
138 t.element_end();
139 assert_eq!(t.element_level(), 0);
140 assert_eq!(t.root().children.len(), 1);
141 assert_eq!(t.root().children[0].name, "atom");
142 }
143
144 #[test]
145 fn nested_begin_end_builds_tree() {
146 let mut t = ElementTree::new();
147 t.element_begin("moov");
148 t.element_begin("trak");
149 t.element_begin("tkhd");
150 t.element_end();
151 t.element_begin("mdia");
152 t.element_end();
153 t.element_end();
154 t.element_end();
155
156 let root = t.root();
157 assert_eq!(root.children.len(), 1);
158 let moov = &root.children[0];
159 assert_eq!(moov.name, "moov");
160 assert_eq!(moov.children.len(), 1);
161 let trak = &moov.children[0];
162 assert_eq!(trak.children.len(), 2);
163 assert_eq!(trak.children[0].name, "tkhd");
164 assert_eq!(trak.children[1].name, "mdia");
165 }
166
167 #[test]
168 fn element_info_records_value_and_measure() {
169 let mut t = ElementTree::new();
170 t.element_begin("tkhd");
171 t.element_info("1000", Some("ms"));
172 t.element_info("42", None);
173 t.element_end();
174 let tkhd = &t.root().children[0];
175 assert_eq!(tkhd.infos.len(), 2);
176 assert_eq!(tkhd.infos[0].name, None);
177 assert_eq!(tkhd.infos[0].value, "1000");
178 assert_eq!(tkhd.infos[0].measure.as_deref(), Some("ms"));
179 assert_eq!(tkhd.infos[1].measure, None);
180 }
181
182 #[test]
183 fn param_records_named_field_read() {
184 let mut t = ElementTree::new();
185 t.element_begin("mvhd");
186 t.param("Version", "0");
187 t.param("Flags", "0x000000");
188 t.element_end();
189 let mvhd = &t.root().children[0];
190 assert_eq!(mvhd.infos.len(), 2);
191 assert_eq!(mvhd.infos[0].name.as_deref(), Some("Version"));
192 assert_eq!(mvhd.infos[0].value, "0");
193 assert_eq!(mvhd.infos[1].name.as_deref(), Some("Flags"));
194 }
195
196 #[test]
197 fn nok_marks_element_as_error() {
198 let mut t = ElementTree::new();
199 t.element_begin("bad");
200 t.element_info("NOK", None);
201 t.element_end();
202 assert!(t.root().children[0].has_error);
203 }
204
205 #[test]
206 fn measure_error_marks_element_as_error() {
207 let mut t = ElementTree::new();
208 t.element_begin("bad");
209 t.element_info("0x1234", Some("Error"));
210 t.element_end();
211 assert!(t.root().children[0].has_error);
212 }
213
214 #[test]
215 fn element_name_renames_current_frame() {
216 let mut t = ElementTree::new();
217 t.element_begin("");
218 t.element_name("renamed");
219 t.element_end();
220 assert_eq!(t.root().children[0].name, "renamed");
221 }
222
223 #[test]
224 fn extra_element_end_is_noop() {
225 let mut t = ElementTree::new();
226 t.element_end();
227 t.element_end();
228 assert_eq!(t.element_level(), 0);
229 }
230}