Skip to main content

haste_hl7v2/
parser.rs

1use haste_fhir_model::r4::generated::resources::{
2    HL7V2, HL7V2Segments, HL7V2SegmentsFields, HL7V2SegmentsFieldsValue,
3    HL7V2SegmentsFieldsValueValue,
4};
5use haste_fhir_model::r4::generated::terminology::IssueType;
6use haste_fhir_model::r4::generated::types::{FHIRId, FHIRString};
7use haste_fhir_operation_error::OperationOutcomeError;
8
9#[derive(Debug, Clone)]
10pub struct ParsedHL7V2Message(pub HL7V2);
11
12fn parse_empty_string(v: String) -> FHIRString {
13    let mut fhir_string: FHIRString = v.into();
14
15    fhir_string.id = Some("_non_empty".to_string());
16    fhir_string
17}
18
19fn parse_empty_id(v: String) -> FHIRId {
20    let mut fhir_id: FHIRId = v.into();
21
22    fhir_id.id = Some("_non_empty".to_string());
23    fhir_id
24}
25
26impl TryFrom<&str> for ParsedHL7V2Message {
27    type Error = OperationOutcomeError;
28
29    fn try_from(value: &str) -> Result<Self, Self::Error> {
30        let mut segments = vec![];
31
32        let segment_lines = value.split(['\r', '\n']).filter(|s| !s.is_empty());
33
34        let header = value[..3].to_string();
35
36        if header != "MSH" {
37            return Err(OperationOutcomeError::error(
38                IssueType::Exception(None),
39                "Message does not start with MSH segment".to_string(),
40            ));
41        }
42
43        let field_seperator = value.chars().nth(3).ok_or_else(|| {
44            OperationOutcomeError::error(
45                IssueType::Exception(None),
46                "Missing field separator".to_string(),
47            )
48        })?;
49
50        let encoding_characters = value[4..].split(field_seperator).next().ok_or_else(|| {
51            OperationOutcomeError::error(
52                IssueType::Exception(None),
53                "Missing encoding characters".to_string(),
54            )
55        })?;
56
57        for segment in segment_lines {
58            let mut segment = segment.split(field_seperator);
59            let segment_id = segment.next().ok_or_else(|| {
60                OperationOutcomeError::error(
61                    IssueType::Exception(None),
62                    "Missing segment ID".to_string(),
63                )
64            })?;
65
66            let segment_fields = segment.map(|field| {
67                let fields = field
68                    .split(encoding_characters.chars().nth(1).unwrap_or('~'))
69                    .map(|field| {
70                        let components = field
71                            .split(encoding_characters.chars().nth(0).unwrap_or('^'))
72                            .map(|component| {
73                                let subcomponent = component
74                                    .split(encoding_characters.chars().nth(3).unwrap_or('&'))
75                                    .collect::<Vec<_>>();
76                                if subcomponent.len() > 1 {
77                                    HL7V2SegmentsFieldsValueValue {
78                                        value: None,
79                                        subcomponents: Some(
80                                            subcomponent
81                                                .iter()
82                                                .map(|s| {
83                                                    Box::new(parse_empty_string(s.to_string()))
84                                                })
85                                                .collect(),
86                                        ),
87                                    }
88                                } else {
89                                    HL7V2SegmentsFieldsValueValue {
90                                        value: Some(Box::new(parse_empty_string(
91                                            component.to_string(),
92                                        ))),
93                                        subcomponents: None,
94                                    }
95                                }
96                            })
97                            .collect::<Vec<_>>();
98                        if components.len() > 1 {
99                            HL7V2SegmentsFieldsValue {
100                                value: None,
101                                components: Some(components),
102                            }
103                        } else {
104                            HL7V2SegmentsFieldsValue {
105                                value: components.into_iter().next(),
106                                components: None,
107                            }
108                        }
109                    })
110                    .collect::<Vec<_>>();
111                if fields.len() > 1 {
112                    HL7V2SegmentsFields {
113                        value: None,
114                        repetitions: Some(fields),
115                    }
116                } else {
117                    HL7V2SegmentsFields {
118                        value: Some(fields.into_iter().next().unwrap()),
119                        repetitions: None,
120                    }
121                }
122            });
123
124            segments.push(HL7V2Segments {
125                id: Box::new(parse_empty_id(segment_id.to_string())),
126                fields: Some(segment_fields.collect()),
127            });
128        }
129
130        Ok(ParsedHL7V2Message(HL7V2 {
131            fieldSeparator: Box::new(parse_empty_string(field_seperator.to_string())),
132            segments: Some(segments),
133            ..Default::default()
134        }))
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use crate::serialize::SerializeMessage;
141
142    use super::*;
143
144    #[test]
145    fn test_parse_hl7v2_message() {
146        let input = std::fs::read_to_string("./test_data/message1.bin").unwrap();
147
148        let result = ParsedHL7V2Message::try_from(input.as_str());
149
150        assert!(result.is_ok());
151
152        let message = result.unwrap().0;
153        let segments = message.segments.expect("message should contain segments");
154        assert_eq!(segments.len(), 8);
155
156        assert_eq!(segments[1].id.value.as_deref(), Some("SCH"));
157
158        let sch_fields = segments[1]
159            .fields
160            .clone()
161            .expect("SCH should contain fields");
162        assert_eq!(sch_fields.len(), 25);
163        assert_eq!(
164            sch_fields[0]
165                .value
166                .clone()
167                .unwrap()
168                .components
169                .unwrap()
170                .into_iter()
171                .map(|c| c.value.unwrap().value.unwrap())
172                .collect::<Vec<_>>(),
173            vec!["10345".to_string(), "10345".to_string()]
174        );
175
176        assert_eq!(segments[2].id.value.as_deref(), Some("PID"));
177        assert_eq!(segments[3].id.value.as_deref(), Some("PV1"));
178        assert_eq!(segments[4].id.value.as_deref(), Some("RGS"));
179        assert_eq!(segments[5].id.value.as_deref(), Some("AIG"));
180        assert_eq!(segments[6].id.value.as_deref(), Some("AIL"));
181        assert_eq!(segments[7].id.value.as_deref(), Some("AIP"));
182    }
183
184    #[test]
185    fn round_trip() {
186        let input = std::fs::read_to_string("./test_data/message1.bin").unwrap();
187        let result = ParsedHL7V2Message::try_from(input.as_str());
188        assert!(result.is_ok());
189
190        let message = result.unwrap();
191        let serialized: String = (SerializeMessage(&message.0)).into();
192
193        pretty_assertions::assert_eq!(serialized, input);
194    }
195
196    #[test]
197    fn round_trip_json_serialize() {
198        let input = std::fs::read_to_string("./test_data/message1.bin").unwrap();
199        let result = ParsedHL7V2Message::try_from(input.as_str());
200        assert!(result.is_ok());
201
202        let message = result.unwrap();
203        let json = serde_json::to_string_pretty(&message.0).unwrap();
204        println!("{}", json);
205        let deserialized: HL7V2 = serde_json::from_str(&json).unwrap();
206        let serialized: String = (SerializeMessage(&deserialized)).into();
207
208        pretty_assertions::assert_eq!(serialized, input);
209    }
210}