use haste_fhir_model::r4::generated::{resources::StructureDefinition, types::ElementDefinition};
use regex::Regex;
pub fn ele_index_to_child_indices(
elements: &[Box<ElementDefinition>],
index: usize,
) -> Result<Vec<usize>, String> {
let parent = elements
.get(index)
.ok_or_else(|| format!("Index {} out of bounds", index))?;
let parent_path: String = parent
.path
.value
.as_ref()
.ok_or("Element has no path")?
.to_string();
let depth = parent_path.matches('.').count();
let parent_path_escaped = parent_path.replace('.', "\\.");
let child_regex = Regex::new(&format!("^{}\\.[^.]+$", parent_path_escaped))
.map_err(|e| format!("Failed to compile regex: {}", e))?;
let mut cur_index = index + 1;
let mut children_indices = Vec::new();
while cur_index < elements.len()
&& let path = elements[cur_index]
.path
.value
.as_ref()
.ok_or("Not Found")?
.to_owned()
&& path.matches('.').count() > depth
{
if child_regex.is_match(&path) {
children_indices.push(cur_index);
}
cur_index += 1;
}
Ok(children_indices)
}
fn traversal_bottom_up_sd_elements<'a, F, V>(
elements: &'a Vec<Box<ElementDefinition>>,
index: usize,
visitor_function: &mut F,
) -> Result<V, String>
where
F: FnMut(&'a ElementDefinition, Vec<V>, usize) -> V,
{
let child_indices = ele_index_to_child_indices(elements.as_slice(), index)?;
let child_traversal_values: Vec<V> = child_indices
.iter()
.map(|&child_index| {
traversal_bottom_up_sd_elements(elements, child_index, visitor_function)
})
.collect::<Result<Vec<V>, String>>()?;
Ok(visitor_function(
&elements[index],
child_traversal_values,
index,
))
}
pub fn traversal<'a, F, V>(sd: &'a StructureDefinition, visitor: &mut F) -> Result<V, String>
where
F: FnMut(&'a ElementDefinition, Vec<V>, usize) -> V,
{
let elements = &sd
.snapshot
.as_ref()
.ok_or("StructureDefinition has no snapshot")?
.element;
traversal_bottom_up_sd_elements(elements, 0, visitor)
}
#[cfg(test)]
mod tests {
use haste_fhir_model::r4::generated::resources::{Bundle, Resource};
use super::*;
#[test]
fn test_traversal() {
let bundle = haste_fhir_serialization_json::from_str::<Bundle>(
&std::fs::read_to_string(
"../artifacts/artifacts/r4/hl7/minified/profiles-resources.min.json",
)
.unwrap(),
)
.unwrap();
let sds: Vec<&StructureDefinition> = bundle
.entry
.as_ref()
.unwrap()
.iter()
.filter_map(|e| match e.resource.as_ref().map(|r| r.as_ref()) {
Some(Resource::StructureDefinition(sd)) => Some(sd),
_ => None,
})
.collect();
let mut visitor =
|element: &ElementDefinition, children: Vec<String>, _index: usize| -> String {
let path: String = element.path.value.as_ref().unwrap().to_string();
let result = children.join("\n") + "\n" + &path;
result
};
println!("StructureDefinitions: {}", sds.len());
for sd in sds {
let result = traversal(sd, &mut visitor);
println!("Result: {:?}", result);
}
}
}