rusthl7/
separators.rs

1use super::*;
2use std::fmt::Display;
3use std::str::FromStr;
4
5/// A helper struct to store the separator (delimiter) characters used to parse this message.
6/// Note that HL7 allows each _message_ to define it's own separators, although most messages
7/// use a default set (available from `Separators::default()`)
8#[derive(Debug, PartialEq, Clone, Copy)]
9pub struct Separators {
10    /// constant value, spec fixed to '\r' (ASCII 13, 0x0D)
11    pub segment: char,
12    pub field: char,
13    pub repeat: char,
14    pub component: char,
15    pub subcomponent: char,
16
17    pub escape_char: char,
18}
19
20impl Separators {
21    /// Create a Separator with the default (most common) HL7 values
22    pub fn default() -> Separators {
23        Separators {
24            segment: '\r',
25            field: '|',
26            repeat: '~',
27            component: '^',
28            subcomponent: '&',
29            escape_char: '\\',
30        }
31    }
32
33    // Create a Separators with the values provided in the message.
34    // This assumes the message starts with `MSH|^~\&|` or equiv for custom Separators
35    fn new(message: &str) -> Result<Separators, Hl7ParseError> {
36        //assuming we have a valid message
37        let mut chars = message.char_indices();
38
39        if Some((0, 'M')) != chars.next()
40            || Some((1, 'S')) != chars.next()
41            || Some((2, 'H')) != chars.next()
42        {
43            return Err(Hl7ParseError::Msh1Msh2(
44                "Message doesn't start with 'MSH'".to_string(),
45            ));
46        }
47
48        Ok(Separators {
49            segment: '\r',
50            field: chars.next().unwrap().1,
51            component: chars.next().unwrap().1,
52            repeat: chars.next().unwrap().1,
53            escape_char: chars.next().unwrap().1,
54            subcomponent: chars.next().unwrap().1,
55        })
56    }
57}
58
59impl Display for Separators {
60    /// Required for to_string() and other formatter consumers
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        write!(
63            f,
64            "{}{}{}{}",
65            self.component, self.repeat, self.escape_char, self.subcomponent
66        )
67    }
68}
69
70/// Expects to receive a full message (or at least a MSH segment) in order to parse
71/// out the separator chars.
72impl FromStr for Separators {
73    type Err = Hl7ParseError;
74
75    fn from_str(input: &str) -> Result<Self, Self::Err> {
76        Separators::new(input)
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::separators::Separators;
83    use super::*;
84
85    #[test]
86    fn ensure_separators_load_correctly() -> Result<(), Hl7ParseError> {
87        let expected = Separators::default();
88        let actual = Separators::new("MSH|^~\\&|CATH|StJohn|AcmeHIS|StJohn|20061019172719||ACK^O01|MSGID12349876|P|2.3\rMSA|AA|MSGID12349876")?;
89
90        assert_eq!(expected.component, actual.component);
91        assert_eq!(expected.escape_char, actual.escape_char);
92        assert_eq!(expected.field, actual.field);
93        assert_eq!(expected.repeat, actual.repeat);
94        assert_eq!(expected.segment, actual.segment);
95        assert_eq!(expected.subcomponent, actual.subcomponent);
96
97        Ok(())
98    }
99
100    #[test]
101    fn ensure_separators_load_from_string() -> Result<(), Hl7ParseError> {
102        let expected = Separators::default();
103        let actual = str::parse::<Separators>("MSH|^~\\&|CATH|StJohn|AcmeHIS|StJohn|20061019172719||ACK^O01|MSGID12349876|P|2.3\rMSA|AA|MSGID12349876")?;
104
105        assert_eq!(expected.component, actual.component);
106        assert_eq!(expected.escape_char, actual.escape_char);
107        assert_eq!(expected.field, actual.field);
108        assert_eq!(expected.repeat, actual.repeat);
109        assert_eq!(expected.segment, actual.segment);
110        assert_eq!(expected.subcomponent, actual.subcomponent);
111
112        Ok(())
113    }
114
115    #[test]
116    fn ensure_missing_msh_causes_error() {
117        //note the missing M
118        let result = Separators::new("SH|^~\\&|CATH|StJohn|AcmeHIS|StJohn|20061019172719||ACK^O01|MSGID12349876|P|2.3\rMSA|AA|MSGID12349876");
119        assert!(result.is_err());
120    }
121
122    #[test]
123    fn ensure_separators_to_string() {
124        assert_eq!("^~\\&", Separators::default().to_string());
125    }
126}