1use crate::escape::unescape;
4use crate::{Encoding, ParseError, Path};
5use std::borrow::Cow;
6use std::ops::Range;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
10pub struct Span {
11 pub start: usize,
13 pub end: usize,
15}
16
17impl Span {
18 pub const fn new(start: usize, end: usize) -> Span {
20 Span { start, end }
21 }
22 pub const fn len(&self) -> usize {
24 self.end - self.start
25 }
26 pub const fn is_empty(&self) -> bool {
28 self.start == self.end
29 }
30 pub const fn range(&self) -> Range<usize> {
32 self.start..self.end
33 }
34 pub fn slice<'a>(&self, text: &'a str) -> &'a str {
37 text.get(self.range()).unwrap_or("")
38 }
39}
40
41impl From<Span> for Range<usize> {
42 fn from(s: Span) -> Range<usize> {
43 s.range()
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum Warning {
50 EncodingFallback {
52 found: String,
54 },
55 MllpFraming,
57 ByteOrderMark,
59 NonStandardTerminator,
61 ConflictingStudyUid {
66 first: Span,
68 other: Span,
70 },
71}
72
73impl std::fmt::Display for Warning {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 match self {
76 Warning::EncodingFallback { found } => write!(
77 f,
78 "MSH-2 {found:?} is not a valid set of encoding characters; using ^~\\&"
79 ),
80 Warning::MllpFraming => write!(f, "MLLP framing bytes present and ignored"),
81 Warning::ByteOrderMark => write!(f, "UTF-8 byte order mark ignored"),
82 Warning::NonStandardTerminator => {
83 write!(f, "segments terminated by LF or CRLF instead of CR")
84 }
85 Warning::ConflictingStudyUid { first, other } => write!(
86 f,
87 "the message carries differing study instance UIDs (bytes {}..{} used, \
88 bytes {}..{} differ)",
89 first.start, first.end, other.start, other.end
90 ),
91 }
92 }
93}
94
95#[derive(Debug, Clone)]
96struct SegmentNode {
97 name: Span,
98 span: Span,
99 fields: Vec<FieldNode>,
101}
102
103#[derive(Debug, Clone)]
104struct FieldNode {
105 span: Span,
106 repetitions: Vec<RepetitionNode>,
107}
108
109#[derive(Debug, Clone)]
110struct RepetitionNode {
111 span: Span,
112 components: Vec<ComponentNode>,
113}
114
115#[derive(Debug, Clone)]
116struct ComponentNode {
117 span: Span,
118 subcomponents: Vec<Span>,
119}
120
121#[derive(Debug, Clone)]
125pub struct Message {
126 raw: String,
127 encoding: Encoding,
128 segments: Vec<SegmentNode>,
129 warnings: Vec<Warning>,
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub struct MessageType<'m> {
135 pub code: &'m str,
137 pub trigger: &'m str,
139 pub structure: &'m str,
141}
142
143impl Message {
144 pub fn parse(text: impl Into<String>) -> Result<Message, ParseError> {
146 parse(text.into())
147 }
148
149 pub fn parse_bytes(bytes: &[u8]) -> Result<Message, ParseError> {
151 match std::str::from_utf8(bytes) {
152 Ok(text) => parse(text.to_string()),
153 Err(e) => Err(ParseError::InvalidUtf8 {
154 valid_up_to: e.valid_up_to(),
155 }),
156 }
157 }
158
159 pub fn parse_lossy(bytes: &[u8]) -> Result<Message, ParseError> {
163 parse(String::from_utf8_lossy(bytes).into_owned())
164 }
165
166 pub fn raw(&self) -> &str {
168 &self.raw
169 }
170
171 pub fn encoding(&self) -> Encoding {
173 self.encoding
174 }
175
176 pub fn warnings(&self) -> &[Warning] {
178 &self.warnings
179 }
180
181 pub fn segment_count(&self) -> usize {
183 self.segments.len()
184 }
185
186 pub fn segments(&self) -> impl Iterator<Item = Segment<'_>> + '_ {
188 self.segments
189 .iter()
190 .enumerate()
191 .map(move |(index, node)| Segment {
192 msg: self,
193 node,
194 index,
195 })
196 }
197
198 pub fn segment_at(&self, index: usize) -> Option<Segment<'_>> {
200 self.segments.get(index).map(|node| Segment {
201 msg: self,
202 node,
203 index,
204 })
205 }
206
207 pub fn segment(&self, name: &str) -> Option<Segment<'_>> {
209 self.segments_named(name).next()
210 }
211
212 pub fn segments_named(&self, name: &str) -> impl Iterator<Item = Segment<'_>> + '_ {
214 let name = name.to_ascii_uppercase();
215 self.segments()
216 .filter(move |s| s.name().eq_ignore_ascii_case(&name))
217 }
218
219 pub fn get(&self, path: &str) -> Option<&str> {
224 self.get_span(path).map(|s| s.slice(&self.raw))
225 }
226
227 pub fn get_decoded(&self, path: &str) -> Option<Cow<'_, str>> {
229 self.get(path).map(|v| unescape(v, self.encoding))
230 }
231
232 pub fn get_span(&self, path: &str) -> Option<Span> {
234 Path::parse(path).ok().and_then(|p| self.resolve(&p))
235 }
236
237 pub fn resolve(&self, path: &Path) -> Option<Span> {
239 let seg = self
240 .segments_named(&path.segment)
241 .nth(path.occurrence.checked_sub(1)?)?;
242 let Some(field_no) = path.field else {
243 return Some(seg.span());
244 };
245 let field = seg.field(field_no)?;
246 let rep_no = path.repetition;
247 let Some(comp_no) = path.component else {
248 return match rep_no {
249 None => Some(field.span()),
250 Some(r) => field.repetition(r).map(|r| r.span()),
251 };
252 };
253 let rep = field.repetition(rep_no.unwrap_or(1))?;
254 let comp = rep.component(comp_no)?;
255 match path.subcomponent {
256 None => Some(comp.span()),
257 Some(s) => comp.subcomponent(s).map(|s| s.span()),
258 }
259 }
260
261 pub fn decode<'a>(&self, text: &'a str) -> Cow<'a, str> {
263 unescape(text, self.encoding)
264 }
265
266 pub fn message_type(&self) -> Option<MessageType<'_>> {
268 let field = self.segment("MSH")?.field(9)?;
269 Some(MessageType {
270 code: field.component(1).map(|c| c.value()).unwrap_or(""),
271 trigger: field.component(2).map(|c| c.value()).unwrap_or(""),
272 structure: field.component(3).map(|c| c.value()).unwrap_or(""),
273 })
274 }
275
276 pub fn control_id(&self) -> Option<&str> {
278 self.get("MSH-10").filter(|v| !v.is_empty())
279 }
280
281 pub fn version(&self) -> Option<&str> {
283 self.get("MSH-12.1").filter(|v| !v.is_empty())
284 }
285}
286
287#[derive(Clone, Copy)]
289pub struct Segment<'m> {
290 msg: &'m Message,
291 node: &'m SegmentNode,
292 index: usize,
293}
294
295impl<'m> Segment<'m> {
296 pub fn name(&self) -> &'m str {
298 self.node.name.slice(&self.msg.raw)
299 }
300 pub fn index(&self) -> usize {
302 self.index
303 }
304 pub fn span(&self) -> Span {
306 self.node.span
307 }
308 pub fn text(&self) -> &'m str {
310 self.node.span.slice(&self.msg.raw)
311 }
312 pub fn field_count(&self) -> usize {
314 self.node.fields.len().saturating_sub(1)
315 }
316 pub fn field(&self, n: usize) -> Option<Field<'m>> {
319 if n == 0 {
320 return None;
321 }
322 self.node.fields.get(n).map(|node| Field {
323 msg: self.msg,
324 node,
325 number: n,
326 })
327 }
328 pub fn fields(&self) -> impl Iterator<Item = Field<'m>> + 'm {
330 let msg = self.msg;
331 self.node
332 .fields
333 .iter()
334 .enumerate()
335 .skip(1)
336 .map(move |(number, node)| Field { msg, node, number })
337 }
338}
339
340impl std::fmt::Debug for Segment<'_> {
341 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
342 f.debug_struct("Segment")
343 .field("name", &self.name())
344 .field("index", &self.index)
345 .field("span", &self.node.span)
346 .finish()
347 }
348}
349
350#[derive(Clone, Copy)]
352pub struct Field<'m> {
353 msg: &'m Message,
354 node: &'m FieldNode,
355 number: usize,
356}
357
358impl<'m> Field<'m> {
359 pub fn number(&self) -> usize {
361 self.number
362 }
363 pub fn span(&self) -> Span {
365 self.node.span
366 }
367 pub fn value(&self) -> &'m str {
369 self.node.span.slice(&self.msg.raw)
370 }
371 pub fn decoded(&self) -> Cow<'m, str> {
373 unescape(self.value(), self.msg.encoding)
374 }
375 pub fn is_empty(&self) -> bool {
377 self.node.span.is_empty()
378 }
379 pub fn is_null(&self) -> bool {
381 self.value() == "\"\""
382 }
383 pub fn repetition_count(&self) -> usize {
385 self.node.repetitions.len()
386 }
387 pub fn repetition(&self, n: usize) -> Option<Repetition<'m>> {
389 let node = self.node.repetitions.get(n.checked_sub(1)?)?;
390 Some(Repetition {
391 msg: self.msg,
392 node,
393 index: n,
394 })
395 }
396 pub fn repetitions(&self) -> impl Iterator<Item = Repetition<'m>> + 'm {
398 let msg = self.msg;
399 self.node
400 .repetitions
401 .iter()
402 .enumerate()
403 .map(move |(i, node)| Repetition {
404 msg,
405 node,
406 index: i + 1,
407 })
408 }
409 pub fn component(&self, n: usize) -> Option<Component<'m>> {
411 self.repetition(1)?.component(n)
412 }
413}
414
415impl std::fmt::Debug for Field<'_> {
416 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
417 f.debug_struct("Field")
418 .field("number", &self.number)
419 .field("value", &self.value())
420 .field("span", &self.node.span)
421 .finish()
422 }
423}
424
425#[derive(Clone, Copy)]
427pub struct Repetition<'m> {
428 msg: &'m Message,
429 node: &'m RepetitionNode,
430 index: usize,
431}
432
433impl<'m> Repetition<'m> {
434 pub fn index(&self) -> usize {
436 self.index
437 }
438 pub fn span(&self) -> Span {
440 self.node.span
441 }
442 pub fn value(&self) -> &'m str {
444 self.node.span.slice(&self.msg.raw)
445 }
446 pub fn decoded(&self) -> Cow<'m, str> {
448 unescape(self.value(), self.msg.encoding)
449 }
450 pub fn component_count(&self) -> usize {
452 self.node.components.len()
453 }
454 pub fn component(&self, n: usize) -> Option<Component<'m>> {
456 let node = self.node.components.get(n.checked_sub(1)?)?;
457 Some(Component {
458 msg: self.msg,
459 node,
460 index: n,
461 })
462 }
463 pub fn components(&self) -> impl Iterator<Item = Component<'m>> + 'm {
465 let msg = self.msg;
466 self.node
467 .components
468 .iter()
469 .enumerate()
470 .map(move |(i, node)| Component {
471 msg,
472 node,
473 index: i + 1,
474 })
475 }
476}
477
478impl std::fmt::Debug for Repetition<'_> {
479 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
480 f.debug_struct("Repetition")
481 .field("index", &self.index)
482 .field("value", &self.value())
483 .finish()
484 }
485}
486
487#[derive(Clone, Copy)]
489pub struct Component<'m> {
490 msg: &'m Message,
491 node: &'m ComponentNode,
492 index: usize,
493}
494
495impl<'m> Component<'m> {
496 pub fn index(&self) -> usize {
498 self.index
499 }
500 pub fn span(&self) -> Span {
502 self.node.span
503 }
504 pub fn value(&self) -> &'m str {
506 self.node.span.slice(&self.msg.raw)
507 }
508 pub fn decoded(&self) -> Cow<'m, str> {
510 unescape(self.value(), self.msg.encoding)
511 }
512 pub fn subcomponent_count(&self) -> usize {
514 self.node.subcomponents.len()
515 }
516 pub fn subcomponent(&self, n: usize) -> Option<Subcomponent<'m>> {
518 let span = *self.node.subcomponents.get(n.checked_sub(1)?)?;
519 Some(Subcomponent {
520 msg: self.msg,
521 span,
522 index: n,
523 })
524 }
525 pub fn subcomponents(&self) -> impl Iterator<Item = Subcomponent<'m>> + 'm {
527 let msg = self.msg;
528 self.node
529 .subcomponents
530 .iter()
531 .enumerate()
532 .map(move |(i, &span)| Subcomponent {
533 msg,
534 span,
535 index: i + 1,
536 })
537 }
538}
539
540impl std::fmt::Debug for Component<'_> {
541 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
542 f.debug_struct("Component")
543 .field("index", &self.index)
544 .field("value", &self.value())
545 .finish()
546 }
547}
548
549#[derive(Clone, Copy)]
551pub struct Subcomponent<'m> {
552 msg: &'m Message,
553 span: Span,
554 index: usize,
555}
556
557impl<'m> Subcomponent<'m> {
558 pub fn index(&self) -> usize {
560 self.index
561 }
562 pub fn span(&self) -> Span {
564 self.span
565 }
566 pub fn value(&self) -> &'m str {
568 self.span.slice(&self.msg.raw)
569 }
570 pub fn decoded(&self) -> Cow<'m, str> {
572 unescape(self.value(), self.msg.encoding)
573 }
574}
575
576impl std::fmt::Debug for Subcomponent<'_> {
577 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
578 f.debug_struct("Subcomponent")
579 .field("index", &self.index)
580 .field("value", &self.value())
581 .finish()
582 }
583}
584
585const MLLP_START: u8 = 0x0B;
590const MLLP_END: u8 = 0x1C;
591const BOM: &[u8] = b"\xEF\xBB\xBF";
592
593fn parse(raw: String) -> Result<Message, ParseError> {
594 let bytes = raw.as_bytes();
595 let mut warnings = Vec::new();
596
597 let mut lines = split_lines(bytes, &mut warnings);
598 lines.retain_mut(|line| {
600 let mut s = line.start;
601 let mut e = line.end;
602 loop {
603 if s < e && (bytes[s] == MLLP_START || bytes[s].is_ascii_whitespace()) {
604 if bytes[s] == MLLP_START {
605 push_unique(&mut warnings, Warning::MllpFraming);
606 }
607 s += 1;
608 } else if bytes[s..e].starts_with(BOM) {
609 push_unique(&mut warnings, Warning::ByteOrderMark);
610 s += BOM.len();
611 } else {
612 break;
613 }
614 }
615 while e > s && (bytes[e - 1] == MLLP_END || bytes[e - 1].is_ascii_whitespace()) {
616 if bytes[e - 1] == MLLP_END {
617 push_unique(&mut warnings, Warning::MllpFraming);
618 }
619 e -= 1;
620 }
621 *line = Span::new(s, e);
622 s < e
623 });
624
625 let Some(first) = lines.first().copied() else {
626 return Err(ParseError::Empty);
627 };
628 let first_text = first.slice(&raw);
629 if !first_text.starts_with("MSH") {
630 return Err(ParseError::MissingMsh {
631 found: truncate(first_text, 16),
632 });
633 }
634 if first.len() < 4 {
635 return Err(ParseError::MalformedMsh {
636 reason: "no field separator after MSH",
637 });
638 }
639 let fs = bytes[first.start + 3];
640 if !fs.is_ascii() || fs.is_ascii_alphanumeric() || fs.is_ascii_whitespace() {
641 return Err(ParseError::MalformedMsh {
642 reason: "byte after MSH is not a usable field separator",
643 });
644 }
645 let enc_start = first.start + 4;
646 let enc_end = bytes[enc_start..first.end]
647 .iter()
648 .position(|&b| b == fs)
649 .map(|p| enc_start + p)
650 .unwrap_or(first.end);
651 let enc_bytes = &bytes[enc_start..enc_end];
652 let encoding = match Encoding::from_msh2(fs, enc_bytes) {
653 Some(e) => e,
654 None => {
655 warnings.push(Warning::EncodingFallback {
656 found: String::from_utf8_lossy(enc_bytes).into_owned(),
657 });
658 Encoding::with_field(fs)
659 }
660 };
661
662 let mut segments = Vec::with_capacity(lines.len());
663 for (i, line) in lines.iter().enumerate() {
664 let name_len = bytes[line.start..line.end]
665 .iter()
666 .position(|&b| b == fs)
667 .unwrap_or(line.len());
668 let name = Span::new(line.start, line.start + name_len);
669 let name_bytes = &bytes[name.range()];
670 if name_bytes.len() != 3 || !name_bytes.iter().all(u8::is_ascii_alphanumeric) {
671 return Err(ParseError::MalformedSegment {
672 line: i + 1,
673 text: truncate(line.slice(&raw), 40),
674 });
675 }
676 let mut fields = vec![opaque_field(name)];
677 if i == 0 {
678 fields.push(opaque_field(Span::new(line.start + 3, line.start + 4)));
681 fields.push(opaque_field(Span::new(enc_start, enc_end)));
682 if enc_end < line.end {
683 for span in split_on(bytes, Span::new(enc_end + 1, line.end), fs) {
684 fields.push(parse_field(bytes, span, encoding));
685 }
686 }
687 } else if name.end < line.end {
688 for span in split_on(bytes, Span::new(name.end + 1, line.end), fs) {
689 fields.push(parse_field(bytes, span, encoding));
690 }
691 }
692 segments.push(SegmentNode {
693 name,
694 span: *line,
695 fields,
696 });
697 }
698
699 Ok(Message {
700 raw,
701 encoding,
702 segments,
703 warnings,
704 })
705}
706
707fn split_lines(bytes: &[u8], warnings: &mut Vec<Warning>) -> Vec<Span> {
708 let mut lines = Vec::new();
709 let mut start = 0;
710 let mut i = 0;
711 while i < bytes.len() {
712 match bytes[i] {
713 b'\r' => {
714 lines.push(Span::new(start, i));
715 if bytes.get(i + 1) == Some(&b'\n') {
716 i += 1;
717 push_unique(warnings, Warning::NonStandardTerminator);
718 }
719 start = i + 1;
720 }
721 b'\n' => {
722 lines.push(Span::new(start, i));
723 push_unique(warnings, Warning::NonStandardTerminator);
724 start = i + 1;
725 }
726 _ => {}
727 }
728 i += 1;
729 }
730 if start < bytes.len() {
731 lines.push(Span::new(start, bytes.len()));
732 }
733 lines
734}
735
736fn split_on(bytes: &[u8], span: Span, sep: u8) -> Vec<Span> {
737 let mut out = Vec::new();
738 let mut start = span.start;
739 for (i, &b) in bytes[span.range()].iter().enumerate() {
740 if b == sep {
741 out.push(Span::new(start, span.start + i));
742 start = span.start + i + 1;
743 }
744 }
745 out.push(Span::new(start, span.end));
746 out
747}
748
749fn opaque_field(span: Span) -> FieldNode {
750 FieldNode {
751 span,
752 repetitions: vec![RepetitionNode {
753 span,
754 components: vec![ComponentNode {
755 span,
756 subcomponents: vec![span],
757 }],
758 }],
759 }
760}
761
762fn parse_field(bytes: &[u8], span: Span, enc: Encoding) -> FieldNode {
763 let repetitions = split_on(bytes, span, enc.repetition)
764 .into_iter()
765 .map(|rep| RepetitionNode {
766 span: rep,
767 components: split_on(bytes, rep, enc.component)
768 .into_iter()
769 .map(|comp| ComponentNode {
770 span: comp,
771 subcomponents: split_on(bytes, comp, enc.subcomponent),
772 })
773 .collect(),
774 })
775 .collect();
776 FieldNode { span, repetitions }
777}
778
779fn push_unique(warnings: &mut Vec<Warning>, w: Warning) {
780 if !warnings.contains(&w) {
781 warnings.push(w);
782 }
783}
784
785fn truncate(text: &str, max: usize) -> String {
786 if text.len() <= max {
787 return text.to_string();
788 }
789 let mut end = max;
790 while !text.is_char_boundary(end) {
791 end -= 1;
792 }
793 format!("{}…", &text[..end])
794}