Fidelity
Any message the crate reads, it can write back unchanged — unusual delimiters, unknown segments, empty positions, escape sequences and all. Leaf text is stored raw and decoded on demand, which is what makes that possible.
Rust crate & CLI · v0.1.1
Parse, query, edit, and write HL7 v2 messages in the ER7 pipe-hat encoding — with a byte-for-byte round trip and zero dependencies.
MSH|^~\&|LAB|ACME|EHR|CLINIC|20260815081500||ORU^R01^ORU_R01|MSG00042|P|2.5 PID|1||444333222^^^ACME&1.2.840.114398.1.100&ISO^MR||EVERYWOMAN^EVE^E||19620320|F OBX|1|NM|2093-3^Cholesterol^LN||187|mg/dL|<200|N|||F
A lab result: who sent it, who the patient is, and what the cholesterol was. Delimiters in amber, segment names in blue. The same content in HL7’s XML encoding runs to several kilobytes.
PID-5.1 is a family name because it is fifth and first, not because anything in
the message says so. One misplaced | silently shifts everything after it, and a
message that arrives correct but leaves altered is worse than one that fails to parse —
because the failure is visible and the alteration is not.
This crate makes that structure explicit and keeps it intact. Text is stored exactly as it arrived and decoded only when you ask for a value.
The default output is an outline: one line per value, labelled with the HL7 path that names it. Every label is a valid query, so you can read a path off the output and paste it straight back in.
MSH-1 |
MSH-2 ^~\&
MSH-3 LAB
MSH-9.1 ORU
MSH-9.2 R01
MSH-9.3 ORU_R01
MSH-10 MSG00042
PID-3.1 444333222
PID-3.4.1 ACME
PID-3.4.2 1.2.840.114398.1.100
PID-5.1 EVERYWOMAN
PID-5.2 EVE
PID-13[1] 555-555-1111
PID-13[2] 555-555-2222
OBX[1]-3.2 Cholesterol
OBX[1]-5 187
OBX[2]-3.2 Triglycerides# One value
er7 --query PID-5.1 message.er7
#=> EVERYWOMAN
# Every observation in a result
er7 --query OBX-5 message.er7
#=> 187
#=> 102
# Exactly as sent, escapes intact
er7 --raw --query OBX-5 message.er7
# Canonical ER7, readable in a terminal
er7 --normalize --terminator lf message.er7
# The second message of a batch file
cat batch.er7 | er7 --message 2let message = er7::parse(text)?;
assert_eq!(message.control_id().as_deref(), Some("MSG00042"));
assert_eq!(message.query("PID-5.1")?.as_deref(), Some("EVERYWOMAN"));
assert_eq!(message.query("OBX-3.2")?.as_deref(), Some("Cholesterol"));
// What went in comes back out, byte for byte.
assert_eq!(message.to_er7(), text); query returns the first match and query_all returns every one, so OBX-5 across a result with three observations gives three values. When you need
the node itself — to edit it, or to ask whether it is an explicit null — walk the tree with
1-based accessors that match HL7’s own numbering.
Any message the crate reads, it can write back unchanged — unusual delimiters, unknown segments, empty positions, escape sequences and all. Leaf text is stored raw and decoded on demand, which is what makes that possible.
Absent, empty, and the explicit null "" are three different answers,
not one. The null means clear this value; collapsing it into “empty”
leaves a withdrawn allergy on a patient record.
Below the header, nothing fails. Unknown segments, local Z segments, ragged field counts, and undecodable escapes are all data. A receiver that rejects a message it could have read drops clinical information.
This crate does not know what PID-5 means. It does not carry
segment definitions, data types, message structures, or code tables — all of that is specific
to an HL7 version, while the ER7 encoding is shared by every release from v2.1 to v2.9.
Keeping them apart means the encoding is maintained and tested once, and a dictionary crate
can choose its own version. See the ecosystem for the crates built
on top: the HL7 v2.5 dictionary, and tools such as er7-redact and serde-er7.
cargo add er7cargo install er7