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}