#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Concept {
pub system: Option<String>,
pub version: Option<String>,
pub code: Option<String>,
pub display: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Designation {
pub language: Option<String>,
pub usage: Option<Concept>,
pub value: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PropertyValue {
pub member: String,
pub value: fhir_types::codec::Value,
}
impl PropertyValue {
#[must_use]
pub fn as_str(&self) -> Option<&str> {
match &self.value {
fhir_types::codec::Value::String(text) => Some(text.as_str()),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Property {
pub code: String,
pub value: Option<PropertyValue>,
pub description: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Equivalence {
RelatedTo,
Equivalent,
Equal,
Wider,
Subsumes,
Narrower,
Specializes,
Inexact,
Unmatched,
Disjoint,
Other(String),
}
impl Equivalence {
#[must_use]
pub fn new(code: &str) -> Self {
match code {
"relatedto" => Self::RelatedTo,
"equivalent" => Self::Equivalent,
"equal" => Self::Equal,
"wider" => Self::Wider,
"subsumes" => Self::Subsumes,
"narrower" => Self::Narrower,
"specializes" => Self::Specializes,
"inexact" => Self::Inexact,
"unmatched" => Self::Unmatched,
"disjoint" => Self::Disjoint,
other => Self::Other(other.to_owned()),
}
}
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::RelatedTo => "relatedto",
Self::Equivalent => "equivalent",
Self::Equal => "equal",
Self::Wider => "wider",
Self::Subsumes => "subsumes",
Self::Narrower => "narrower",
Self::Specializes => "specializes",
Self::Inexact => "inexact",
Self::Unmatched => "unmatched",
Self::Disjoint => "disjoint",
Self::Other(code) => code.as_str(),
}
}
#[must_use]
pub fn accepted(&self) -> bool {
matches!(self, Self::Equivalent | Self::Equal)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Match {
pub equivalence: Option<Equivalence>,
pub concept: Option<Concept>,
pub source: Option<String>,
}
impl Match {
#[must_use]
pub fn accepted(&self) -> bool {
self.equivalence.as_ref().is_some_and(Equivalence::accepted)
}
}
#[cfg(test)]
mod tests {
use super::{Concept, Equivalence, Match, PropertyValue};
#[test]
fn only_equivalent_and_equal_are_accepted() {
for code in ["equivalent", "equal"] {
assert!(
Equivalence::new(code).accepted(),
"{code} should be a translation"
);
}
for code in [
"relatedto",
"wider",
"subsumes",
"narrower",
"specializes",
"inexact",
"unmatched",
"disjoint",
] {
assert!(
!Equivalence::new(code).accepted(),
"{code} should not be a translation"
);
}
}
#[test]
fn an_unknown_equivalence_keeps_its_code_and_is_never_accepted() {
let equivalence = Equivalence::new("source-is-narrower-than-target");
assert_eq!(
Equivalence::Other("source-is-narrower-than-target".to_owned()),
equivalence
);
assert_eq!("source-is-narrower-than-target", equivalence.as_str());
assert!(!equivalence.accepted());
}
#[test]
fn a_match_without_an_equivalence_is_not_a_translation() {
let unstated = Match {
equivalence: None,
concept: Some(Concept::default()),
source: None,
};
assert!(!unstated.accepted());
}
#[test]
fn a_string_property_value_reads_as_text() {
let text = PropertyValue {
member: "valueString".to_owned(),
value: fhir_types::codec::Value::String("a note".to_owned()),
};
assert_eq!(Some("a note"), text.as_str());
let flag = PropertyValue {
member: "valueBoolean".to_owned(),
value: fhir_types::codec::Value::Bool(true),
};
assert_eq!(None, flag.as_str());
}
}