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}