1use crate::dictionary::{Dictionary, VARIABLE};
27use er7::{Component, Repetition, Segment, Separators};
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum Kind {
32 Group,
35 Segment,
37 Field,
39 Component,
41 Subcomponent,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct Node {
54 name: String,
55 path: String,
56 kind: Kind,
57 text: String,
58 null: bool,
59 children: Vec<Node>,
60}
61
62impl Node {
63 #[must_use]
65 pub fn name(&self) -> &str {
66 &self.name
67 }
68
69 #[must_use]
75 pub fn path(&self) -> &str {
76 &self.path
77 }
78
79 #[must_use]
81 pub fn kind(&self) -> Kind {
82 self.kind
83 }
84
85 #[must_use]
88 pub fn text(&self) -> &str {
89 &self.text
90 }
91
92 #[must_use]
96 pub fn is_null(&self) -> bool {
97 self.null
98 }
99
100 #[must_use]
102 pub fn is_leaf(&self) -> bool {
103 self.children.is_empty()
104 }
105
106 #[must_use]
108 pub fn children(&self) -> &[Node] {
109 &self.children
110 }
111
112 #[must_use]
122 pub fn child(&self, name: &str) -> Option<&Node> {
123 self.children.iter().find(|child| child.name == name)
124 }
125
126 pub fn children_named<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a Node> {
129 self.children.iter().filter(move |child| child.name == name)
130 }
131
132 #[must_use]
136 pub fn find(&self, name: &str) -> Option<&Node> {
137 self.descendants().find(|node| node.name == name)
138 }
139
140 pub fn find_all<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a Node> {
142 self.descendants().filter(move |node| node.name == name)
143 }
144
145 #[must_use]
147 pub fn descendants(&self) -> Descendants<'_> {
148 Descendants {
149 stack: self.children.iter().rev().collect(),
150 }
151 }
152}
153
154#[derive(Debug)]
156pub struct Descendants<'a> {
157 stack: Vec<&'a Node>,
158}
159
160impl<'a> Iterator for Descendants<'a> {
161 type Item = &'a Node;
162
163 fn next(&mut self) -> Option<&'a Node> {
164 let node = self.stack.pop()?;
165 self.stack.extend(node.children.iter().rev());
166 Some(node)
167 }
168}
169
170pub(crate) fn root(name: &str, children: Vec<Node>) -> Node {
173 let text = children
174 .iter()
175 .map(|child| child.text.as_str())
176 .collect::<Vec<&str>>()
177 .join("\r");
178 Node {
179 name: name.to_string(),
180 path: String::new(),
181 kind: Kind::Group,
182 text,
183 null: false,
184 children,
185 }
186}
187
188pub(crate) fn group(root_name: &str, name: &str, children: Vec<Node>) -> Node {
191 let mut node = root(&format!("{root_name}.{name}"), children);
192 node.path = String::new();
193 node
194}
195
196pub(crate) fn segment(
202 seg: &Segment,
203 occurrence: usize,
204 dictionary: &Dictionary,
205 separators: &Separators,
206) -> Node {
207 let base = format!("{}[{occurrence}]", seg.name);
208 let variable = dictionary.variable_type(seg).map(str::to_string);
210 let mut children = Vec::new();
211 for (index, field) in seg.fields.iter().enumerate() {
212 if field.is_empty() {
213 continue;
214 }
215 let number = index + 1;
216 let name = format!("{}.{number}", seg.name);
217 let data_type = match dictionary.field_type(&seg.name, number) {
218 Some(VARIABLE) => variable.as_deref(),
219 other => other,
220 };
221 for (repetition, occurrence) in field.repetitions.iter().enumerate() {
222 if occurrence.is_empty() {
223 continue;
224 }
225 children.push(field_node(
226 &name,
227 &format!("{base}-{number}[{}]", repetition + 1),
228 data_type,
229 occurrence,
230 dictionary,
231 separators,
232 ));
233 }
234 }
235 Node {
236 name: seg.name.clone(),
237 path: base,
238 kind: Kind::Segment,
239 text: seg.to_text(separators),
240 null: false,
241 children,
242 }
243}
244
245fn field_node(
249 name: &str,
250 path: &str,
251 data_type: Option<&str>,
252 repetition: &Repetition,
253 dictionary: &Dictionary,
254 separators: &Separators,
255) -> Node {
256 let text = repetition.to_text(separators);
257 let mut node = Node {
258 name: name.to_string(),
259 path: path.to_string(),
260 kind: Kind::Field,
261 text,
262 null: repetition.is_null(),
263 children: Vec::new(),
264 };
265 if repetition.is_null() {
266 return node;
267 }
268 if let Some(components) = data_type.and_then(|dt| dictionary.composite_components(dt)) {
269 let data_type = data_type.unwrap_or_default();
270 for (index, component) in repetition.components.iter().enumerate() {
271 if component.is_empty() {
272 continue;
273 }
274 node.children.push(component_node(
275 &format!("{data_type}.{}", index + 1),
276 &format!("{path}.{}", index + 1),
277 components.get(index).map(String::as_str),
278 component,
279 dictionary,
280 separators,
281 ));
282 }
283 return node;
284 }
285 if let [only] = repetition.components.as_slice()
288 && only.subcomponents.len() <= 1
289 {
290 return node;
291 }
292 for (index, component) in repetition.components.iter().enumerate() {
293 if component.is_empty() {
294 continue;
295 }
296 node.children.push(component_node(
297 &format!("{name}.{}", index + 1),
298 &format!("{path}.{}", index + 1),
299 None,
300 component,
301 dictionary,
302 separators,
303 ));
304 }
305 node
306}
307
308fn component_node(
312 name: &str,
313 path: &str,
314 data_type: Option<&str>,
315 component: &Component,
316 dictionary: &Dictionary,
317 separators: &Separators,
318) -> Node {
319 let mut node = Node {
320 name: name.to_string(),
321 path: path.to_string(),
322 kind: Kind::Component,
323 text: component.to_text(separators),
324 null: component.is_null(),
325 children: Vec::new(),
326 };
327 if component.is_null() || component.subcomponents.len() <= 1 {
328 return node;
329 }
330 let composite = data_type.filter(|dt| dictionary.is_composite(dt));
331 for (index, subcomponent) in component.subcomponents.iter().enumerate() {
332 if subcomponent.is_empty() {
333 continue;
334 }
335 let number = index + 1;
336 node.children.push(Node {
337 name: match composite {
338 Some(data_type) => format!("{data_type}.{number}"),
339 None => format!("{name}.{number}"),
340 },
341 path: format!("{path}.{number}"),
342 kind: Kind::Subcomponent,
343 text: subcomponent.value(separators).into_owned(),
344 null: subcomponent.is_null(),
345 children: Vec::new(),
346 });
347 }
348 node
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 fn tree(text: &str) -> Node {
356 crate::parse(text).unwrap().tree()
357 }
358
359 const HEADER: &str = "MSH|^~\\&|hphis||EPIC||20131011093851||ORU^R01|14AAACVDD|P|2.5";
360
361 #[test]
362 fn names_known_types_after_the_type_and_the_rest_positionally() {
363 let tree = tree(&format!("{HEADER}\rPID|1||241900||TEST^FOUAZ\rZPD|a^b"));
364 let pid = tree.find("PID").unwrap();
365 let name = pid.child("PID.5").unwrap();
366 assert_eq!(name.text(), "TEST^FOUAZ");
367 assert_eq!(name.child("XPN.1").unwrap().text(), "TEST");
369 assert_eq!(name.child("XPN.2").unwrap().text(), "FOUAZ");
370 let zpd = tree.find("ZPD").unwrap();
373 assert_eq!(
374 zpd.child("ZPD.1").unwrap().child("ZPD.1.1").unwrap().text(),
375 "a"
376 );
377 assert_eq!(
378 zpd.child("ZPD.1").unwrap().child("ZPD.1.2").unwrap().text(),
379 "b"
380 );
381 }
382
383 #[test]
384 fn every_node_carries_the_path_that_reads_it_back() {
385 let message = crate::parse(&format!("{HEADER}\rPID|1||241900||TEST^FOUAZ")).unwrap();
386 let tree = message.tree();
387 let given = tree.find("XPN.2").unwrap();
388 assert_eq!(given.path(), "PID[1]-5[1].2");
389 assert_eq!(message.get(given.path()).unwrap().as_deref(), Some("FOUAZ"));
390 }
391
392 #[test]
393 fn repetitions_are_separate_siblings() {
394 let tree = tree(&format!("{HEADER}\rPID|1||A~B~C"));
395 let pid = tree.find("PID").unwrap();
396 let ids: Vec<&str> = pid.children_named("PID.3").map(Node::text).collect();
397 assert_eq!(ids, ["A", "B", "C"]);
398 assert_eq!(pid.child("PID.3").unwrap().path(), "PID[1]-3[1]");
399 assert_eq!(
400 pid.children_named("PID.3").nth(2).unwrap().path(),
401 "PID[1]-3[3]"
402 );
403 }
404
405 #[test]
406 fn the_explicit_null_survives() {
407 let tree = tree(&format!("{HEADER}\rPID|1||\"\""));
408 let field = tree.find("PID").unwrap().child("PID.3").unwrap();
409 assert!(field.is_null(), "explicit null must not read as absent");
410 assert!(tree.find("PID").unwrap().child("PID.4").is_none());
411 }
412
413 #[test]
414 fn obx_5_takes_its_type_from_obx_2() {
415 let coded = tree(&format!("{HEADER}\rOBX|1|CE|X||a^b^c"));
416 let value = coded.find("OBX").unwrap().child("OBX.5").unwrap();
417 assert_eq!(value.child("CE.1").unwrap().text(), "a");
418 let numeric = tree(&format!("{HEADER}\rOBX|1|NM|X||7.4"));
420 assert_eq!(
421 numeric.find("OBX").unwrap().child("OBX.5").unwrap().text(),
422 "7.4"
423 );
424 }
425
426 #[test]
427 fn groups_nest_under_the_structure_id() {
428 let tree = tree(&format!("{HEADER}\rPID|1\rOBR|1\rOBX|1|NM|X||7"));
429 assert_eq!(tree.name(), "ORU_R01");
430 let result = tree.child("ORU_R01.PATIENT_RESULT").unwrap();
431 let order = result.child("ORU_R01.ORDER_OBSERVATION").unwrap();
432 assert!(
433 order
434 .child("ORU_R01.OBSERVATION")
435 .unwrap()
436 .child("OBX")
437 .is_some()
438 );
439 assert!(tree.find("OBX").is_some());
441 }
442
443 #[test]
444 fn descendants_walks_everything_once() {
445 let tree = tree(&format!("{HEADER}\rPID|1||A~B"));
446 let count = tree.descendants().count();
447 let named = tree.find_all("PID.3").count();
448 assert_eq!(named, 2);
449 assert!(count > named);
450 }
451}