Skip to main content

hl7_2_soap/
lib.rs

1//! HL7 v2 over SOAP: the envelope, faults, payload carriage, WSDL, and
2//! response evaluation that carry HL7 v2 messages over HTTP.
3//!
4//! MLLP is how HL7 v2 usually moves, and `hl7-2-mllp` is that transport.
5//! SOAP is the other one — the transport an estate ends up with when the
6//! messages have to cross a boundary that speaks HTTP, or when the system
7//! at the far end was built by a team who had a WSDL and no socket. This
8//! crate is that transport, and it is deliberately the same shape as its
9//! MLLP sibling: it does the protocol and nothing else.
10//!
11//! # What it does
12//!
13//! - [`parse`] a SOAP envelope and take the single payload out of its body
14//! - [`Fault`]s, each carrying the HTTP status that belongs with it
15//! - [`message`] — read a v2.xml payload, or ER7 wrapped in one, and check
16//!   a payload against what the interface accepts
17//! - [`response`] — build the reply, and read one as accepted or rejected
18//! - [`wsdl`] — describe the endpoint to client tooling, at its real address
19//!
20//! # What it does not do
21//!
22//! No HTTP client and no HTTP server: this crate turns bytes into meaning
23//! and back, and leaves the socket to whatever the caller already uses.
24//! No HL7 validation and no format conversion either — `hl7-rust` and the
25//! `hl7-2-from-*` crates own those, and a transport that also converted
26//! formats would be two crates in a trench coat.
27//!
28//! # Receiving
29//!
30//! ```
31//! use hl7_2_soap::{Fault, message, response};
32//!
33//! fn handle(request_body: &str) -> (u16, String) {
34//!     match accept(request_body) {
35//!         Ok(control_id) => (200, response::success(&control_id)),
36//!         Err(fault) => (fault.status, fault.to_envelope()),
37//!     }
38//! }
39//!
40//! fn accept(request_body: &str) -> Result<String, Fault> {
41//!     let envelope = hl7_2_soap::parse(request_body)?;
42//!     let payload = envelope.payload()?;
43//!     message::check(payload, &["ADT_A05".to_string()], &[])?;
44//!     // ...validate and forward the payload here...
45//!     Ok(message::control_id(payload).unwrap_or_default().to_string())
46//! }
47//!
48//! let request = r#"<Envelope><Body><ADT_A05><MSH><MSH.10>9</MSH.10></MSH></ADT_A05></Body></Envelope>"#;
49//! assert_eq!(handle(request).0, 200);
50//!
51//! let wrong = r#"<Envelope><Body><ADT_A39/></Body></Envelope>"#;
52//! assert_eq!(handle(wrong).0, 400);
53//! ```
54//!
55//! # Sending
56//!
57//! ```
58//! use hl7_2_soap::{message, response::{self, Outcome}};
59//!
60//! let body = message::wrap_er7("MSH|^~\\&|APP||||1||ADT^A01|9|P|2.5");
61//! // ...POST `body` with Content-Type: text/xml; charset=utf-8...
62//! # let (status, reply) = (200, response::success("9"));
63//! match response::evaluate(status, &reply) {
64//!     Outcome::Accepted => {}
65//!     Outcome::Rejected(reason) => panic!("not delivered: {reason}"),
66//! }
67//! ```
68//!
69//! See `spec/index.md` for the exact rules (source of truth).
70
71#![warn(missing_docs, clippy::pedantic)]
72// XML literals keep their `r#"..."#` delimiters even where no `"` currently
73// forces them: these are documents, and adding a quoted attribute to one
74// should not also mean changing its delimiter.
75#![allow(clippy::needless_raw_string_hashes)]
76
77pub mod envelope;
78pub mod fault;
79pub mod message;
80pub mod response;
81pub mod wsdl;
82
83/// The XML reader this crate is built on, re-exported so callers can name
84/// [`xml::Element`] and walk a payload themselves without adding their own
85/// dependency.
86///
87/// `hl7-2-xml-lite-helper` has no dependencies of its own, and is shared with the other
88/// crates in this family that read XML, so there is one parser to audit
89/// rather than one per crate.
90pub use hl7_2_xml_lite_helper as xml;
91
92pub use envelope::{Envelope, parse, wrap_xml};
93pub use fault::{Fault, SOAP_NS};
94pub use response::Outcome;
95
96/// The content type a SOAP 1.1 request and response are sent with.
97///
98/// SOAP 1.1 uses `text/xml`; SOAP 1.2 would use `application/soap+xml`.
99/// This crate speaks 1.1, which is what the HL7 interfaces in the field
100/// were written against.
101pub const CONTENT_TYPE: &str = "text/xml; charset=utf-8";