1use haste_fhir_model::r4::generated::terminology::IssueType;
2use haste_fhir_operation_error::OperationOutcomeError;
3
4#[derive(Debug, PartialEq, Eq, Clone)]
5pub struct Component {
6 pub value: Option<String>,
7 pub subcomponents: Option<Vec<String>>,
8}
9
10#[derive(Debug, PartialEq, Eq, Clone)]
11pub struct FieldValue {
12 pub value: Option<Component>,
13 pub components: Option<Vec<Component>>,
14}
15
16#[derive(Debug, PartialEq, Eq, Clone)]
17pub struct Field {
18 pub value: Option<FieldValue>,
19 pub repetitions: Option<Vec<FieldValue>>,
20}
21
22#[derive(Debug, PartialEq, Eq, Clone)]
23pub struct Segment {
24 pub id: String,
25 pub fields: Vec<Field>,
26}
27
28#[derive(Debug, PartialEq, Eq, Clone)]
29pub struct MessageHeader {
30 field_separator: char,
31 encoding_characters: String,
33 sending_application: Option<String>,
34 sending_facility: Option<String>,
35 receiving_application: Option<String>,
36 receiving_facility: Option<String>,
37 datetime_of_message: Option<String>,
38 security: Option<String>,
39 message_type: Option<String>,
40 message_control_id: Option<String>,
41 processing_id: Option<String>,
42 version_id: Option<String>,
43
44 additional_fields: Vec<Field>,
45}
46
47#[derive(Debug, PartialEq, Eq, Clone)]
48pub struct Hl7V2Message {
49 pub header: MessageHeader,
50 pub segments: Vec<Segment>,
51}
52
53impl TryFrom<&str> for MessageHeader {
54 type Error = OperationOutcomeError;
55
56 fn try_from(value: &str) -> Result<Self, Self::Error> {
57 let h = value.get(..3);
58 if h != Some("MSH") {
59 return Err(OperationOutcomeError::error(
60 IssueType::Exception(None),
61 "Not an MSH segment".to_string(),
62 ));
63 }
64 let field_separator = value.chars().nth(3).ok_or_else(|| {
65 OperationOutcomeError::error(
66 IssueType::Exception(None),
67 "Missing field separator".to_string(),
68 )
69 })?;
70
71 let mut fields = value[4..].split(field_separator);
72
73 Ok(MessageHeader {
74 field_separator: field_separator,
75 encoding_characters: fields
76 .next()
77 .ok_or_else(|| {
78 OperationOutcomeError::error(
79 IssueType::Exception(None),
80 "Missing encoding characters".to_string(),
81 )
82 })?
83 .to_string(),
84 sending_application: fields.next().map(|s| s.to_string()),
85 sending_facility: fields.next().map(|s| s.to_string()),
86 receiving_application: fields.next().map(|s| s.to_string()),
87 receiving_facility: fields.next().map(|s| s.to_string()),
88 datetime_of_message: (fields.next().map(|s| s.to_string())),
89 security: fields.next().map(|s| s.to_string()),
90 message_type: fields.next().map(|s| s.to_string()),
91 message_control_id: fields.next().map(|s| s.to_string()),
92 processing_id: fields.next().map(|s| s.to_string()),
93 version_id: fields.next().map(|s| s.to_string()),
94 additional_fields: fields
95 .map(|f| Field {
96 value: Some(FieldValue {
97 value: Some(Component {
98 value: Some(f.to_string()),
99 subcomponents: None,
100 }),
101 components: None,
102 }),
103 repetitions: None,
104 })
105 .collect(),
106 })
107 }
108}
109
110impl TryFrom<&str> for Hl7V2Message {
111 type Error = OperationOutcomeError;
112
113 fn try_from(value: &str) -> Result<Self, Self::Error> {
114 let mut segments = vec![];
115 let mut segment_lines = value.lines();
116
117 let message_header = MessageHeader::try_from(segment_lines.next().ok_or_else(|| {
118 OperationOutcomeError::error(
119 IssueType::Invalid(None),
120 "Missing MSH segment".to_string(),
121 )
122 })?)?;
123
124 for segment in segment_lines {
125 let mut segment = segment.split(message_header.field_separator);
126 let segment_id = segment.next().ok_or_else(|| {
127 OperationOutcomeError::error(
128 IssueType::Exception(None),
129 "Missing segment ID".to_string(),
130 )
131 })?;
132
133 let segment_fields = segment.map(|field| {
134 let fields = field
135 .split(
136 message_header
137 .encoding_characters
138 .chars()
139 .nth(1)
140 .unwrap_or('~'),
141 )
142 .map(|field| {
143 let components = field
144 .split(
145 message_header
146 .encoding_characters
147 .chars()
148 .nth(0)
149 .unwrap_or('^'),
150 )
151 .map(|component| {
152 let subcomponent = field
153 .split(
154 message_header
155 .encoding_characters
156 .chars()
157 .nth(3)
158 .unwrap_or('&'),
159 )
160 .collect::<Vec<_>>();
161 if subcomponent.len() > 1 {
162 Component {
163 value: None,
164 subcomponents: Some(
165 subcomponent.iter().map(|s| s.to_string()).collect(),
166 ),
167 }
168 } else {
169 Component {
170 value: Some(component.to_string()),
171 subcomponents: None,
172 }
173 }
174 })
175 .collect::<Vec<_>>();
176 if components.len() > 1 {
177 FieldValue {
178 value: None,
179 components: Some(components),
180 }
181 } else {
182 FieldValue {
183 value: Some(components.into_iter().next().unwrap()),
184 components: None,
185 }
186 }
187 })
188 .collect::<Vec<_>>();
189 if fields.len() > 1 {
190 Field {
191 value: None,
192 repetitions: Some(fields),
193 }
194 } else {
195 Field {
196 value: Some(fields.into_iter().next().unwrap()),
197 repetitions: None,
198 }
199 }
200 });
201
202 segments.push(Segment {
203 id: segment_id.to_string(),
204 fields: segment_fields.collect(),
205 });
206 }
207
208 Ok(Hl7V2Message {
209 header: message_header,
210 segments,
211 })
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn test_parse_hl7v2_message() {
221 let input = std::fs::read_to_string("./test_data/message1.bin").unwrap();
222
223 let result = Hl7V2Message::try_from(input.as_str());
224 println!("{:#?}", result);
225 assert!(result.is_ok());
226
227 let message = result.unwrap();
228 assert_eq!(message.segments.len(), 7);
229
230 assert_eq!(message.segments[0].id, "SCH");
231 assert_eq!(message.segments[0].fields.len(), 25);
232 assert_eq!(
233 message.segments[0].fields[0]
234 .value
235 .clone()
236 .unwrap()
237 .components,
238 Some(vec![
239 Component {
240 value: Some("10345".to_string()),
241 subcomponents: None,
242 },
243 Component {
244 value: Some("10345".to_string()),
245 subcomponents: None,
246 },
247 ])
248 );
249
250 assert_eq!(message.segments[1].id, "PID");
251 assert_eq!(message.segments[2].id, "PV1");
252 assert_eq!(message.segments[3].id, "RGS");
253 assert_eq!(message.segments[4].id, "AIG");
254 assert_eq!(message.segments[5].id, "AIL");
255 assert_eq!(message.segments[6].id, "AIP");
256 }
257}