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