haste_codegen/
traversal.rs

1use 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}