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