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}