use std::collections::BTreeMap;
use std::sync::Arc;
use fhir_types::r4::element_definition::ElementDefinition;
use fhir_types::r4::structure_definition::StructureDefinition;
use super::ValidateError;
use crate::set::PackageId;
#[derive(Debug)]
pub(crate) struct Structure {
url: String,
definition: Arc<StructureDefinition>,
by_id: BTreeMap<String, usize>,
origin: PackageId,
}
impl Structure {
pub(crate) fn new(
definition: Arc<StructureDefinition>,
origin: PackageId,
) -> Result<Self, ValidateError> {
let url = definition.url.value.clone().unwrap_or_default();
let Some(snapshot) = &definition.snapshot else {
return Err(ValidateError::NoSnapshot { url });
};
let mut by_id = BTreeMap::new();
for (index, element) in snapshot.element.iter().enumerate() {
let id = element
.id
.clone()
.or_else(|| element.path.value.clone())
.unwrap_or_default();
if id.is_empty() {
continue;
}
by_id.entry(id).or_insert(index);
}
Ok(Self {
url,
definition,
by_id,
origin,
})
}
pub(crate) const fn origin(&self) -> PackageId {
self.origin
}
pub(crate) fn url(&self) -> &str {
&self.url
}
pub(crate) fn definition(&self) -> &StructureDefinition {
&self.definition
}
pub(crate) fn type_name(&self) -> &str {
self.definition.r#type.value.as_deref().unwrap_or_default()
}
pub(crate) fn element(&self, id: &str) -> Option<&ElementDefinition> {
let index = *self.by_id.get(id)?;
self.definition.snapshot.as_ref()?.element.get(index)
}
pub(crate) fn slices(&self, entry: &str) -> Vec<(&str, &ElementDefinition)> {
let last = entry.rsplit('.').next().unwrap_or(entry);
let separator = if last.contains(':') { '/' } else { ':' };
let Some(snapshot) = &self.definition.snapshot else {
return Vec::new();
};
snapshot
.element
.iter()
.filter(|element| element.slice_name.is_some())
.filter_map(|element| {
let id = element.id.as_deref()?;
let name = id.strip_prefix(entry)?.strip_prefix(separator)?;
let simple = !name.is_empty() && !name.contains(['.', ':', '/']);
simple.then_some((id, element))
})
.collect()
}
pub(crate) fn has_children(&self, id: &str) -> bool {
let prefix = format!("{id}.");
self.by_id
.range(prefix.clone()..)
.next()
.is_some_and(|(key, _)| key.starts_with(&prefix))
}
}