1use std::fmt;
4use std::ops::{Deref, DerefMut};
5
6use serde::de::{self, MapAccess, Visitor};
7use serde::ser::SerializeStruct;
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9
10use crate::{Segment, Separators};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct Message(pub er7::Message);
61
62impl Message {
63 pub fn parse(text: &str) -> Result<Message, er7::Error> {
87 er7::parse(text).map(Message)
88 }
89}
90
91impl From<er7::Message> for Message {
92 fn from(inner: er7::Message) -> Message {
93 Message(inner)
94 }
95}
96
97impl From<Message> for er7::Message {
98 fn from(outer: Message) -> er7::Message {
99 outer.0
100 }
101}
102
103impl Deref for Message {
104 type Target = er7::Message;
105
106 fn deref(&self) -> &er7::Message {
107 &self.0
108 }
109}
110
111impl DerefMut for Message {
112 fn deref_mut(&mut self) -> &mut er7::Message {
113 &mut self.0
114 }
115}
116
117impl fmt::Display for Message {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 f.write_str(&self.0.to_er7())
121 }
122}
123
124impl Serialize for Message {
125 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
126 where
127 S: Serializer,
128 {
129 let segments: Vec<Segment> = self.0.segments.iter().map(|s| Segment(s.clone())).collect();
130 let mut state = serializer.serialize_struct("Message", 2)?;
131 state.serialize_field("separators", &Separators(self.0.separators))?;
132 state.serialize_field("segments", &segments)?;
133 state.end()
134 }
135}
136
137const FIELDS: &[&str] = &["separators", "segments"];
138
139struct MessageVisitor;
140
141impl<'de> Visitor<'de> for MessageVisitor {
142 type Value = Message;
143
144 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
145 formatter.write_str("a Message object with \"separators\" and \"segments\"")
146 }
147
148 fn visit_map<V>(self, mut map: V) -> Result<Message, V::Error>
149 where
150 V: MapAccess<'de>,
151 {
152 let mut separators: Option<Separators> = None;
153 let mut segments: Option<Vec<Segment>> = None;
154
155 while let Some(key) = map.next_key::<String>()? {
156 match key.as_str() {
157 "separators" => {
158 if separators.is_some() {
159 return Err(de::Error::duplicate_field("separators"));
160 }
161 separators = Some(map.next_value()?);
162 }
163 "segments" => {
164 if segments.is_some() {
165 return Err(de::Error::duplicate_field("segments"));
166 }
167 segments = Some(map.next_value()?);
168 }
169 _ => {
170 let _ = map.next_value::<de::IgnoredAny>()?;
171 }
172 }
173 }
174
175 let separators = separators.ok_or_else(|| de::Error::missing_field("separators"))?;
176 let segments = segments.ok_or_else(|| de::Error::missing_field("segments"))?;
177 Ok(Message(er7::Message {
178 separators: separators.0,
179 segments: segments.into_iter().map(|s| s.0).collect(),
180 }))
181 }
182}
183
184impl<'de> Deserialize<'de> for Message {
185 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
186 where
187 D: Deserializer<'de>,
188 {
189 deserializer.deserialize_struct("Message", FIELDS, MessageVisitor)
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 const ADT: &str = "MSH|^~\\&|LAB|ACME|EHR|CLINIC|20260815120000||ADT^A08^ADT_A01|MSG9|P|2.5\r\
198 PID|1||12345^^^ACME&1.2.3&ISO^MR||SMITH^JOHN^Q||19800101|M|||||\
199 555-1111~555-2222\r\
200 OBX|1|NM|2093-3^Cholesterol^LN||187|mg/dL\r\
201 OBX|2|ST|X^Note^L||\"\"";
202
203 #[test]
204 fn round_trips_a_full_message_through_json() {
205 let message = Message::parse(ADT).unwrap();
206 let json = serde_json::to_string(&message).unwrap();
207 let back: Message = serde_json::from_str(&json).unwrap();
208 assert_eq!(back.to_er7(), ADT);
209 assert_eq!(back, message);
210 }
211
212 #[test]
213 fn round_trips_custom_delimiters() {
214 let text = "MSH#*!?@#LAB#*A*B#C!D";
215 let message = Message::parse(text).unwrap();
216 let json = serde_json::to_string(&message).unwrap();
217 let back: Message = serde_json::from_str(&json).unwrap();
218 assert_eq!(back.to_er7(), text);
219 }
220
221 #[test]
222 fn round_trips_a_batch() {
223 let text = "FHS|^~\\&|SENDER\rBHS|^~\\&|SENDER\r\
224 MSH|^~\\&|SENDER||RECEIVER||20260815090000||ACK^A08^ACK|B1|P|2.5\r\
225 MSA|AA|MSG00001\rBTS|1\rFTS|1";
226 let message = Message::parse(text).unwrap();
227 let json = serde_json::to_string(&message).unwrap();
228 let back: Message = serde_json::from_str(&json).unwrap();
229 assert_eq!(back.to_er7(), text);
230 }
231
232 #[test]
233 fn deref_reaches_query() {
234 let message = Message::parse(ADT).unwrap();
235 assert_eq!(message.query("PID-5.1").unwrap().as_deref(), Some("SMITH"));
236 }
237
238 #[test]
239 fn rejects_a_message_missing_segments() {
240 let err = serde_json::from_str::<Message>(r#"{"separators":{"field":"|","component":"^","repetition":"~","escape":"\\","subcomponent":"&"}}"#)
241 .unwrap_err();
242 assert!(err.to_string().contains("segments"));
243 }
244
245 #[test]
246 fn pretty_json_keeps_the_tree_shape() {
247 let message = Message::parse("MSH|^~\\&|LAB\rPID|1||9|4|SMITH^JOHN").unwrap();
248 let json = serde_json::to_value(&message).unwrap();
249 let pid = &json["segments"][1];
250 assert_eq!(pid["name"], "PID");
251 assert_eq!(
253 pid["fields"][4][0],
254 serde_json::json!([["SMITH"], ["JOHN"]])
255 );
256 }
257}