hl7_parser/message/
separators.rs

1use std::fmt::Display;
2
3/// Separators used in HL7 messages
4#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct Separators {
7    pub field: char,
8    pub component: char,
9    pub subcomponent: char,
10    pub repetition: char,
11    pub escape: char,
12    /// Not a separator, but a flag to indicate that newlines should be leniently parsed
13    /// as part of the message. Enabling this flag will allow `\n` and `\r\n` to be treated
14    /// the same as `\r` as the separator for segments.
15    pub lenient_newlines: bool,
16}
17
18impl Default for Separators {
19    /// Default separators for HL7 messages
20    ///
21    /// # Examples
22    ///
23    /// ```
24    /// use hl7_parser::message::Separators;
25    /// let separators = Separators::default();
26    /// assert_eq!(separators.field, '|');
27    /// assert_eq!(separators.component, '^');
28    /// assert_eq!(separators.subcomponent, '&');
29    /// assert_eq!(separators.repetition, '~');
30    /// assert_eq!(separators.escape, '\\');
31    /// ```
32    fn default() -> Self {
33        Separators {
34            field: '|',
35            component: '^',
36            subcomponent: '&',
37            repetition: '~',
38            escape: '\\',
39            lenient_newlines: false,
40        }
41    }
42}
43
44impl Separators {
45    /// Encode a string that has separators into a string that escapes the separators
46    /// with the escape characters
47    ///
48    /// # Examples
49    ///
50    /// ```
51    /// use hl7_parser::message::Separators;
52    /// let separators = Separators::default();
53    /// let input = "foo|bar^baz&quux~quuz\\corge\rquack\nduck";
54    /// let expected = r"foo\F\bar\S\baz\T\quux\R\quuz\E\corge\X0D\quack\X0A\duck";
55    /// let actual = separators.encode(input).to_string();
56    /// assert_eq!(expected, actual);
57    /// ```
58    pub fn encode<'m>(&'m self, value: &'m str) -> EncodedSeparatorsDisplay<'m> {
59        EncodedSeparatorsDisplay {
60            separators: self,
61            value,
62        }
63    }
64
65    /// Decode a string that has separators encoding values
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use hl7_parser::message::Separators;
71    /// let separators = Separators::default();
72    /// let input = r"foo\F\bar\S\baz\T\quux\R\quuz\E\corge\X0D\quack\X0A\duck\.br\";
73    /// let expected = "foo|bar^baz&quux~quuz\\corge\rquack\nduck\r";
74    /// let actual = separators.decode(input).to_string();
75    /// assert_eq!(expected, actual);
76    /// ```
77    pub fn decode<'m>(&'m self, value: &'m str) -> DecodedSeparatorsDisplay<'m> {
78        DecodedSeparatorsDisplay {
79            separators: self,
80            value,
81        }
82    }
83
84    /// Allow lenient newlines in the message. This will allow `\n` and `\r\n` to be treated
85    /// the same as `\r` as the separator for segments.
86    pub fn with_lenient_newlines(&mut self, lenient_newlines: bool) -> Self {
87        self.lenient_newlines = lenient_newlines;
88        *self
89    }
90}
91
92impl Display for Separators {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        write!(
95            f,
96            "{}{}{}{}{}",
97            self.field, self.component, self.repetition, self.escape, self.subcomponent
98        )
99    }
100}
101
102/// A display implementation which encodes the separators in the value. (i.e. replaces them with
103/// escape sequences)
104pub struct EncodedSeparatorsDisplay<'m> {
105    pub(crate) separators: &'m Separators,
106    pub(crate) value: &'m str,
107}
108
109impl Display for EncodedSeparatorsDisplay<'_> {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        for c in self.value.chars() {
112            if c == '\r' {
113                write!(f, "{escape}X0D{escape}", escape = self.separators.escape)?;
114            } else if c == '\n' {
115                write!(f, "{escape}X0A{escape}", escape = self.separators.escape)?;
116            } else if c == self.separators.field {
117                write!(f, "{escape}F{escape}", escape = self.separators.escape)?;
118            } else if c == self.separators.repetition {
119                write!(f, "{escape}R{escape}", escape = self.separators.escape)?;
120            } else if c == self.separators.component {
121                write!(f, "{escape}S{escape}", escape = self.separators.escape)?;
122            } else if c == self.separators.subcomponent {
123                write!(f, "{escape}T{escape}", escape = self.separators.escape)?;
124            } else if c == self.separators.escape {
125                write!(f, "{escape}E{escape}", escape = self.separators.escape)?;
126            } else {
127                write!(f, "{}", c)?;
128            }
129        }
130        Ok(())
131    }
132}
133
134/// A display implementation which decodes the escape sequences in the value using the separators.
135pub struct DecodedSeparatorsDisplay<'m> {
136    pub(crate) separators: &'m Separators,
137    pub(crate) value: &'m str,
138}
139
140impl Display for DecodedSeparatorsDisplay<'_> {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        let mut escaped = false;
143        let mut escape_i: usize = 0;
144        for (i, c) in self.value.chars().enumerate() {
145            if c == self.separators.escape {
146                if escaped {
147                    escaped = false;
148                    match &self.value[escape_i..i] {
149                        "F" => write!(f, "{}", self.separators.field)?,
150                        "R" => write!(f, "{}", self.separators.repetition)?,
151                        "S" => write!(f, "{}", self.separators.component)?,
152                        "T" => write!(f, "{}", self.separators.subcomponent)?,
153                        "E" => write!(f, "{}", self.separators.escape)?,
154                        "X0A" => writeln!(f)?,
155                        "X0D" | ".br" => write!(f, "\r")?,
156                        v => write!(f, "{v}")?,
157                    }
158                } else {
159                    escape_i = i + 1;
160                    escaped = true;
161                }
162            } else if !escaped {
163                write!(f, "{}", c)?;
164            }
165        }
166        Ok(())
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173    use pretty_assertions_sorted::assert_eq;
174
175    #[test]
176    fn separators_can_encode() {
177        let separators = Separators::default();
178
179        let input = "foo|bar^baz&quux~quuz\\corge\rquack\nduck";
180        let expected = r"foo\F\bar\S\baz\T\quux\R\quuz\E\corge\X0D\quack\X0A\duck";
181        let actual = separators.encode(input).to_string();
182        assert_eq!(expected, actual);
183    }
184
185    #[test]
186    fn sample_encode() {
187        let separators = Separators::default();
188
189        let input = "Pierre DuRho^ne & Cie";
190        let expected = r"Pierre DuRho\S\ne \T\ Cie";
191        let actual = separators.encode(input).to_string();
192        assert_eq!(expected, actual);
193    }
194
195    #[test]
196    fn separators_can_decode() {
197        let separators = Separators::default();
198
199        let input = r"foo\F\bar\S\baz\T\quux\R\quuz\E\corge\X0D\quack\X0A\duck\.br\";
200        let expected = "foo|bar^baz&quux~quuz\\corge\rquack\nduck\r";
201        let actual = separators.decode(input).to_string();
202        assert_eq!(expected, actual);
203    }
204}