Documentation
Architecture
Fourteen crates in one Cargo workspace, plus er7 outside it. This page is the reasoning: what each seam is for, and why you can stop at any of them.
The map
er7 the ER7 encoding: delimiters,
escapes, paths, byte-for-byte
rendering, batch splitting
|
hl7-2 the HL7 v2 dictionary: releases
| 2.1-2.9, data types, message
| structures; three parsing modes;
| mutation; validation
|
+-- hl7-2-mllp transport: HL7 v2 over TCP (MLLP)
+-- hl7-2-soap transport: HL7 v2 over HTTP (SOAP)
+-- hl7-2-from-er7-into-json format conversions
+-- hl7-2-from-er7-into-xml
+-- hl7-2-from-json-into-er7
+-- hl7-2-from-xml-into-er7
+-- hl7-2-from-xsd-into-json-dictionary writes the dictionaries
| hl7-2 reads, from HL7
| v2.xml XSDs
+-- hl7-2-xml-lite-helper shared minimal XML reader, also
| used directly by:
+-- hl7-3 HL7 v3: RIM backbone classes,
| coded values, the three-level
| message envelope — a foundation,
| not a full implementation
+-- hl7-3-derive #[derive(FromElement)] for hl7-3's
| struct mode
+-- hl7-3-soap transport: HL7 v3 over HTTP (SOAP)
hl7 the umbrella crate — hl7::v2 and
hl7::v3 today, room for hl7::fhirOne crate per layer
Read the map top to bottom and each line is a layer that can be taken on its own. That is the organising principle, and it is worth spelling out because it is what makes fourteen crates less alarming than it sounds.
er7— syntax- Delimiters, escapes, paths, byte-for-byte rendering, batch splitting. No knowledge of what any field means. It lives in its own repository, outside this workspace, because plenty of people want the encoding and not the dictionary.
hl7-2— meaning- The per-release data-type tables, the message structures, and the three ways to apply them. This is where nearly all the v2 capability lives, and it is the only crate most callers need.
- The transports — delivery
hl7-2-mllpandhl7-2-soapknow how to get a message across a network and how to answer it. Neither has an opinion about HL7 semantics, and neither owns a socket:IoTransporttakes any reader/writer, and the SOAP crate leaves HTTP to whatever stack you already use.- The conversions — representation
- Four crates covering ER7's two directions against both target formats. They are the layer you use when what is downstream is not Rust.
- The tooling — everything else
- The derive macros, the dictionary builder, and the shared XML reader. Each is in a crate of its own for a specific reason, given below.
One module per standard
The umbrella crate is, in its entirety:
// hl7/src/lib.rs, in full, minus the documentation
pub use hl7_2 as v2;
pub use hl7_3 as v3;Nothing lives at its root, and that is a decision rather than an omission. A “message”, a
“segment”, and a “code” mean different things in v2, v3, and FHIR, and flattening them into one
namespace would invite mixing them up — in a domain where mixing them up means a clinical
record. Depend on hl7-2 or hl7-3 directly if you want one standard
without the indirection; the umbrella exists for callers who want the room left for hl7::fhir.
Why the dependency counts are so small
hl7-2 has one dependency. hl7-3 has one. hl7-2-soap has one. hl7-2-xml-lite-helper has none, and intends to
keep it that way. hl7-2-mllp with --no-default-features has none.
That is not minimalism for its own sake. Healthcare software gets audited, and a dependency
tree is part of what gets audited. The JSON reader that loads dictionaries in hl7-2 is hand-written for exactly this reason — in a domain where trees get read
line by line, a two-crate tree is worth a few hundred lines of code.
The same logic is why the derive macros live in separate crates. syn and quote are large, and they are compiled only for callers who ask for the macros;
the default build of hl7-2 never sees them.
Forward and reverse are not symmetric
The four conversion crates look like two symmetric pairs. They are not, and the asymmetry is the most interesting thing about them.
A forward crate — ER7 into XML or JSON — needs the full HL7 v2.5 dictionary,
because naming <XPN.1> rather than <PID.5.1> requires
knowing that PID-5 holds an XPN. It also needs the message-structure
grammars, to decide what nests inside what.
A reverse crate needs neither. Every element or key name the forward crate
writes already carries its own position, as the number after the name's last dot — whether the
name in front of that number is a recognized data type or a positional fallback. So reversal is
a purely structural rebuild. Each reverse crate's spec/index.md §1.1 says exactly
this, and it is why those two crates are so much smaller than their counterparts.
What is shared, and what is deliberately not
hl7-2-xml-lite-helper exists because
three crates in this family had each written their own minimal XML reader, and three copies of a
parser is three places for a bug. It now serves four: hl7-2-soap, hl7-3-soap, hl7-2-from-xml-into-er7, and hl7-2-from-xsd-into-json-dictionary — plus hl7-3, which reads v3's
native XML through it. Its name says it is scoped to this family; it is not offered as a
general-purpose parser.
What is not shared is just as deliberate. The two forward conversion crates keep their
own renderers, because the target formats differ in ways no shared abstraction would model
honestly — JSON has real arrays and a real null; XML has repeated sibling elements
and empty elements. Their specs are kept consistent with each other instead, and each one's §0
states exactly where they are meant to diverge.
hl7-3-soap deliberately does not depend on hl7-3. A SOAP envelope is
XML, and reading one requires no HL7 knowledge beyond the names of a few elements — so the
transport stays usable by anyone who wants to route v3 traffic without decoding it.
The workspace, and the history behind it
This is one Cargo workspace with one Cargo.lock at its root. Member crates depend
on each other by relative path, and the flat one-directory-per-crate layout was kept
specifically so those paths did not have to change — because most of these directories used to
be standalone repositories, merged in with git subtree so that each crate's commit
history is still walkable under its own directory.
Each crate keeps its own edition, feature set, and dependency list; the workspace does not use [workspace.package] inheritance or a shared [workspace.dependencies] table. That is a live decision rather than an oversight —
adding either would touch every member's manifest at once.
One historical wrinkle shows up in names. Every hl7-v2* crate was renamed to hl7-2* — hl7-v2 itself because that name was already taken on
crates.io by an unrelated crate, and the rest for consistency. The archived repositories those
directories came from still carry the original names, and the installed CLI binary is still
called hl7-v2.
Every crate, by layer
Core
The standards themselves, and the umbrella crate that re-exports them.
| Crate | Depends on | What it adds |
|---|---|---|
hl7 | hl7-2, hl7-3 | The umbrella crate: one module per HL7 standard |
hl7-2 | er7 | HL7 v2 itself: parse, navigate, validate, modify, render |
hl7-3 | hl7-2-xml-lite-helper | HL7 v3: the RIM backbone, the data types, the message envelope |
Transports
Getting messages across a network, and answering them.
| Crate | Depends on | What it adds |
|---|---|---|
hl7-2-mllp | hl7-2 (feature `ack`), chrono (feature `clock`) | MLLP: HL7 v2 framed on a TCP stream |
hl7-2-soap | hl7-2-xml-lite-helper | HL7 v2 carried in a SOAP envelope over HTTP |
hl7-3-soap | hl7-2-xml-lite-helper | HL7 v3 carried in a SOAP envelope over HTTP |
Format conversions
ER7 in both directions against both target formats.
| Crate | Depends on | What it adds |
|---|---|---|
hl7-2-from-er7-into-xml | er7, hl7-2 | ER7 → the official v2.xml XML representation |
hl7-2-from-xml-into-er7 | er7, hl7-2-xml-lite-helper | v2.xml XML → ER7 |
hl7-2-from-er7-into-json | er7 | ER7 → typed JSON |
hl7-2-from-json-into-er7 | er7 | Typed JSON → ER7 |
Tooling and helpers
The macros, the dictionary builder, and the shared XML reader.
| Crate | Depends on | What it adds |
|---|---|---|
hl7-2-derive | syn, quote | #[derive(FromHl7)] and #[derive(ToHl7)] for struct mode |
hl7-3-derive | syn, quote | #[derive(FromElement)] for HL7 v3 struct mode |
hl7-2-from-xsd-into-json-dictionary | hl7-2-xml-lite-helper | HL7 v2.xml XSDs → the JSON dictionary hl7-2 reads |
hl7-2-xml-lite-helper | nothing | The small, dependency-free XML reader this family shares |
14 crates here, plus er7 in its own repository. See the crate reference for a page on each.