use std::collections::BTreeMap;
use openehr_base::v1_3::base_types::identification::archetype_id::ArchetypeId;
use openehr_its::opt14::types::Codedefinitionset;
use openehr_its::opt14::types::OperationalTemplate;
use openehr_its::xml::runtime::XmlError;
use openehr_sdt::flat::error::FlatError;
use openehr_sdt::flat::webtemplate::builder::build_web_template;
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),
#[error("the operational template builds no Web Template")]
Build(#[source] FlatError),
}
impl TermDefinitions {
pub fn from_opt14(xml: &str) -> Result<Self, TermDefinitionsError> {
let template = openehr_its::opt14::from_xml(xml).map_err(TermDefinitionsError::Template)?;
Self::from_template(&template)
}
pub fn from_template(template: &OperationalTemplate) -> Result<Self, TermDefinitionsError> {
let mut definitions = Self {
language: template.language.code_string.clone(),
terms: BTreeMap::new(),
};
let built = build_web_template(template).map_err(TermDefinitionsError::Build)?;
let language = definitions.language.clone();
let mut stack = vec![&built.tree];
while let Some(node) = stack.pop() {
if let Some(archetype) = node.node_id.as_deref() {
for (code, term) in &node.facts.archetype_terms {
if !term.text.is_empty() {
definitions.insert(&language, archetype, code, &term.text);
}
}
}
stack.extend(node.children.iter().chain(&node.alternatives));
}
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);
}
}
Ok(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 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());
}
}