1use crate::Encoding;
37use std::borrow::Cow;
38
39pub fn escape<'a>(text: &'a str, enc: Encoding) -> Cow<'a, str> {
45 let special = [
46 enc.field,
47 enc.component,
48 enc.repetition,
49 enc.escape,
50 enc.subcomponent,
51 b'\r',
52 b'\n',
53 ];
54 if !text.bytes().any(|b| special.contains(&b)) {
55 return Cow::Borrowed(text);
56 }
57 let esc = enc.escape as char;
58 let mut out = String::with_capacity(text.len() + 8);
59 let mut chars = text.chars().peekable();
60 while let Some(c) = chars.next() {
61 let code = if c.is_ascii() { Some(c as u8) } else { None };
62 match code {
63 Some(b) if b == enc.escape => push_seq(&mut out, esc, "E"),
64 Some(b) if b == enc.field => push_seq(&mut out, esc, "F"),
65 Some(b) if b == enc.component => push_seq(&mut out, esc, "S"),
66 Some(b) if b == enc.repetition => push_seq(&mut out, esc, "R"),
67 Some(b) if b == enc.subcomponent => push_seq(&mut out, esc, "T"),
68 Some(b'\r') => {
69 if chars.peek() == Some(&'\n') {
70 chars.next();
71 }
72 push_seq(&mut out, esc, ".br");
73 }
74 Some(b'\n') => push_seq(&mut out, esc, ".br"),
75 _ => out.push(c),
76 }
77 }
78 Cow::Owned(out)
79}
80
81fn push_seq(out: &mut String, esc: char, seq: &str) {
82 out.push(esc);
83 out.push_str(seq);
84 out.push(esc);
85}
86
87#[derive(Debug, Clone, Default, PartialEq, Eq)]
92pub struct Value {
93 pub repetitions: Vec<Vec<Vec<String>>>,
96}
97
98impl Value {
99 pub fn text(text: impl Into<String>) -> Value {
101 Value {
102 repetitions: vec![vec![vec![text.into()]]],
103 }
104 }
105
106 pub fn components<I, S>(components: I) -> Value
108 where
109 I: IntoIterator<Item = S>,
110 S: Into<String>,
111 {
112 Value {
113 repetitions: vec![components.into_iter().map(|c| vec![c.into()]).collect()],
114 }
115 }
116
117 pub fn repetitions<I, R, S>(reps: I) -> Value
119 where
120 I: IntoIterator<Item = R>,
121 R: IntoIterator<Item = S>,
122 S: Into<String>,
123 {
124 Value {
125 repetitions: reps
126 .into_iter()
127 .map(|r| r.into_iter().map(|c| vec![c.into()]).collect())
128 .collect(),
129 }
130 }
131
132 pub fn set(&mut self, r: usize, c: usize, s: usize, text: impl Into<String>) -> &mut Value {
135 let (r, c, s) = (r.max(1) - 1, c.max(1) - 1, s.max(1) - 1);
136 if self.repetitions.len() <= r {
137 self.repetitions.resize(r + 1, Vec::new());
138 }
139 let rep = &mut self.repetitions[r];
140 if rep.len() <= c {
141 rep.resize(c + 1, Vec::new());
142 }
143 let comp = &mut rep[c];
144 if comp.len() <= s {
145 comp.resize(s + 1, String::new());
146 }
147 comp[s] = text.into();
148 self
149 }
150
151 fn render(&self, enc: Encoding) -> String {
152 let sub = (enc.subcomponent as char).to_string();
153 let comp = (enc.component as char).to_string();
154 let rep = (enc.repetition as char).to_string();
155 self.repetitions
156 .iter()
157 .map(|r| {
158 r.iter()
159 .map(|c| {
160 c.iter()
161 .map(|s| escape(s, enc))
162 .collect::<Vec<_>>()
163 .join(&sub)
164 })
165 .collect::<Vec<_>>()
166 .join(&comp)
167 })
168 .collect::<Vec<_>>()
169 .join(&rep)
170 }
171}
172
173impl From<&str> for Value {
174 fn from(s: &str) -> Value {
175 Value::text(s)
176 }
177}
178
179impl From<String> for Value {
180 fn from(s: String) -> Value {
181 Value::text(s)
182 }
183}
184
185impl From<&String> for Value {
186 fn from(s: &String) -> Value {
187 Value::text(s.clone())
188 }
189}
190
191#[derive(Debug, Clone, PartialEq, Eq)]
193pub struct SegmentBuilder {
194 name: String,
195 fields: Vec<Option<Value>>,
197}
198
199impl SegmentBuilder {
200 fn new(name: &str) -> SegmentBuilder {
201 SegmentBuilder {
202 name: name.to_ascii_uppercase(),
203 fields: Vec::new(),
204 }
205 }
206
207 pub fn name(&self) -> &str {
209 &self.name
210 }
211
212 pub fn set(&mut self, n: usize, value: impl Into<Value>) -> &mut SegmentBuilder {
215 if n == 0 || (self.name == "MSH" && n <= 2) {
216 return self;
217 }
218 if self.fields.len() < n {
219 self.fields.resize(n, None);
220 }
221 self.fields[n - 1] = Some(value.into());
222 self
223 }
224
225 pub fn set_component(
228 &mut self,
229 n: usize,
230 c: usize,
231 text: impl Into<String>,
232 ) -> &mut SegmentBuilder {
233 self.set_subcomponent(n, c, 1, text)
234 }
235
236 pub fn set_subcomponent(
238 &mut self,
239 n: usize,
240 c: usize,
241 s: usize,
242 text: impl Into<String>,
243 ) -> &mut SegmentBuilder {
244 if n == 0 || (self.name == "MSH" && n <= 2) {
245 return self;
246 }
247 if self.fields.len() < n {
248 self.fields.resize(n, None);
249 }
250 self.fields[n - 1]
251 .get_or_insert_with(Value::default)
252 .set(1, c, s, text);
253 self
254 }
255
256 pub fn add_repetition<I, S>(&mut self, n: usize, components: I) -> &mut SegmentBuilder
258 where
259 I: IntoIterator<Item = S>,
260 S: Into<String>,
261 {
262 if n == 0 || (self.name == "MSH" && n <= 2) {
263 return self;
264 }
265 if self.fields.len() < n {
266 self.fields.resize(n, None);
267 }
268 let value = self.fields[n - 1].get_or_insert_with(Value::default);
269 if value.repetitions.len() == 1 && value.repetitions[0].is_empty() {
270 value.repetitions.clear();
271 }
272 value
273 .repetitions
274 .push(components.into_iter().map(|c| vec![c.into()]).collect());
275 self
276 }
277
278 fn render(&self, enc: Encoding) -> String {
279 let fs = enc.field as char;
280 let mut out = self.name.clone();
281 let mut fields: Vec<String> = self
282 .fields
283 .iter()
284 .map(|f| f.as_ref().map(|v| v.render(enc)).unwrap_or_default())
285 .collect();
286 if self.name == "MSH" {
287 let mut msh = vec![enc.msh2()];
289 msh.extend(fields.drain(..).skip(2));
290 fields = msh;
291 }
292 for f in fields {
293 out.push(fs);
294 out.push_str(&f);
295 }
296 out
297 }
298}
299
300#[derive(Debug, Clone, PartialEq, Eq)]
302pub struct Builder {
303 encoding: Encoding,
304 segments: Vec<SegmentBuilder>,
305}
306
307impl Default for Builder {
308 fn default() -> Self {
309 Builder::new()
310 }
311}
312
313impl Builder {
314 pub fn new() -> Builder {
316 Builder::with_encoding(Encoding::STANDARD)
317 }
318
319 pub fn with_encoding(encoding: Encoding) -> Builder {
321 Builder {
322 encoding,
323 segments: Vec::new(),
324 }
325 }
326
327 pub fn encoding(&self) -> Encoding {
329 self.encoding
330 }
331
332 pub fn segment(&mut self, name: &str) -> &mut SegmentBuilder {
335 self.segments.push(SegmentBuilder::new(name));
336 let last = self.segments.len() - 1;
337 &mut self.segments[last]
338 }
339
340 pub fn segments(&self) -> &[SegmentBuilder] {
342 &self.segments
343 }
344
345 pub fn segment_at(&mut self, index: usize) -> Option<&mut SegmentBuilder> {
347 self.segments.get_mut(index)
348 }
349
350 pub fn build(&self) -> String {
353 let mut out = String::new();
354 if self.segments.first().map(|s| s.name.as_str()) != Some("MSH") {
355 out.push_str(&SegmentBuilder::new("MSH").render(self.encoding));
356 out.push('\r');
357 }
358 for seg in &self.segments {
359 out.push_str(&seg.render(self.encoding));
360 out.push('\r');
361 }
362 out
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369 use crate::{unescape, Message};
370
371 #[test]
372 fn escape_roundtrips_through_unescape() {
373 let enc = Encoding::STANDARD;
374 let text = "a|b^c~d\\e&f\r\ng\nh";
375 let escaped = escape(text, enc);
376 assert_eq!(escaped, "a\\F\\b\\S\\c\\R\\d\\E\\e\\T\\f\\.br\\g\\.br\\h");
377 assert_eq!(unescape(&escaped, enc), "a|b^c~d\\e&f\ng\nh");
378 assert!(matches!(escape("plain", enc), Cow::Borrowed(_)));
379 }
380
381 #[test]
382 fn msh_is_written_from_the_encoding() {
383 let mut b = Builder::new();
384 b.segment("MSH")
385 .set(1, "ignored")
386 .set(2, "ignored")
387 .set(3, "APP");
388 assert_eq!(b.build(), "MSH|^~\\&|APP\r");
389 let mut c = Builder::with_encoding(Encoding::from_msh2(b'#', b"!*?%").unwrap());
390 c.segment("MSH").set(9, Value::components(["ADT", "A01"]));
391 c.segment("PID").set(3, "A#B");
392 assert_eq!(c.build(), "MSH#!*?%#######ADT!A01\rPID###A?F?B\r");
393 let msg = Message::parse(c.build()).unwrap();
394 assert_eq!(msg.get_decoded("PID-3").unwrap(), "A#B");
395 }
396
397 #[test]
398 fn missing_msh_is_inserted() {
399 let mut b = Builder::new();
400 b.segment("PID").set(1, "1");
401 assert_eq!(b.build(), "MSH|^~\\&\rPID|1\r");
402 }
403
404 #[test]
405 fn structure_roundtrips_through_parser() {
406 let mut b = Builder::new();
407 b.segment("MSH")
408 .set(9, Value::components(["ORU", "R01"]))
409 .set(10, "1");
410 let pid = b.segment("PID");
411 pid.set(
412 3,
413 Value::repetitions([["A", "", "", "H1"], ["B", "", "", "H2"]]),
414 );
415 pid.set_subcomponent(3, 4, 2, "sub");
416 pid.set_component(5, 2, "First");
417 pid.add_repetition(3, ["C"]);
418 b.segment("OBX").set(5, "value with | and ^ inside");
419 let text = b.build();
420 assert_eq!(
421 text,
422 "MSH|^~\\&|||||||ORU^R01|1\rPID|||A^^^H1&sub~B^^^H2~C||^First\rOBX|||||value with \\F\\ and \\S\\ inside\r"
423 );
424 let msg = Message::parse(&text).unwrap();
425 assert_eq!(msg.get("PID-3[2].4"), Some("H2"));
426 assert_eq!(msg.get("PID-3[3].1"), Some("C"));
427 assert_eq!(msg.get("PID-3.4.2"), Some("sub"));
428 assert_eq!(msg.get("PID-5.2"), Some("First"));
429 assert_eq!(
430 msg.get_decoded("OBX-5").unwrap(),
431 "value with | and ^ inside"
432 );
433 assert!(msg.warnings().is_empty());
434 }
435
436 #[test]
437 fn value_set_grows() {
438 let mut v = Value::default();
439 v.set(2, 3, 1, "x");
440 assert_eq!(v.render(Encoding::STANDARD), "~^^x");
441 assert_eq!(Value::from("t").render(Encoding::STANDARD), "t");
442 }
443}