hl7_parser/message/mod.rs
1mod separators;
2pub use separators::*;
3mod subcomponent;
4pub use subcomponent::*;
5mod component;
6pub use component::*;
7mod repeat;
8pub use repeat::*;
9mod field;
10pub use field::*;
11mod segment;
12pub use segment::*;
13
14use crate::locate::LocatedCursor;
15
16use crate::{
17 parser::ParseError,
18 query::{LocationQuery, LocationQueryResult},
19};
20
21/// A parsed HL7 message. This is the top-level structure that you get when you parse a message.
22/// It contains the segments of the message, as well as the separators used in the message.
23///
24/// # Examples
25///
26/// ```
27/// let message = hl7_parser::Message::parse("MSH|^~\\&|").unwrap();
28/// let msh = message.segment("MSH").unwrap();
29/// assert_eq!(msh.name, "MSH");
30/// ```
31#[derive(Debug, Clone, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize))]
33pub struct Message<'m> {
34 pub(crate) source: &'m str,
35 pub segments: Vec<Segment<'m>>,
36 pub separators: Separators,
37}
38
39impl<'m> Message<'m> {
40 /// Parse a message from a string.
41 /// This will return an error if the message is not a valid HL7 message.
42 ///
43 /// # Examples
44 ///
45 /// ```
46 /// let message = hl7_parser::Message::parse("MSH|^~\\&|EPIC|EPICADT|SMS|SMSADT|199912271408|CHARRIS|ADT^A04|1817457|D|2.5|\rEVN|A04|199912271408|||CHARRIS\rPID||0493575^^^2^ID 1|454721||DOE^JOHN^^^^|DOE^JOHN^^^^|19480203|M||B|254 MYSTREET AVE^^MYTOWN^OH^44123^USA||(216)123-4567|||M|NON|400003403~1129086|\rNK1||ROE^MARIE^^^^|SPO||(216)123-4567||EC|||||||||||||||||||||||||||\rPV1||O|168 ~219~C~PMA^^^^^^^^^||||277^ALLEN MYLASTNAME^BONNIE^^^^|||||||||| ||2688684|||||||||||||||||||||||||199912271408||||||002376853").unwrap();
47 /// let msh = message.segment("MSH").unwrap();
48 /// assert_eq!(msh.field(4).unwrap().raw_value(), "EPICADT");
49 /// let pid = message.segment("PID").unwrap();
50 /// let patient_name = pid.field(5).unwrap();
51 /// assert_eq!(patient_name.raw_value(), "DOE^JOHN^^^^");
52 /// let first_name = patient_name.component(2).unwrap();
53 /// let last_name = patient_name.component(1).unwrap();
54 /// assert_eq!(first_name.raw_value(), "JOHN");
55 /// assert_eq!(last_name.raw_value(), "DOE");
56 /// ```
57 pub fn parse(input: &'m str) -> Result<Self, ParseError> {
58 crate::parser::message::message(false)(input.into())
59 .map(|(_, m)| m)
60 .map_err(|e| e.into())
61 }
62
63 /// Parse a message from a string, allowing lenient newlines.
64 /// This will return an error if the message is not a valid HL7 message.
65 /// If `lenient_newlines` is true, this will allow `\n` and `\r\n` to be treated
66 /// the same as `\r` as the separator for segments.
67 /// This is useful for parsing messages that come as standard text files
68 /// where each segment is separated by platform-specific newlines.
69 ///
70 /// # Examples
71 ///
72 /// ```
73 /// let message = hl7_parser::Message::parse_with_lenient_newlines("MSH|^~\\&|EPIC|EPICADT|SMS|SMSADT|199912271408|CHARRIS|ADT^A04|1817457|D|2.5|\nEVN|A04|199912271408|||CHARRIS\nPID||0493575^^^2^ID 1|454721||DOE^JOHN^^^^|DOE^JOHN^^^^|19480203|M||B|254 MYSTREET AVE^^MYTOWN^OH^44123^USA||(216)123-4567|||M|NON|400003403~1129086|\nNK1||ROE^MARIE^^^^|SPO||(216)123-4567||EC|||||||||||||||||||||||||||\nPV1||O|168 ~219~C~PMA^^^^^^^^^||||277^ALLEN MYLASTNAME^BONNIE^^^^|||||||||| ||2688684|||||||||||||||||||||||||199912271408||||||002376853", true).unwrap();
74 /// let msh = message.segment("MSH").unwrap();
75 /// assert_eq!(msh.field(4).unwrap().raw_value(), "EPICADT");
76 /// let pid = message.segment("PID").unwrap();
77 /// let patient_name = pid.field(5).unwrap();
78 /// assert_eq!(patient_name.raw_value(), "DOE^JOHN^^^^");
79 /// let first_name = patient_name.component(2).unwrap();
80 /// let last_name = patient_name.component(1).unwrap();
81 /// assert_eq!(first_name.raw_value(), "JOHN");
82 /// assert_eq!(last_name.raw_value(), "DOE");
83 /// ```
84 pub fn parse_with_lenient_newlines(
85 input: &'m str,
86 lenient_newlines: bool,
87 ) -> Result<Self, ParseError> {
88 crate::parser::message::message(lenient_newlines)(input.into())
89 .map(|(_, m)| m)
90 .map_err(|e| e.into())
91 }
92
93 /// Find a segment with the given name. If there are more than one segments
94 /// with this name, return the first one.
95 ///
96 /// # Examples
97 ///
98 /// ```
99 /// let message = hl7_parser::Message::parse("MSH|^~\\&|EPIC|EPICADT|SMS|SMSADT|199912271408|CHARRIS|ADT^A04|1817457|D|2.5|\rEVN|A04|199912271408|||CHARRIS\rPID||0493575^^^2^ID 1|454721||DOE^JOHN^^^^|DOE^JOHN^^^^|19480203|M||B|254 MYSTREET AVE^^MYTOWN^OH^44123^USA||(216)123-4567|||M|NON|400003403~1129086|\rNK1||ROE^MARIE^^^^|SPO||(216)123-4567||EC|||||||||||||||||||||||||||\rPV1||O|168 ~219~C~PMA^^^^^^^^^||||277^ALLEN MYLASTNAME^BONNIE^^^^|||||||||| ||2688684|||||||||||||||||||||||||199912271408||||||002376853").unwrap();
100 /// let msh = message.segment("MSH").unwrap();
101 /// assert_eq!(msh.name, "MSH");
102 /// assert_eq!(msh.field(4).unwrap().raw_value(), "EPICADT");
103 /// let pid = message.segment("PID").unwrap();
104 /// assert_eq!(pid.field(2).unwrap().raw_value(), "0493575^^^2^ID 1");
105 /// ```
106 pub fn segment(&self, name: &str) -> Option<&Segment<'m>> {
107 self.segments.iter().find(|s| s.name == name)
108 }
109
110 /// Find the nth segment with the given name. If there are fewer than n segments
111 /// with this name, return `None`.
112 /// Segments are 1-indexed.
113 ///
114 /// # Examples
115 ///
116 /// ```
117 /// let message =
118 /// hl7_parser::Message::parse("MSH|^~\\&|\rABC|foo\rXYZ|bar\rABC|baz").unwrap();
119 /// let abc1 = message.segment_n("ABC", 1).unwrap();
120 /// assert_eq!(abc1.field(1).unwrap().raw_value(), "foo");
121 /// let abc2 = message.segment_n("ABC", 2).unwrap();
122 /// assert_eq!(abc2.field(1).unwrap().raw_value(), "baz");
123 /// let abc3 = message.segment_n("ABC", 3);
124 /// assert_eq!(abc3, None);
125 /// ```
126 pub fn segment_n(&self, name: &str, n: usize) -> Option<&Segment<'m>> {
127 debug_assert!(n > 0, "Segments are 1-indexed");
128 self.segments.iter().filter(|s| s.name == name).nth(n - 1)
129 }
130
131 /// Count the number of segments with the given name.
132 pub fn segment_count(&self, name: &str) -> usize {
133 self.segments.iter().filter(|s| s.name == name).count()
134 }
135
136 /// An iterator over the segments of the message
137 pub fn segments(&self) -> impl Iterator<Item = &Segment<'m>> {
138 self.segments.iter()
139 }
140
141 /// Get the raw value of the message. This is the value as it appears in the message,
142 /// without any decoding of escape sequences, and including all segments and
143 /// their separators.
144 /// This is the same as the input string that was used to parse the message.
145 pub fn raw_value(&self) -> &'m str {
146 self.source
147 }
148
149 /// Locate the cursor within the message. Equivalent to calling
150 /// `hl7_parser::locate::locate_cursor` with the message and the cursor position.
151 pub fn locate_cursor(&self, cursor: usize) -> Option<LocatedCursor> {
152 crate::locate::locate_cursor(self, cursor)
153 }
154
155 /// Query the message for a specific location. This is a more flexible way to
156 /// access the fields, components, and subcomponents of the message.
157 ///
158 /// # Examples
159 /// ```
160 /// let message =
161 /// hl7_parser::Message::parse("MSH|^~\\&|foo|bar|baz|quux|20010504094523||ADT^A01|1234|P|2.3|||").unwrap();
162 /// let field = message.query("MSH.3").unwrap().raw_value();
163 /// assert_eq!(field, "foo");
164 /// let component = message.query("MSH.7.1").unwrap().raw_value();
165 /// assert_eq!(component, "20010504094523");
166 /// ```
167 pub fn query<Q>(&'m self, query: Q) -> Option<LocationQueryResult<'m>>
168 where
169 Q: TryInto<LocationQuery>,
170 {
171 let query = query.try_into().ok()?;
172 let segment_index = query.segment_index.unwrap_or(1);
173
174 if let Some(field) = query.field {
175 let repeat = query.repeat.unwrap_or(1);
176 if let Some(component) = query.component {
177 if let Some(subcomponent) = query.subcomponent {
178 self.segment_n(&query.segment, segment_index)
179 .and_then(|s| s.field(field))
180 .and_then(|f| f.repeat(repeat))
181 .and_then(|r| r.component(component))
182 .and_then(|c| c.subcomponent(subcomponent))
183 .map(LocationQueryResult::Subcomponent)
184 } else {
185 self.segment_n(&query.segment, segment_index)
186 .and_then(|s| s.field(field))
187 .and_then(|f| f.repeat(repeat))
188 .and_then(|r| r.component(component))
189 .map(LocationQueryResult::Component)
190 }
191 } else if query.repeat.is_some() {
192 self.segment_n(&query.segment, segment_index)
193 .and_then(|s| s.field(field))
194 .and_then(|f| f.repeat(repeat))
195 .map(LocationQueryResult::Repeat)
196 } else {
197 self.segment_n(&query.segment, segment_index)
198 .and_then(|s| s.field(field))
199 .map(LocationQueryResult::Field)
200 }
201 } else {
202 self.segment_n(&query.segment, segment_index)
203 .map(LocationQueryResult::Segment)
204 }
205 }
206}