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