use crate::Error;
use crate::vocabulary::{Cd, Ii};
use hl7_2_xml_lite_helper::Element;
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Message {
pub id: Option<Ii>,
pub creation_time: Option<String>,
pub interaction_id: Option<Ii>,
pub sender: Option<Element>,
pub receiver: Option<Element>,
pub control_act: Option<ControlAct>,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ControlAct {
pub class_code: String,
pub mood_code: String,
pub code: Option<Cd>,
pub domain: Option<Element>,
}
pub fn parse(xml_text: &str) -> Result<Message, Error> {
let root = hl7_2_xml_lite_helper::parse(xml_text).map_err(Error::Xml)?;
Ok(Message {
id: root.child("id").and_then(Ii::from_element),
creation_time: root
.child("creationTime")
.and_then(|time| time.attribute("value"))
.map(str::to_string),
interaction_id: root.child("interactionId").and_then(Ii::from_element),
sender: root
.child("sender")
.and_then(|wrapper| wrapper.children.first())
.cloned(),
receiver: root
.child("receiver")
.and_then(|wrapper| wrapper.children.first())
.cloned(),
control_act: root
.child("controlActProcess")
.map(control_act_from_element),
})
}
fn control_act_from_element(element: &Element) -> ControlAct {
ControlAct {
class_code: element
.attribute("classCode")
.unwrap_or_default()
.to_string(),
mood_code: element
.attribute("moodCode")
.unwrap_or_default()
.to_string(),
code: element.child("code").and_then(Cd::from_element),
domain: element
.child("subject")
.and_then(|subject| subject.children.first())
.cloned(),
}
}
#[cfg(test)]
mod tests {
use super::*;
const SAMPLE: &str = r#"
<QUQI_IN000001UV01 xmlns="urn:hl7-org:v3">
<id root="2.16.840.1.113883.19.5" extension="MSG00001"/>
<creationTime value="20260101120000"/>
<interactionId root="2.16.840.1.113883.1.6" extension="QUQI_IN000001UV01"/>
<sender typeCode="SND"><device classCode="DEV" determinerCode="INSTANCE"/></sender>
<receiver typeCode="RCV"><device classCode="DEV" determinerCode="INSTANCE"/></receiver>
<controlActProcess classCode="CACT" moodCode="EVN">
<code code="QUQI_TE000001UV01"/>
<subject>
<observation classCode="OBS" moodCode="EVN">
<id root="2.16.840.1.113883.19.5" extension="1"/>
</observation>
</subject>
</controlActProcess>
</QUQI_IN000001UV01>
"#;
#[test]
fn reads_the_transport_wrapper() {
let message = parse(SAMPLE).unwrap();
assert_eq!(message.id.unwrap().extension.as_deref(), Some("MSG00001"));
assert_eq!(message.creation_time.as_deref(), Some("20260101120000"));
assert_eq!(
message.interaction_id.unwrap().extension.as_deref(),
Some("QUQI_IN000001UV01")
);
assert_eq!(message.sender.unwrap().local_name(), "device");
}
#[test]
fn reads_the_control_act_wrapper_and_trigger_event() {
let message = parse(SAMPLE).unwrap();
let control_act = message.control_act.unwrap();
assert_eq!(control_act.class_code, "CACT");
assert_eq!(control_act.mood_code, "EVN");
assert_eq!(control_act.code.unwrap().code, "QUQI_TE000001UV01");
}
#[test]
fn reads_the_domain_payload_as_a_raw_element() {
let message = parse(SAMPLE).unwrap();
let domain = message.control_act.unwrap().domain.unwrap();
assert_eq!(domain.local_name(), "observation");
assert_eq!(domain.attribute("classCode"), Some("OBS"));
let act = crate::rim::Act::from_element(&domain);
assert_eq!(act.id[0].extension.as_deref(), Some("1"));
}
#[test]
fn missing_wrappers_read_as_none_not_an_error() {
let message = parse(r"<EMPTY_MESSAGE/>").unwrap();
assert_eq!(message.id, None);
assert_eq!(message.interaction_id, None);
assert_eq!(message.control_act, None);
}
#[test]
fn malformed_xml_is_an_error() {
assert!(matches!(parse("<unclosed>"), Err(Error::Xml(_))));
}
}