Documentation

Conformance

What “supports HL7 v2 releases 2.1 through 2.9” means here, stated precisely enough to evaluate against — including everything it does not mean.

The one-paragraph answer

hl7-2 reads and writes any syntactically well-formed ER7 message from any release, losing nothing and rejecting nothing. What varies by release, and what the dictionary bounds, is how much of the message it can name: which fields it knows the data type of, which segments it can check against a structure, and which values it can validate. It ships a complete v2.5 dictionary of 24 segments, 42 composite data types, and 4 message structures, and models the other thirteen releases as deltas of it.

So: if your criterion is “does it handle our feed without losing data”, the answer is yes for any ER7 input. If it is “does it know what every field in our feed means”, the answer is bounded by the dictionary below — and extending it is one JSON file.

Degrade, never reject

Every conformance gap in this project resolves the same way, and it is the design decision the rest of this page elaborates:

An unmodelled segment, field, data type, structure, or release difference costs you a name, never a value. The field reads with a positional generic name instead of its data type; the message still parses, every value is still reachable by path, and rendering still returns the original bytes.

Only four things fail a call: a message with no usable MSH header, a path that is not a path, a dictionary that will not load, and — in struct mode — a value that does not fit the Rust type you asked for. Everything below the header degrades, and is reported by validate() if you want to know.

That is why the incompleteness here is bounded rather than dangerous. A library that rejected what it did not recognise would turn every gap into a dropped clinical message.

The v2.5 dictionary, exactly

v2.5 is the complete base; every other release is a delta of it. See Versions and compatibility for what each release restates. The base covers:

24 segments

segments
AL1 BLG CTI DG1 DSC ERR EVN IN1 MRG MSA MSH NK1 NTE OBR OBX ORC PD1 PID PR1 PV1 PV2 ROL SFT SPM

42 composite data types

data types
AD AUI CCD CE CNE CNN CP CQ CWE CX DLD DLN DR ED EI EIP ELD ERL FC FN HD JCC MO MOC MSG NDL PL PRL PT RI RP SAD SN SPS TQ TS VID XAD XCN XON XPN XTN

4 message structuresACK, ADT_A01, ORM_O01, ORU_R01 — with ADT_A04, ADT_A08, and ADT_A13 aliased onto ADT_A01.

That is the honest number. HL7 v2.5 defines well over a hundred segments and around eighty message structures. This dictionary covers the ones carrying the overwhelming majority of real interface traffic — admissions, orders, results, and their acknowledgements — and nothing else.

What happens outside that set

This is the part that decides whether the number above matters to you.

InputResult
A segment not in the 24Parses; fields read positionally; SegmentUnknown warning
A Z-segmentParses; no warning at all — a local extension is the site’s business
A field past the end of a known segmentParses; reads positionally; FieldUnknown warning
A component past the end of a known typeParses; reads positionally; ComponentUnknown warning
A message structure not in the 4Parses; reads as a flat segment list; StructureUnknown warning
A data type not in the 42Treated as primitive: the value is a scalar

In every row the message parses, every value is reachable by path, and the round trip is byte for byte.

Encoding

Encoding conformance belongs to er7, and it is where this project makes its strongest claims:

  • Delimiters come from the message. MSH-1 and MSH-2 declare them; the usual set is a default, not an assumption.
  • Escape sequences are decoded on demand and re-encoded on the way out.
  • The explicit null is distinct from an absent value, at every level, and stays distinct through parsing, mutation, and rendering. This is the single most commonly botched detail in HL7 tooling, and it is the difference between “the patient has no middle name” and “delete the middle name we have on file”.
  • Byte-for-byte round trip: a message parsed and not modified renders back identically, after documented input normalization.
  • Batches split into their constituent messages.
  • PathsPID-5.1, OBX[2]-5[1].1.2 — with repetition, component, and subcomponent addressing.

What is out of scope

Named explicitly, because an evaluation needs the gaps more than the features.

  • Vocabulary and code tables. HL7 tables, LOINC, SNOMED CT, ICD. A coded value is read as the string it is; nothing checks that it exists or is allowed there.
  • Conformance profiles. HL7 v2 message profiles are not read, applied, or generated. Validation is against the dictionary, not against a profile.
  • Field length limits. Not modelled, so never enforced.
  • Usage codes beyond the required/repeats pair the dictionary carries. Conditional usage rules are not modelled.
  • Exact repetition upper bounds. “At most 10” and “unbounded” both read as “repeats”.
  • Implementation guides. No US Core, no national extension, no IHE profile, no jurisdiction-specific rule set.
  • Clinical semantics. Nothing here knows that an A08 should update a patient rather than create one.

Transports, conversions, v3, the HL7® FHIR® standard

MLLP
Framing, streaming reassembly, and acknowledgement generation. No TLS, no async runtime, no pooling, no retry policy, no persistence — and it opens no sockets itself; you supply the stream.
SOAP
SOAP 1.1 envelopes, faults with their HTTP statuses, both ways v2 is carried in a body, the response, and a WSDL. No HTTP client or server, and deliberately no SOAP 1.2, WS-Security, WS-Addressing, MTOM, or attachments — none appeared in the interfaces this was built from.
Format conversions
ER7 to and from v2.xml and typed JSON, in both directions, with the tree names, the JSON keys, and the XML elements deliberately identical. The round trip is a test, not an aspiration.
HL7 v3
A foundation, not an implementation: six RIM backbone classes, six data types, and the three-level envelope read generically. No vocabulary validation, no per-interaction schemas, and no CDA — it reuses the RIM but its document model is its own thing.
HL7® FHIR® standard
Not implemented. The umbrella crate reserves the module path and nothing more. If you need the HL7® FHIR® standard today, this project is not it.

How to evaluate this yourself

Do not take the above on trust; it takes about an hour to check.

On a redacted sample from your own feed

sh
# 1. What does it name, and what does it miss?
hl7-v2 --tree --paths redacted-sample.hl7

# 2. Does the round trip come back byte for byte?
hl7-v2 --er7 redacted-sample.hl7 | diff - redacted-sample.hl7

# 3. How big is the gap between this dictionary and your feed?
hl7-v2 --check redacted-sample.hl7
  1. What does it name? A field that reads as PID.34 rather than by its data type is a dictionary gap, and you will see exactly which ones you have.
  2. Does the round trip hold? The diff should be empty.
  3. How big is the gap? Count the warnings by kind over a day of redacted traffic. SegmentUnknown and StructureUnknown size it for you.
  4. Then decide whether the gap is work. A vendor dialect is one JSON file, or a dictionary generated from your own XSDs. Usually an afternoon, not a project.