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(),
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(),
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(),
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(),
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| parse_empty_string(s.to_string()))
83                                                .collect(),
84                                        ),
85                                    }
86                                } else {
87                                    HL7V2SegmentsFieldsValueValue {
88                                        value: Some(Box::new(parse_empty_string(
89                                            component.to_string(),
90                                        ))),
91                                        subcomponents: None,
92                                    }
93                                }
94                            })
95                            .collect::<Vec<_>>();
96                        if components.len() > 1 {
97                            HL7V2SegmentsFieldsValue {
98                                value: None,
99                                components: Some(components),
100                            }
101                        } else {
102                            HL7V2SegmentsFieldsValue {
103                                value: components.into_iter().next(),
104                                components: None,
105                            }
106                        }
107                    })
108                    .collect::<Vec<_>>();
109                if fields.len() > 1 {
110                    HL7V2SegmentsFields {
111                        value: None,
112                        repetitions: Some(fields),
113                    }
114                } else {
115                    HL7V2SegmentsFields {
116                        value: Some(fields.into_iter().next().unwrap()),
117                        repetitions: None,
118                    }
119                }
120            });
121
122            segments.push(HL7V2Segments {
123                id: Box::new(parse_empty_id(segment_id.to_string())),
124                fields: Some(segment_fields.collect()),
125            });
126        }
127
128        Ok(ParsedHL7V2Message(HL7V2 {
129            fieldSeparator: Box::new(parse_empty_string(field_seperator.to_string())),
130            segments: Some(segments),
131            ..Default::default()
132        }))
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use crate::serialize::SerializeMessage;
139
140    use super::*;
141
142    #[test]
143    fn test_parse_hl7v2_message() {
144        let input = std::fs::read_to_string("./test_data/message1.bin").unwrap();
145
146        let result = ParsedHL7V2Message::try_from(input.as_str());
147
148        assert!(result.is_ok());
149
150        let message = result.unwrap().0;
151        let segments = message.segments.expect("message should contain segments");
152        assert_eq!(segments.len(), 8);
153
154        assert_eq!(segments[1].id.value.as_deref(), Some("SCH"));
155
156        let sch_fields = segments[1]
157            .fields
158            .clone()
159            .expect("SCH should contain fields");
160        assert_eq!(sch_fields.len(), 25);
161        assert_eq!(
162            sch_fields[0]
163                .value
164                .clone()
165                .unwrap()
166                .components
167                .unwrap()
168                .into_iter()
169                .map(|c| c.value.unwrap().value.unwrap())
170                .collect::<Vec<_>>(),
171            vec!["10345".to_string(), "10345".to_string()]
172        );
173
174        assert_eq!(segments[2].id.value.as_deref(), Some("PID"));
175        assert_eq!(segments[3].id.value.as_deref(), Some("PV1"));
176        assert_eq!(segments[4].id.value.as_deref(), Some("RGS"));
177        assert_eq!(segments[5].id.value.as_deref(), Some("AIG"));
178        assert_eq!(segments[6].id.value.as_deref(), Some("AIL"));
179        assert_eq!(segments[7].id.value.as_deref(), Some("AIP"));
180    }
181
182    #[test]
183    fn round_trip() {
184        let input = std::fs::read_to_string("./test_data/message1.bin").unwrap();
185        let result = ParsedHL7V2Message::try_from(input.as_str());
186        assert!(result.is_ok());
187
188        let message = result.unwrap();
189        let serialized: String = (SerializeMessage(&message.0)).into();
190
191        pretty_assertions::assert_eq!(serialized, input);
192    }
193
194    #[test]
195    fn round_trip_json_serialize() {
196        let input = std::fs::read_to_string("./test_data/message1.bin").unwrap();
197        let result = ParsedHL7V2Message::try_from(input.as_str());
198        assert!(result.is_ok());
199
200        let message = result.unwrap();
201        let json = serde_json::to_string_pretty(&message.0).unwrap();
202        println!("{json}");
203        let deserialized: HL7V2 = serde_json::from_str(&json).unwrap();
204        let serialized: String = (SerializeMessage(&deserialized)).into();
205
206        pretty_assertions::assert_eq!(serialized, input);
207    }
208}