Skip to main content

hl7_3_soap/
lib.rs

1//! HL7 v3 over SOAP: the envelope, faults, message carriage, WSDL, and
2//! acknowledgement evaluation that carry HL7 v3 messages over HTTP.
3//!
4//! Unlike v2 — where MLLP is the usual transport and SOAP is the exception
5//! — SOAP *is* HL7 v3's own historically dominant transport: v3 was
6//! designed alongside SOAP/WS-*, and real deployments (NHS England's
7//! Personal Demographics Service, IHE profiles built on v3) carry it that
8//! way. This crate is that transport, and it is deliberately the same
9//! shape as its `hl7-2-soap` cousin: it does the protocol and nothing
10//! else.
11//!
12//! # What it does
13//!
14//! - [`parse`] a SOAP envelope and take the single payload out of its body
15//! - [`Fault`]s, each carrying the HTTP status that belongs with it
16//! - [`message`] — read which interaction a v3 payload is, its control ID,
17//!   and its claimed assigning authority, and check a payload against what
18//!   the interface accepts
19//! - [`response`] — build the real HL7 v3 acknowledgement, and read one as
20//!   accepted or rejected
21//! - [`wsdl`] — describe the endpoint to client tooling, at its real address
22//!
23//! # What it does not do
24//!
25//! No HTTP client and no HTTP server: this crate turns bytes into meaning
26//! and back, and leaves the socket to whatever the caller already uses.
27//! No RIM decoding and no domain-payload interpretation either — `hl7-3`
28//! owns those; this crate reads only what it needs to route and
29//! acknowledge a message, the same restraint `hl7-2-soap` applies to v2.
30//!
31//! # Receiving
32//!
33//! ```
34//! use hl7_3_soap::{Fault, message, response};
35//!
36//! fn handle(request_body: &str) -> (u16, String) {
37//!     match accept(request_body) {
38//!         Ok(control_id) => (200, response::success(&control_id)),
39//!         Err(fault) => (fault.status, fault.to_envelope()),
40//!     }
41//! }
42//!
43//! fn accept(request_body: &str) -> Result<String, Fault> {
44//!     let envelope = hl7_3_soap::parse(request_body)?;
45//!     let payload = envelope.payload()?;
46//!     message::check(payload, &["PRPA_IN201305UV02".to_string()], &[])?;
47//!     // ...decode the payload with hl7-3, and forward it, here...
48//!     Ok(message::control_id(payload).unwrap_or_default().to_string())
49//! }
50//!
51//! let request = r#"<Envelope><Body><PRPA_IN201305UV02><id extension="9"/></PRPA_IN201305UV02></Body></Envelope>"#;
52//! assert_eq!(handle(request).0, 200);
53//!
54//! let wrong = r#"<Envelope><Body><PRPA_IN201306UV02/></Body></Envelope>"#;
55//! assert_eq!(handle(wrong).0, 400);
56//! ```
57//!
58//! # Sending
59//!
60//! ```
61//! use hl7_3_soap::{envelope, response::{self, Outcome}};
62//!
63//! let body = envelope::wrap_xml(r#"<PRPA_IN201305UV02><id extension="9"/></PRPA_IN201305UV02>"#);
64//! // ...POST `body` with Content-Type: text/xml; charset=utf-8...
65//! # let (status, reply) = (200, response::success("9"));
66//! match response::evaluate(status, &reply) {
67//!     Outcome::Accepted => {}
68//!     Outcome::Rejected(reason) => panic!("not delivered: {reason}"),
69//! }
70//! ```
71//!
72//! See `spec/index.md` for the exact rules (source of truth).
73
74#![warn(missing_docs, clippy::pedantic)]
75// XML literals keep their `r#"..."#` delimiters even where no `"` currently
76// forces them: these are documents, and adding a quoted attribute to one
77// should not also mean changing its delimiter.
78#![allow(clippy::needless_raw_string_hashes)]
79
80pub mod envelope;
81pub mod fault;
82pub mod message;
83pub mod response;
84pub mod wsdl;
85
86/// The XML reader this crate is built on, re-exported so callers can name
87/// [`xml::Element`] and walk a payload themselves without adding their own
88/// dependency.
89///
90/// `hl7-2-xml-lite-helper` has no dependencies of its own, and is shared
91/// with the other crates in this family that read XML, so there is one
92/// parser to audit rather than one per crate.
93pub use hl7_2_xml_lite_helper as xml;
94
95pub use envelope::{Envelope, parse, wrap_xml};
96pub use fault::{Fault, SOAP_NS};
97pub use response::Outcome;
98
99/// The content type a SOAP 1.1 request and response are sent with.
100///
101/// SOAP 1.1 uses `text/xml`; SOAP 1.2 would use `application/soap+xml`.
102/// This crate speaks 1.1, matching `hl7-2-soap` and the real HL7 v3
103/// interfaces it was built from.
104pub const CONTENT_TYPE: &str = "text/xml; charset=utf-8";