Skip to main content

serde_er7/
lib.rs

1//! # serde-er7
2//!
3//! **[website](https://er7-rust.github.io/serde-er7/)**
4//! •
5//! **[documentation](https://docs.rs/serde-er7/)**
6//! •
7//! **[source](https://github.com/er7-rust/er7-rust/tree/main/serde-er7)**
8//! •
9//! **[crate](https://crates.io/crates/serde-er7)**
10//! •
11//! **[email](mailto:joel@joelparkerhenderson.com)**
12//!
13//! Serde support for [`er7`], the pipe-hat encoding that carries HL7 v2
14//! messages between healthcare systems.
15//!
16//! [`er7`] parses, queries, edits, and writes ER7 text, deliberately with
17//! no dependencies of its own. This crate is the bridge from that value
18//! tree to the rest of the Serde ecosystem: wrap a parsed [`er7::Message`]
19//! in [`Message`], and it can flow through `serde_json`, `serde_yaml`,
20//! `bincode`, or any other Serde data format — for storing a parsed
21//! message in a document database, logging it as structured JSON,
22//! returning it from a web API, or testing it with `assert_eq!` against a
23//! literal.
24//!
25//! Every impl in this crate is written by hand against the low-level
26//! `Serializer`/`Deserializer`/`Visitor` traits, following the pattern
27//! [serde's own documentation](https://docs.rs/serde/latest/serde/)
28//! walks through for a manual implementation — no `#[derive(Serialize)]`
29//! anywhere, because [`er7`]'s tree cannot derive its way to the shapes
30//! below (a bare array for a field's repetitions, a bare string for a
31//! leaf) without help.
32//!
33//! Example:
34//!
35//! ```
36//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
37//! use serde_er7::Message;
38//!
39//! let text = "MSH|^~\\&|LAB|ACME|EHR|CLINIC|20260815120000||ORU^R01|MSG9|P|2.5\r\
40//!             PID|1||12345^^^ACME^MR||SMITH^JOHN^Q||19800101|M\r\
41//!             OBX|1|NM|2093-3^Cholesterol^LN||187|mg/dL|||||F";
42//!
43//! let message = Message::parse(text)?;
44//!
45//! // Out to JSON, and back — this crate never mentions JSON itself; any
46//! // Serde format works the same way.
47//! let json = serde_json::to_string(&message)?;
48//! let back: Message = serde_json::from_str(&json)?;
49//!
50//! assert_eq!(back.to_er7(), text);
51//! # Ok(())
52//! # }
53//! ```
54//!
55//! # The shape each level serializes as
56//!
57//! | Level | Wrapper | Serializes as |
58//! |-------|---------|----------------|
59//! | Message | [`Message`] | object: `{"separators": ..., "segments": [...]}` |
60//! | Segment | [`Segment`] | object: `{"name": "PID", "fields": [...]}` |
61//! | Field | [`Field`] | array of repetitions |
62//! | Repetition | [`Repetition`] | array of components |
63//! | Component | [`Component`] | array of subcomponent strings |
64//! | Subcomponent | [`Subcomponent`] | a bare string, `raw` (not [`er7::Subcomponent::value`]-decoded) |
65//! | Separators | [`Separators`] | object of six named fields, chars as one-character strings |
66//! | Terminator | [`Terminator`] | one of the strings `"Cr"`, `"Lf"`, `"CrLf"` |
67//!
68//! Every level below [`Message`] is optional to reach for directly — most
69//! callers only ever construct a [`Message`] and let its `Serialize` impl
70//! walk the rest of the tree. The lower levels are `pub` so a caller who
71//! only wants to serialize one segment, or one field, out of a larger
72//! message can do that too.
73//!
74//! # What is deliberately not here
75//!
76//! This crate adds exactly one thing to [`er7`]: Serde support for its
77//! existing value tree. It does not add a dictionary, a validator, or a
78//! transport — [`er7`] does not have those either, and a bridge crate is
79//! the wrong place to add what the crate it bridges declined to have. See
80//! [`er7`]'s own documentation for what "encoding, not dictionary" means in
81//! practice.
82//!
83//! It also does not pick a wire format. `serde_json` appears only as a
84//! dev-dependency, to test and demonstrate against — the whole point of
85//! building against `serde`'s traits rather than writing an ER7-to-JSON
86//! converter directly is that the format is the caller's choice, not this
87//! crate's.
88//!
89//! # Documentation
90//!
91//! `spec/index.md` in the repository is this crate's specification —
92//! what each wrapper type must serialize as and why, following the same
93//! spec-driven process [`er7`] itself uses. A tutorial is in
94//! `docs/usage/index.md`, and runnable programs are in `examples/`.
95
96#![warn(missing_docs)]
97
98mod component;
99mod field;
100mod message;
101mod repetition;
102mod segment;
103mod separators;
104mod subcomponent;
105mod terminator;
106
107pub use component::Component;
108pub use field::Field;
109pub use message::Message;
110pub use repetition::Repetition;
111pub use segment::Segment;
112pub use separators::Separators;
113pub use subcomponent::Subcomponent;
114pub use terminator::Terminator;
115
116// Re-exported so a caller can name `er7::Message`, `er7::Error`, and the
117// rest without adding their own dependency on `er7` — the same convenience
118// `hl7-2-5-to-xml-using-rust` and `hl7-2-5-to-json-using-rust` extend for
119// the crates they build on top of.
120pub use er7;