use std::collections::BTreeMap;
use openehr_base::v1_3::base_types::identification::archetype_id::ArchetypeId;
use openehr_its::opt14::types::CArchetypeRoot;
use openehr_its::opt14::types::CAttribute;
use openehr_its::opt14::types::CObject;
use openehr_its::opt14::types::Codedefinitionset;
use openehr_its::opt14::types::OperationalTemplate;
use openehr_its::xml::runtime::XmlError;
const TEXT: &str = "text";
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TermDefinitions {
language: String,
terms: BTreeMap<Key, String>,
}
type Key = (String, String, String);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Term<'a> {
Defined(&'a str),
OtherLanguage,
Undefined,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum TermDefinitionsError {
#[error("the operational template is not an OPT 1.4 template")]
Template(#[source] XmlError),
}
impl TermDefinitions {
pub fn from_opt14(xml: &str) -> Result<Self, TermDefinitionsError> {
openehr_its::opt14::from_xml(xml)
.map(|template| Self::from_template(&template))
.map_err(TermDefinitionsError::Template)
}
#[must_use]
pub fn from_template(template: &OperationalTemplate) -> Self {
let mut definitions = Self {
language: template.language.code_string.clone(),
terms: BTreeMap::new(),
};
definitions.root(&template.definition);
for ontology in template
.ontology
.iter()
.chain(&template.component_ontologies)
{
for set in &ontology.term_definitions {
definitions.set(&ontology.archetype_id, set);
}
for set in &ontology.constraint_definitions {
definitions.set(&ontology.archetype_id, set);
}
}
definitions
}
#[must_use]
pub fn language(&self) -> &str {
&self.language
}
#[must_use]
pub fn term(&self, language: &str, archetype: &ArchetypeId, code: &str) -> Term<'_> {
let archetype = archetype.value.as_str();
let key = (language.to_owned(), archetype.to_owned(), code.to_owned());
if let Some(text) = self.terms.get(&key) {
return Term::Defined(text);
}
if self
.terms
.keys()
.any(|(_, held, defined)| held == archetype && defined == code)
{
Term::OtherLanguage
} else {
Term::Undefined
}
}
fn root(&mut self, root: &CArchetypeRoot) {
let archetype = root.archetype_id.value.as_str();
let language = self.language.clone();
for term in &root.term_definitions {
if let Some(text) = term.items.get(TEXT) {
self.insert(&language, archetype, &term.code, text);
}
}
self.attributes(&root.attributes);
}
fn attributes(&mut self, attributes: &[CAttribute]) {
for attribute in attributes {
let children = match attribute {
CAttribute::CMultipleAttribute(multiple) => &multiple.children,
CAttribute::CSingleAttribute(single) => &single.children,
};
for child in children {
match child {
CObject::CArchetypeRoot(root) => self.root(root),
CObject::CComplexObject(complex) => self.attributes(&complex.attributes),
CObject::TComplexObject(complex) => self.attributes(&complex.attributes),
_ => {}
}
}
}
}
fn set(&mut self, archetype: &str, set: &Codedefinitionset) {
for term in &set.items {
if let Some(text) = term.items.get(TEXT) {
self.insert(&set.language, archetype, &term.code, text);
}
}
}
fn insert(&mut self, language: &str, archetype: &str, code: &str, text: &str) {
self.terms
.entry((language.to_owned(), archetype.to_owned(), code.to_owned()))
.or_insert_with(|| text.to_owned());
}
}