haste_hl7v2/
parser.rs

1use haste_fhir_model::r4::generated::terminology::IssueType;
2use haste_fhir_operation_error::OperationOutcomeError;
3
4#[derive(Debug, PartialEq, Eq, Clone)]
5pub struct Component {
6    pub value: Option<String>,
7    pub subcomponents: Option<Vec<String>>,
8}
9
10#[derive(Debug, PartialEq, Eq, Clone)]
11pub struct FieldValue {
12    pub value: Option<Component>,
13    pub components: Option<Vec<Component>>,
14}
15
16#[derive(Debug, PartialEq, Eq, Clone)]
17pub struct Field {
18    pub value: Option<FieldValue>,
19    pub repetitions: Option<Vec<FieldValue>>,
20}
21
22#[derive(Debug, PartialEq, Eq, Clone)]
23pub struct Segment {
24    pub id: String,
25    pub fields: Vec<Field>,
26}
27
28#[derive(Debug, PartialEq, Eq, Clone)]
29pub struct MessageHeader {
30    field_separator: char,
31    /// component separator, repetition separator, escape character, and subcomponent separator
32    encoding_characters: String,
33    sending_application: Option<String>,
34    sending_facility: Option<String>,
35    receiving_application: Option<String>,
36    receiving_facility: Option<String>,
37    datetime_of_message: Option<String>,
38    security: Option<String>,
39    message_type: Option<String>,
40    message_control_id: Option<String>,
41    processing_id: Option<String>,
42    version_id: Option<String>,
43
44    additional_fields: Vec<Field>,
45}
46
47#[derive(Debug, PartialEq, Eq, Clone)]
48pub struct Hl7V2Message {
49    pub header: MessageHeader,
50    pub segments: Vec<Segment>,
51}
52
53impl TryFrom<&str> for MessageHeader {
54    type Error = OperationOutcomeError;
55
56    fn try_from(value: &str) -> Result<Self, Self::Error> {
57        let h = value.get(..3);
58        if h != Some("MSH") {
59            return Err(OperationOutcomeError::error(
60                IssueType::Exception(None),
61                "Not an MSH segment".to_string(),
62            ));
63        }
64        let field_separator = value.chars().nth(3).ok_or_else(|| {
65            OperationOutcomeError::error(
66                IssueType::Exception(None),
67                "Missing field separator".to_string(),
68            )
69        })?;
70
71        let mut fields = value[4..].split(field_separator);
72
73        Ok(MessageHeader {
74            field_separator: field_separator,
75            encoding_characters: fields
76                .next()
77                .ok_or_else(|| {
78                    OperationOutcomeError::error(
79                        IssueType::Exception(None),
80                        "Missing encoding characters".to_string(),
81                    )
82                })?
83                .to_string(),
84            sending_application: fields.next().map(|s| s.to_string()),
85            sending_facility: fields.next().map(|s| s.to_string()),
86            receiving_application: fields.next().map(|s| s.to_string()),
87            receiving_facility: fields.next().map(|s| s.to_string()),
88            datetime_of_message: (fields.next().map(|s| s.to_string())),
89            security: fields.next().map(|s| s.to_string()),
90            message_type: fields.next().map(|s| s.to_string()),
91            message_control_id: fields.next().map(|s| s.to_string()),
92            processing_id: fields.next().map(|s| s.to_string()),
93            version_id: fields.next().map(|s| s.to_string()),
94            additional_fields: fields
95                .map(|f| Field {
96                    value: Some(FieldValue {
97                        value: Some(Component {
98                            value: Some(f.to_string()),
99                            subcomponents: None,
100                        }),
101                        components: None,
102                    }),
103                    repetitions: None,
104                })
105                .collect(),
106        })
107    }
108}
109
110impl TryFrom<&str> for Hl7V2Message {
111    type Error = OperationOutcomeError;
112
113    fn try_from(value: &str) -> Result<Self, Self::Error> {
114        let mut segments = vec![];
115        let mut segment_lines = value.lines();
116
117        let message_header = MessageHeader::try_from(segment_lines.next().ok_or_else(|| {
118            OperationOutcomeError::error(
119                IssueType::Invalid(None),
120                "Missing MSH segment".to_string(),
121            )
122        })?)?;
123
124        for segment in segment_lines {
125            let mut segment = segment.split(message_header.field_separator);
126            let segment_id = segment.next().ok_or_else(|| {
127                OperationOutcomeError::error(
128                    IssueType::Exception(None),
129                    "Missing segment ID".to_string(),
130                )
131            })?;
132
133            let segment_fields = segment.map(|field| {
134                let fields = field
135                    .split(
136                        message_header
137                            .encoding_characters
138                            .chars()
139                            .nth(1)
140                            .unwrap_or('~'),
141                    )
142                    .map(|field| {
143                        let components = field
144                            .split(
145                                message_header
146                                    .encoding_characters
147                                    .chars()
148                                    .nth(0)
149                                    .unwrap_or('^'),
150                            )
151                            .map(|component| {
152                                let subcomponent = field
153                                    .split(
154                                        message_header
155                                            .encoding_characters
156                                            .chars()
157                                            .nth(3)
158                                            .unwrap_or('&'),
159                                    )
160                                    .collect::<Vec<_>>();
161                                if subcomponent.len() > 1 {
162                                    Component {
163                                        value: None,
164                                        subcomponents: Some(
165                                            subcomponent.iter().map(|s| s.to_string()).collect(),
166                                        ),
167                                    }
168                                } else {
169                                    Component {
170                                        value: Some(component.to_string()),
171                                        subcomponents: None,
172                                    }
173                                }
174                            })
175                            .collect::<Vec<_>>();
176                        if components.len() > 1 {
177                            FieldValue {
178                                value: None,
179                                components: Some(components),
180                            }
181                        } else {
182                            FieldValue {
183                                value: Some(components.into_iter().next().unwrap()),
184                                components: None,
185                            }
186                        }
187                    })
188                    .collect::<Vec<_>>();
189                if fields.len() > 1 {
190                    Field {
191                        value: None,
192                        repetitions: Some(fields),
193                    }
194                } else {
195                    Field {
196                        value: Some(fields.into_iter().next().unwrap()),
197                        repetitions: None,
198                    }
199                }
200            });
201
202            segments.push(Segment {
203                id: segment_id.to_string(),
204                fields: segment_fields.collect(),
205            });
206        }
207
208        Ok(Hl7V2Message {
209            header: message_header,
210            segments,
211        })
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218
219    #[test]
220    fn test_parse_hl7v2_message() {
221        let input = std::fs::read_to_string("./test_data/message1.bin").unwrap();
222
223        let result = Hl7V2Message::try_from(input.as_str());
224        println!("{:#?}", result);
225        assert!(result.is_ok());
226
227        let message = result.unwrap();
228        assert_eq!(message.segments.len(), 7);
229
230        assert_eq!(message.segments[0].id, "SCH");
231        assert_eq!(message.segments[0].fields.len(), 25);
232        assert_eq!(
233            message.segments[0].fields[0]
234                .value
235                .clone()
236                .unwrap()
237                .components,
238            Some(vec![
239                Component {
240                    value: Some("10345".to_string()),
241                    subcomponents: None,
242                },
243                Component {
244                    value: Some("10345".to_string()),
245                    subcomponents: None,
246                },
247            ])
248        );
249
250        assert_eq!(message.segments[1].id, "PID");
251        assert_eq!(message.segments[2].id, "PV1");
252        assert_eq!(message.segments[3].id, "RGS");
253        assert_eq!(message.segments[4].id, "AIG");
254        assert_eq!(message.segments[5].id, "AIL");
255        assert_eq!(message.segments[6].id, "AIP");
256    }
257}