use std::collections::BTreeMap;
use openehr_am::v2_4::aom2::terminology::archetype_terminology::ArchetypeTerminology;
pub(super) fn split_operational(constraint: &str) -> (&str, Option<&str>) {
match constraint.split_once('@') {
Some((code, terminology)) => (code.trim(), Some(terminology.trim())),
None => (constraint.trim(), None),
}
}
pub(super) fn bound_codes<'t>(
term: &'t ArchetypeTerminology,
operational: Option<&str>,
codes: &[String],
) -> Option<(&'t str, Vec<String>)> {
if codes.is_empty() {
return None;
}
let bindings = term.term_bindings.as_ref()?;
let covering = |targets: &BTreeMap<String, String>| -> Option<Vec<String>> {
codes
.iter()
.map(|code| targets.get(code).and_then(|uri| bound_term_code(uri)))
.collect()
};
if let Some(named) = operational {
let (terminology, targets) = bindings
.iter()
.find(|(terminology, _)| terminology.eq_ignore_ascii_case(named))?;
return covering(targets).map(|bound| (terminology.as_str(), bound));
}
let mut full: Vec<(&str, Vec<String>)> = bindings
.iter()
.filter_map(|(terminology, targets)| {
covering(targets).map(|bound| (terminology.as_str(), bound))
})
.collect();
if full.len() == 1 {
return full.pop();
}
full.into_iter()
.find(|(terminology, _)| terminology.eq_ignore_ascii_case("openehr"))
}
fn bound_term_code(uri: &str) -> Option<String> {
let (_, code) = uri.trim().rsplit_once("/id/")?;
(!code.is_empty() && !code.contains('/')).then(|| code.to_owned())
}
#[cfg(test)]
mod tests {
use super::*;
fn terminology(bindings: &[(&str, &[(&str, &str)])]) -> ArchetypeTerminology {
ArchetypeTerminology {
is_differential: false,
original_language: "en".to_owned(),
concept_code: "id1".to_owned(),
term_definitions: BTreeMap::new(),
value_sets: None,
terminology_extracts: None,
term_bindings: Some(
bindings
.iter()
.map(|(name, targets)| {
let targets = targets
.iter()
.map(|(code, uri)| ((*code).to_owned(), (*uri).to_owned()))
.collect();
((*name).to_owned(), targets)
})
.collect(),
),
}
}
fn codes(list: &[&str]) -> Vec<String> {
list.iter().map(|c| (*c).to_owned()).collect()
}
#[test]
fn an_openehr_binding_names_its_term() {
let term = terminology(&[("openehr", &[("at1", "http://openehr.org/id/433")])]);
assert_eq!(
bound_codes(&term, None, &codes(&["at1"])),
Some(("openehr", codes(&["433"])))
);
}
#[test]
fn a_partial_binding_keeps_the_local_codes() {
let term = terminology(&[("snomed_ct", &[("at1", "http://snomed.info/id/1")])]);
assert_eq!(bound_codes(&term, None, &codes(&["at1", "at2"])), None);
}
#[test]
fn openehr_wins_among_several_full_bindings_and_an_operational_one_wins_over_both() {
let term = terminology(&[
("openehr", &[("at1", "http://openehr.org/id/433")]),
("snomed_ct", &[("at1", "http://snomed.info/id/7")]),
]);
let at1 = codes(&["at1"]);
assert_eq!(
bound_codes(&term, None, &at1),
Some(("openehr", codes(&["433"])))
);
assert_eq!(
bound_codes(&term, Some("snomed_ct"), &at1),
Some(("snomed_ct", codes(&["7"])))
);
assert_eq!(bound_codes(&term, Some("loinc"), &at1), None);
}
#[test]
fn a_uri_without_an_id_segment_names_no_term() {
assert_eq!(
bound_term_code("http://snomed.info/id/406464007"),
Some("406464007".to_owned())
);
assert_eq!(bound_term_code("http://example.org/valueset/12"), None);
assert_eq!(bound_term_code("http://example.org/id/"), None);
}
#[test]
fn an_operational_binding_splits_from_its_code() {
assert_eq!(
split_operational("ac1@snomed_ct"),
("ac1", Some("snomed_ct"))
);
assert_eq!(split_operational("at3"), ("at3", None));
}
}