use crate::{Error, Message, Options, Version};
use std::sync::Arc;
#[derive(Debug)]
pub struct Builder {
message: Message,
failures: Vec<Error>,
}
impl Builder {
#[must_use]
pub fn new(version: Version) -> Builder {
let header = format!("MSH|^~\\&|||||||||P|{version}");
let message = crate::parse_with_options(&header, &Options::new().with_version(version))
.expect("a builder's own header is always well formed");
Builder {
message,
failures: Vec::new(),
}
}
#[must_use]
pub fn with_dictionary(version: Version, dictionary: Arc<crate::Dictionary>) -> Builder {
let header = format!("MSH|^~\\&|||||||||P|{version}");
let options = Options::new()
.with_version(version)
.with_dictionary(dictionary);
let message = crate::parse_with_options(&header, &options)
.expect("a builder's own header is always well formed");
Builder {
message,
failures: Vec::new(),
}
}
#[must_use]
pub fn from_message(message: Message) -> Builder {
Builder {
message,
failures: Vec::new(),
}
}
#[must_use]
pub fn message_type(mut self, code: &str, trigger: &str) -> Builder {
let structure = self.message.dictionary().structure_id(code, trigger);
self = self.set("MSH-9.1", code);
self = self.set("MSH-9.2", trigger);
self.set("MSH-9.3", &structure)
}
#[must_use]
pub fn control_id(self, id: &str) -> Builder {
self.set("MSH-10", id)
}
#[must_use]
pub fn timestamp(self, timestamp: &str) -> Builder {
self.set("MSH-7.1", timestamp)
}
#[must_use]
pub fn sending(self, application: &str, facility: &str) -> Builder {
self.set("MSH-3.1", application).set("MSH-4.1", facility)
}
#[must_use]
pub fn receiving(self, application: &str, facility: &str) -> Builder {
self.set("MSH-5.1", application).set("MSH-6.1", facility)
}
#[must_use]
pub fn processing_id(self, id: &str) -> Builder {
self.set("MSH-11.1", id)
}
#[must_use]
pub fn segment(mut self, name: &str) -> Builder {
self.message.append_segment(name);
self
}
#[must_use]
pub fn set(mut self, path: &str, value: &str) -> Builder {
if let Err(error) = self.message.set(path, value) {
self.failures.push(error);
}
self
}
#[must_use]
pub fn set_er7(mut self, path: &str, er7_text: &str) -> Builder {
if let Err(error) = self.message.set_er7(path, er7_text) {
self.failures.push(error);
}
self
}
#[must_use]
pub fn encode(mut self, value: &impl crate::ToHl7) -> Builder {
if let Err(error) = value.to_hl7(&mut self.message) {
self.failures.push(error);
}
self
}
pub fn build(self) -> Result<Message, Error> {
match self.failures.into_iter().next() {
Some(error) => Err(error),
None => Ok(self.message),
}
}
pub fn build_valid(self) -> Result<Message, Error> {
let message = self.build()?;
let failures: Vec<crate::Diagnostic> = message
.validate()
.into_iter()
.filter(|diagnostic| diagnostic.severity == crate::Severity::Error)
.collect();
if failures.is_empty() {
Ok(message)
} else {
Err(Error::Invalid(failures))
}
}
}
#[must_use]
pub fn acknowledge(message: &Message, code: &str, control_id: &str, timestamp: &str) -> Builder {
let value = |path: &str| message.get(path).ok().flatten().unwrap_or_default();
Builder::new(message.version())
.message_type("ACK", &value("MSH-9.2"))
.control_id(control_id)
.timestamp(timestamp)
.sending(&value("MSH-5.1"), &value("MSH-6.1"))
.receiving(&value("MSH-3.1"), &value("MSH-4.1"))
.processing_id(&value("MSH-11.1"))
.segment("MSA")
.set("MSA-1", code)
.set("MSA-2", &value("MSH-10"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builds_a_message_from_nothing() {
let message = Builder::new(Version::V2_5)
.message_type("ADT", "A01")
.control_id("1")
.timestamp("20240101093851")
.segment("EVN")
.set("EVN-1", "A01")
.segment("PID")
.set("PID-3.1", "241900")
.segment("PV1")
.set("PV1-2", "I")
.build()
.unwrap();
assert_eq!(message.version(), Version::V2_5);
assert_eq!(message.structure_id(), "ADT_A01");
assert_eq!(message.get("PID-3.1").unwrap().as_deref(), Some("241900"));
assert_eq!(message.validate(), []);
let text = message.to_er7();
assert_eq!(crate::parse(&text).unwrap().to_er7(), text);
}
#[test]
fn resolves_the_structure_id_through_the_dictionary() {
let message = Builder::new(Version::V2_5)
.message_type("ADT", "A08")
.build()
.unwrap();
assert_eq!(message.get("MSH-9.3").unwrap().as_deref(), Some("ADT_A01"));
}
#[test]
fn reports_the_first_failure_at_build_time() {
let error = Builder::new(Version::V2_5)
.set("PID-3.1", "241900") .build()
.unwrap_err();
assert!(matches!(error, Error::NoSuchSegment { .. }), "{error}");
}
#[test]
fn build_valid_rejects_an_incomplete_message() {
let error = Builder::new(Version::V2_5).build_valid().unwrap_err();
match error {
Error::Invalid(diagnostics) => assert_eq!(diagnostics.len(), 2, "{diagnostics:?}"),
other => panic!("expected a validation failure, got {other}"),
}
}
#[test]
fn acknowledges_a_message() {
let message = crate::parse(
"MSH|^~\\&|LAB|LAB1|EPIC|CLINIC|20240101||ORU^R01|99|P|2.5\rPID|1\rOBR|1\rOBX|1|NM|X||7",
)
.unwrap();
let ack = acknowledge(&message, "AA", "ACK1", "20240101093900")
.build_valid()
.unwrap();
assert_eq!(ack.structure_id(), "ACK");
assert_eq!(ack.get("MSA-1").unwrap().as_deref(), Some("AA"));
assert_eq!(ack.get("MSA-2").unwrap().as_deref(), Some("99"));
assert_eq!(ack.get("MSH-5.1").unwrap().as_deref(), Some("LAB"));
assert_eq!(ack.get("MSH-6.1").unwrap().as_deref(), Some("LAB1"));
assert_eq!(ack.get("MSH-3.1").unwrap().as_deref(), Some("EPIC"));
}
#[test]
fn acknowledges_in_the_senders_release() {
let message =
crate::parse("MSH|^~\\&|LAB|L|EPIC|E|20240101||ORU^R01|99|P|2.3\rPID|1").unwrap();
let ack = acknowledge(&message, "AE", "1", "20240101")
.build()
.unwrap();
assert_eq!(ack.version(), Version::V2_3);
assert_eq!(ack.get("MSH-12").unwrap().as_deref(), Some("2.3"));
}
}