1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//! # ER7
//!
//! **[website](https://er7-rust.github.io/)**
//! •
//! **[documentation](https://docs.rs/er7/)**
//! •
//! **[source](https://github.com/er7-rust/er7-rust)**
//! •
//! **[crate](https://crates.io/crates/er7)**
//! •
//! **[email](mailto:joel@joelparkerhenderson.com)**
//!
//! ER7 — the pipe-hat encoding that carries HL7 v2 messages between
//! healthcare systems — parsed, queried, edited, and written back, with no
//! dependencies.
//!
//! ER7 is compact and everywhere, and it is also unforgiving: a value's
//! meaning comes entirely from its position, so one misplaced `|` silently
//! shifts everything after it. This crate exists to make that structure
//! explicit and to keep it intact — text is stored exactly as it arrived,
//! and decoded only when you ask for a value.
//!
//! Example:
//!
//! ```
//! # fn main() -> Result<(), er7::Error> {
//! let text = "MSH|^~\\&|LAB|ACME|EHR|CLINIC|20260815120000||ORU^R01|MSG9|P|2.5\r\
//! PID|1||12345^^^ACME^MR||SMITH^JOHN^Q||19800101|M\r\
//! OBX|1|NM|2093-3^Cholesterol^LN||187|mg/dL|||||F";
//!
//! let message = er7::parse(text)?;
//! assert_eq!(message.control_id().as_deref(), Some("MSG9"));
//! assert_eq!(message.query("PID-5.1")?.as_deref(), Some("SMITH"));
//! 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);
//! # Ok(())
//! # }
//! ```
//!
//! # What is here
//!
//! | Item | Purpose |
//! |------|---------|
//! | [`parse()`], [`parse_with`] | read text into a [`Message`] |
//! | [`Message`], [`Segment`], [`Field`], [`Repetition`], [`Component`], [`Subcomponent`] | the six-level value tree |
//! | [`Message::query`], [`Message::query_all`] | read values by HL7 [`Path`], e.g. `PID-5.1` or `OBX[2]-5` |
//! | [`Message::to_er7`], [`Segment::to_text`] and siblings | write the tree back out, as sent or decoded |
//! | [`split_messages`] | cut a batch file or concatenated messages into individual ones |
//! | [`escape::unescape`], [`escape::escape`], [`escape::escapes`] | the escape-sequence vocabulary |
//! | [`Separators`] | the delimiter set, read from each message's own header rather than assumed |
//!
//! # What is deliberately not here
//!
//! This crate is an encoding, not a dictionary. It does not know which
//! fields a segment should have, what data type each one carries, which
//! message structures exist, or what any code table means — all of that is
//! version-specific and belongs in a layer above. It performs no
//! validation and no transport.
//!
//! The one exception is a handful of `MSH` accessors such as
//! [`Message::control_id`], because routing a message requires reading
//! them and their positions have never moved in any HL7 v2 release.
//!
//! # The crate family
//!
//! Each layer above this one is its own crate, so a caller pays only for
//! what they use:
//!
//! | Crate | Adds |
//! |-------|------|
//! | [`er7-redact`](https://crates.io/crates/er7-redact) | redaction: remove patient detail without changing the shape of the message |
//! | [`serde-er7`](https://crates.io/crates/serde-er7) | Serde support for every type in this tree |
//! | [`hl7-2-5-to-xml`](https://crates.io/crates/hl7-2-5-to-xml), [`hl7-2-5-to-json`](https://crates.io/crates/hl7-2-5-to-json) | the HL7 v2.5 dictionary |
//!
//! `spec/01-purpose-and-scope.md` §1.3.1 is the source of truth for that
//! list, and <https://er7-rust.github.io/ecosystem/> presents it.
//!
//! # Documentation
//!
//! `spec/index.md` in the repository is the normative specification of
//! everything above; where this documentation and that document disagree,
//! that document is right. Section references such as "spec §6.2" and rule
//! IDs such as "R16" throughout these docs point into it.
//!
//! The repository also holds a tutorial (`docs/usage/`), references for
//! [paths](Path) (`docs/paths/`) and [escape sequences](escape)
//! (`docs/escapes/`), an FAQ (`docs/faq/`), and runnable programs
//! (`examples/`).
pub use crate;
pub use crate;
pub use cratePath;
pub use crateRenderOptions;
pub use crate;
use fmt;
/// What can go wrong.
///
/// Four variants, arising from exactly two situations: a message with no
/// usable header, and a path that is not a path. Everything else about ER7
/// is recoverable, and this crate recovers rather than refusing, because a
/// receiver that rejects a message it could have read is worse than one
/// that reads it as written (spec §11, rules R6 and R23).
///
/// Example:
///
/// ```
/// use er7::Error;
///
/// assert!(matches!(er7::parse(""), Err(Error::Empty)));
/// assert!(matches!(er7::parse("PID|1"), Err(Error::MissingHeader(_))));
/// assert!(matches!(er7::parse("MSH"), Err(Error::BadHeader(_))));
/// assert!(matches!("PID-0".parse::<er7::Path>(), Err(Error::BadPath(_))));
///
/// // Everything below the header parses, however odd it is.
/// assert!(er7::parse("MSH|^~\\&|LAB\rZZZ\rPID||||||||||").is_ok());
/// ```