haste_codegen/
traversal.rs1use haste_fhir_model::r4::generated::{resources::StructureDefinition, types::ElementDefinition};
2use regex::Regex;
3
4fn ele_index_to_child_indices(
5 elements: &[Box<ElementDefinition>],
6 index: usize,
7) -> Result<Vec<usize>, String> {
8 let parent = elements
9 .get(index)
10 .ok_or_else(|| format!("Index {} out of bounds", index))?;
11
12 let parent_path: String = parent
13 .path
14 .value
15 .as_ref()
16 .ok_or("Element has no path")?
17 .to_string();
18
19 let depth = parent_path.matches('.').count();
20 let parent_path_escaped = parent_path.replace('.', "\\.");
21 let child_regex = Regex::new(&format!("^{}\\.[^.]+$", parent_path_escaped))
22 .map_err(|e| format!("Failed to compile regex: {}", e))?;
23
24 let mut cur_index = index + 1;
25 let mut children_indices = Vec::new();
26
27 while cur_index < elements.len()
28 && let path = elements[cur_index]
29 .path
30 .value
31 .as_ref()
32 .ok_or("Not Found")?
33 .to_owned()
34 && path.matches('.').count() > depth
35 {
36 if child_regex.is_match(&path) {
37 children_indices.push(cur_index);
38 }
39 cur_index += 1;
40 }
41
42 Ok(children_indices)
43}
44
45fn traversal_bottom_up_sd_elements<'a, F, V>(
46 elements: &'a Vec<Box<ElementDefinition>>,
47 index: usize,
48 visitor_function: &mut F,
49) -> Result<V, String>
50where
51 F: FnMut(&'a ElementDefinition, Vec<V>, usize) -> V,
52{
53 let child_indices = ele_index_to_child_indices(elements.as_slice(), index)?;
54
55 let child_traversal_values: Vec<V> = child_indices
56 .iter()
57 .map(|&child_index| {
58 traversal_bottom_up_sd_elements(elements, child_index, visitor_function)
59 })
60 .collect::<Result<Vec<V>, String>>()?;
61
62 Ok(visitor_function(
63 &elements[index],
64 child_traversal_values,
65 index,
66 ))
67}
68
69pub fn traversal<'a, F, V>(sd: &'a StructureDefinition, visitor: &mut F) -> Result<V, String>
70where
71 F: FnMut(&'a ElementDefinition, Vec<V>, usize) -> V,
72{
73 let elements = &sd
74 .snapshot
75 .as_ref()
76 .ok_or("StructureDefinition has no snapshot")?
77 .element;
78
79 traversal_bottom_up_sd_elements(elements, 0, visitor)
80}
81
82#[cfg(test)]
83mod tests {
84
85 use haste_fhir_model::r4::generated::resources::{Bundle, Resource};
86
87 use super::*;
88
89 #[test]
90 fn test_traversal() {
91 let bundle = haste_fhir_serialization_json::from_str::<Bundle>(
92 &std::fs::read_to_string(
93 "../artifacts/artifacts/r4/hl7/minified/profiles-resources.min.json",
94 )
95 .unwrap(),
96 )
97 .unwrap();
98
99 let sds: Vec<&StructureDefinition> = bundle
100 .entry
101 .as_ref()
102 .unwrap()
103 .iter()
104 .filter_map(|e| match e.resource.as_ref().map(|r| r.as_ref()) {
105 Some(Resource::StructureDefinition(sd)) => Some(sd),
106 _ => None,
107 })
108 .collect();
109
110 let mut visitor =
111 |element: &ElementDefinition, children: Vec<String>, _index: usize| -> String {
112 let path: String = element.path.value.as_ref().unwrap().to_string();
113 let result = children.join("\n") + "\n" + &path;
114 result
115 };
116
117 println!("StructureDefinitions: {}", sds.len());
118
119 for sd in sds {
120 let result = traversal(sd, &mut visitor);
121
122 println!("Result: {:?}", result);
123 }
124 }
125}