1use crate::{EdifactError, Segment};
9use std::borrow::Cow;
10use std::io::Read;
11use std::str::FromStr;
12
13pub trait EdifactDeserialize: Sized {
20 fn edifact_deserialize(segments: &[Segment<'_>]) -> Result<Self, EdifactError>;
25
26 fn edifact_deserialize_owned(segments: &[crate::OwnedSegment]) -> Result<Self, EdifactError> {
45 let borrowed: Vec<Segment<'_>> = segments.iter().map(|s| s.as_borrowed()).collect();
46 Self::edifact_deserialize(&borrowed)
47 }
48}
49
50pub trait EdifactCompositeDeserialize: Sized {
55 fn edifact_deserialize_composite(composite: CompositeElement<'_>)
57 -> Result<Self, EdifactError>;
58}
59
60impl EdifactCompositeDeserialize for Vec<String> {
61 fn edifact_deserialize_composite(
62 composite: CompositeElement<'_>,
63 ) -> Result<Self, EdifactError> {
64 Ok(composite.iter().map(str::to_owned).collect())
65 }
66}
67
68pub trait EdifactSegmentTag {
73 const SEGMENT_TAG: &'static str;
75
76 const QUALIFIER_PATTERN: Option<&'static str> = None;
82
83 fn matches_qualifier(seg: &Segment<'_>) -> bool {
85 match Self::QUALIFIER_PATTERN {
86 Some(pattern) => seg
87 .element_str(0)
88 .is_some_and(|q| qualifier_matches_pattern(q, pattern)),
89 None => true,
90 }
91 }
92
93 fn matches_segment(seg: &Segment<'_>) -> bool {
98 seg.tag == Self::SEGMENT_TAG && Self::matches_qualifier(seg)
99 }
100
101 fn matches_owned_segment(seg: &crate::OwnedSegment) -> bool {
105 if seg.tag != Self::SEGMENT_TAG {
106 return false;
107 }
108 match Self::QUALIFIER_PATTERN {
109 None => true,
110 Some(pattern) => {
111 let q = seg
112 .elements
113 .first()
114 .and_then(|e| e.components.first())
115 .map(|(c, _)| c.as_str())
116 .unwrap_or("");
117 qualifier_matches_pattern(q, pattern)
118 }
119 }
120 }
121}
122
123impl<T> EdifactDeserialize for Vec<T>
128where
129 T: EdifactDeserialize + EdifactSegmentTag,
130{
131 fn edifact_deserialize(segments: &[Segment<'_>]) -> Result<Self, EdifactError> {
132 segments
133 .iter()
134 .filter(|s| T::matches_segment(s))
135 .map(|seg| T::edifact_deserialize(std::slice::from_ref(seg)))
136 .collect()
137 }
138
139 fn edifact_deserialize_owned(segments: &[crate::OwnedSegment]) -> Result<Self, EdifactError> {
140 segments
141 .iter()
142 .filter(|s| T::matches_owned_segment(s))
143 .map(|seg| T::edifact_deserialize_owned(std::slice::from_ref(seg)))
144 .collect()
145 }
146}
147
148pub fn deserialize<T: EdifactDeserialize>(input: &[u8]) -> Result<T, EdifactError> {
164 let segments: Vec<Segment<'_>> = crate::from_bytes(input).collect::<Result<_, _>>()?;
165 T::edifact_deserialize(&segments)
166}
167
168pub fn deserialize_first_streaming<T>(input: &[u8]) -> Result<T, EdifactError>
173where
174 T: EdifactDeserialize + EdifactSegmentTag,
175{
176 for segment in crate::from_bytes(input) {
177 let segment = segment?;
178 if T::matches_segment(&segment) {
179 return T::edifact_deserialize(std::slice::from_ref(&segment));
180 }
181 }
182
183 Err(EdifactError::MissingSegment {
184 tag: T::SEGMENT_TAG.to_owned(),
185 expected_position: "any position in input".to_owned(),
186 })
187}
188
189pub fn deserialize_all_streaming<T>(input: &[u8]) -> Result<Vec<T>, EdifactError>
193where
194 T: EdifactDeserialize + EdifactSegmentTag,
195{
196 let mut out = Vec::new();
197 for segment in crate::from_bytes(input) {
198 let segment = segment?;
199 if T::matches_segment(&segment) {
200 out.push(T::edifact_deserialize(std::slice::from_ref(&segment))?);
201 }
202 }
203 Ok(out)
204}
205
206pub fn deserialize_first_from_reader<T, R>(reader: R) -> Result<T, EdifactError>
210where
211 T: EdifactDeserialize + EdifactSegmentTag,
212 R: Read,
213{
214 for segment in crate::from_reader(reader) {
215 let segment = segment?;
216 if !T::matches_owned_segment(&segment) {
218 continue;
219 }
220 return T::edifact_deserialize_owned(std::slice::from_ref(&segment));
221 }
222
223 Err(EdifactError::MissingSegment {
224 tag: T::SEGMENT_TAG.to_owned(),
225 expected_position: "any position in input".to_owned(),
226 })
227}
228
229pub fn deserialize_all_from_reader<T, R>(reader: R) -> Result<Vec<T>, EdifactError>
231where
232 T: EdifactDeserialize + EdifactSegmentTag,
233 R: Read,
234{
235 let mut out = Vec::new();
236 for segment in crate::from_reader(reader) {
237 let segment = segment?;
238 if !T::matches_owned_segment(&segment) {
240 continue;
241 }
242 out.push(T::edifact_deserialize_owned(std::slice::from_ref(
243 &segment,
244 ))?);
245 }
246 Ok(out)
247}
248
249pub fn deserialize_str<T: EdifactDeserialize>(input: &str) -> Result<T, EdifactError> {
251 deserialize(input.as_bytes())
252}
253
254pub fn find_segment<'s, 'd>(segments: &'s [Segment<'d>], tag: &str) -> Option<&'s Segment<'d>> {
258 segments.iter().find(|s| s.tag == tag)
259}
260
261pub fn find_segments_iter<'s, 'd: 's>(
263 segments: &'s [Segment<'d>],
264 tag: &'s str,
265) -> impl Iterator<Item = &'s Segment<'d>> {
266 segments.iter().filter(move |s| s.tag == tag)
267}
268
269pub fn find_qualified_segment<'s, 'd>(
271 segments: &'s [Segment<'d>],
272 tag: &str,
273 qualifier: &str,
274) -> Option<&'s Segment<'d>> {
275 segments
276 .iter()
277 .find(|s| s.tag == tag && s.element_str(0).unwrap_or("") == qualifier)
278}
279
280pub fn find_segment_typed<'s, 'd, T>(segments: &'s [Segment<'d>]) -> Option<&'s Segment<'d>>
282where
283 T: EdifactSegmentTag,
284{
285 segments.iter().find(|s| T::matches_segment(s))
286}
287
288pub fn find_segments_typed<'s, 'd: 's, T>(
290 segments: &'s [Segment<'d>],
291) -> impl Iterator<Item = &'s Segment<'d>>
292where
293 T: EdifactSegmentTag,
294{
295 segments.iter().filter(|s| T::matches_segment(s))
296}
297
298pub fn contiguous_groups_by_qualifier<'s, 'd, T>(
303 segments: &'s [Segment<'d>],
304) -> Vec<&'s [Segment<'d>]>
305where
306 T: EdifactSegmentTag,
307{
308 let mut groups = Vec::new();
309 let mut idx = 0;
310 while idx < segments.len() {
311 if T::matches_segment(&segments[idx]) {
312 let start = idx;
313 idx += 1;
314 while idx < segments.len() && T::matches_segment(&segments[idx]) {
315 idx += 1;
316 }
317 groups.push(&segments[start..idx]);
318 } else {
319 idx += 1;
320 }
321 }
322 groups
323}
324
325pub fn contiguous_groups_iter<'s, 'd, T>(
342 segments: &'s [Segment<'d>],
343) -> impl Iterator<Item = &'s [Segment<'d>]> + 's
344where
345 T: EdifactSegmentTag,
346{
347 let mut idx = 0;
348 let len = segments.len();
349 std::iter::from_fn(move || {
350 while idx < len && !T::matches_segment(&segments[idx]) {
352 idx += 1;
353 }
354 if idx >= len {
355 return None;
356 }
357 let start = idx;
358 idx += 1;
359 while idx < len && T::matches_segment(&segments[idx]) {
360 idx += 1;
361 }
362 Some(&segments[start..idx])
363 })
364}
365
366pub fn groups_are_contiguous_by_qualifier<T>(segments: &[Segment<'_>]) -> bool
368where
369 T: EdifactSegmentTag,
370{
371 let mut seen_match = false;
372 let mut seen_gap_after_match = false;
373
374 for seg in segments {
375 if T::matches_segment(seg) {
376 if seen_gap_after_match {
377 return false;
378 }
379 seen_match = true;
380 } else if seen_match {
381 seen_gap_after_match = true;
382 }
383 }
384
385 true
386}
387
388pub fn qualifier_matches_pattern(value: &str, pattern: &str) -> bool {
400 if pattern.is_empty() {
401 return value.is_empty();
402 }
403
404 if !pattern.contains('*') {
405 return value == pattern;
406 }
407
408 if let Some((prefix, suffix)) = pattern.split_once('*') {
413 if !suffix.contains('*') {
414 return value.len() >= prefix.len() + suffix.len()
415 && value.starts_with(prefix)
416 && value.ends_with(suffix);
417 }
418 }
419
420 let parts: smallvec::SmallVec<[&str; 4]> = pattern.split('*').collect();
422
423 if parts.len() > 4 {
428 return false;
429 }
430
431 let prefix = parts[0];
432 let suffix = parts[parts.len() - 1];
433
434 if !value.starts_with(prefix) || !value.ends_with(suffix) {
435 return false;
436 }
437
438 let mid_start = prefix.len();
439 let mid_end = value.len().saturating_sub(suffix.len());
440
441 if mid_start > mid_end {
442 return parts[1..parts.len() - 1].iter().all(|p| p.is_empty());
443 }
444
445 let mut remaining = &value[mid_start..mid_end];
446
447 for part in &parts[1..parts.len() - 1] {
448 if part.is_empty() {
449 continue;
450 }
451 match remaining.find(part) {
452 Some(idx) => remaining = &remaining[idx + part.len()..],
453 None => return false,
454 }
455 }
456
457 true
458}
459
460#[inline]
462pub fn element_str<'s>(seg: &'s Segment<'_>, idx: usize) -> &'s str {
463 seg.element_str(idx).unwrap_or("")
464}
465
466pub fn required_element<'a>(seg: &'a Segment<'_>, idx: usize) -> Result<&'a str, EdifactError> {
482 seg.text_element(idx)
483}
484
485pub fn optional_element<'a>(seg: &'a Segment<'_>, idx: usize) -> Option<&'a str> {
491 SegmentAccessor::optional_element(seg, idx)
492}
493
494pub fn required_component<'a>(
512 seg: &'a Segment<'_>,
513 elem_idx: usize,
514 comp_idx: usize,
515) -> Result<&'a str, EdifactError> {
516 seg.required_composite(elem_idx, comp_idx)
517}
518
519pub fn optional_component<'a>(
525 seg: &'a Segment<'_>,
526 elem_idx: usize,
527 comp_idx: usize,
528) -> Option<&'a str> {
529 SegmentAccessor::get_component(seg, elem_idx, comp_idx)
530}
531
532pub fn get_components_iter<'a>(seg: &'a Segment<'_>, idx: usize) -> impl Iterator<Item = &'a str> {
536 seg.elements
537 .get(idx)
538 .into_iter()
539 .flat_map(|elem| elem.components.iter().map(|(c, _)| c.as_ref()))
540}
541
542pub struct CompositeElement<'a> {
552 components: smallvec::SmallVec<[&'a str; 4]>,
553}
554
555impl<'a> CompositeElement<'a> {
556 pub fn from_slice(components: &'a [std::borrow::Cow<'a, str>]) -> Self {
560 Self {
561 components: components.iter().map(|c| c.as_ref()).collect(),
562 }
563 }
564
565 pub(crate) fn from_strs(components: smallvec::SmallVec<[&'a str; 4]>) -> Self {
567 Self { components }
568 }
569
570 pub fn get(&self, i: usize) -> Option<&'a str> {
572 self.components.get(i).copied()
573 }
574
575 pub fn get_or_empty(&self, i: usize) -> &'a str {
577 self.get(i).unwrap_or("")
578 }
579
580 pub fn len(&self) -> usize {
582 self.components.len()
583 }
584
585 pub fn is_empty(&self) -> bool {
587 self.components.is_empty()
588 }
589
590 pub fn iter(&self) -> impl Iterator<Item = &'a str> + '_ {
592 self.components.iter().copied()
593 }
594}
595
596pub fn composite_element<'a, 'd: 'a>(
598 seg: &'a Segment<'d>,
599 idx: usize,
600) -> Option<CompositeElement<'a>> {
601 seg.elements.get(idx).map(|elem| {
604 CompositeElement::from_strs(elem.components.iter().map(|(c, _)| c.as_ref()).collect())
605 })
606}
607
608pub fn find_segment_owned<'s>(
615 segments: &'s [crate::OwnedSegment],
616 tag: &str,
617) -> Option<&'s crate::OwnedSegment> {
618 segments.iter().find(|s| s.tag == tag)
619}
620
621pub fn find_qualified_segment_owned<'s>(
629 segments: &'s [crate::OwnedSegment],
630 tag: &str,
631 qualifier: &str,
632) -> Option<&'s crate::OwnedSegment> {
633 segments
634 .iter()
635 .find(|s| s.tag == tag && s.element_str(0).unwrap_or("") == qualifier)
636}
637
638pub trait SegmentAccessor<'a> {
640 fn get_element(&'a self, idx: usize) -> Option<&'a str>;
642 fn get_component(&'a self, elem: usize, comp: usize) -> Option<&'a str>;
644 fn get_composite(&'a self, idx: usize) -> Option<CompositeElement<'a>>;
646
647 fn text_element(&'a self, idx: usize) -> Result<&'a str, EdifactError>;
649 fn optional_element(&'a self, idx: usize) -> Option<&'a str>;
651 fn code_element<T: FromStr>(&'a self, idx: usize) -> Result<T, EdifactError>;
653 fn required_composite(&'a self, elem: usize, comp: usize) -> Result<&'a str, EdifactError>;
655 fn component_range(
666 &'a self,
667 elem: usize,
668 start_idx: usize,
669 count: usize,
670 ) -> Result<Vec<&'a str>, EdifactError> {
671 self.component_range_iter(elem, start_idx, count).collect()
674 }
675
676 fn component_range_iter(
681 &'a self,
682 elem: usize,
683 start_idx: usize,
684 count: usize,
685 ) -> impl Iterator<Item = Result<&'a str, EdifactError>> + 'a;
686}
687
688impl<'s, 'd> SegmentAccessor<'s> for Segment<'d>
689where
690 'd: 's,
691{
692 fn get_element(&'s self, idx: usize) -> Option<&'s str> {
693 self.element_str(idx).filter(|s| !s.is_empty())
694 }
695
696 fn get_component(&'s self, elem: usize, comp: usize) -> Option<&'s str> {
697 self.elements
698 .get(elem)
699 .and_then(|e| e.get_component(comp))
700 .filter(|s| !s.is_empty())
701 }
702
703 fn get_composite(&'s self, idx: usize) -> Option<CompositeElement<'s>> {
704 composite_element(self, idx)
705 }
706
707 fn text_element(&'s self, idx: usize) -> Result<&'s str, EdifactError> {
708 <Self as SegmentAccessor>::get_element(self, idx).ok_or_else(|| {
709 EdifactError::MissingRequiredElement {
710 tag: self.tag.to_owned(),
711 element_index: idx,
712 }
713 })
714 }
715
716 fn optional_element(&'s self, idx: usize) -> Option<&'s str> {
717 <Self as SegmentAccessor>::get_element(self, idx)
718 }
719
720 fn code_element<T: FromStr>(&'s self, idx: usize) -> Result<T, EdifactError> {
721 let raw = self.text_element(idx)?;
722 raw.parse::<T>().map_err(|_| EdifactError::InvalidText {
723 offset: self
724 .element_span(idx)
725 .map(|s| s.start)
726 .unwrap_or(self.span.start),
727 })
728 }
729
730 fn required_composite(&'s self, elem: usize, comp: usize) -> Result<&'s str, EdifactError> {
731 match self.elements.get(elem) {
732 None => Err(EdifactError::MissingRequiredElement {
733 tag: self.tag.to_owned(),
734 element_index: elem,
735 }),
736 Some(e) => e
737 .get_component(comp)
738 .filter(|s| !s.is_empty())
739 .ok_or_else(|| EdifactError::MissingRequiredComponent {
740 tag: self.tag.to_owned(),
741 element_index: elem,
742 component_index: comp,
743 }),
744 }
745 }
746
747 fn component_range_iter(
748 &'s self,
749 elem: usize,
750 start_idx: usize,
751 count: usize,
752 ) -> impl Iterator<Item = Result<&'s str, EdifactError>> + 's {
753 let tag = self.tag;
754 let element_exists = self.elements.get(elem).is_some();
755 let components = self
756 .elements
757 .get(elem)
758 .map(|e| e.components.as_slice())
759 .unwrap_or(&[]);
760 (start_idx..start_idx + count).map(move |idx| {
761 components
762 .get(idx)
763 .map(|(c, _)| c.as_ref())
764 .filter(|s| !s.is_empty())
765 .ok_or_else(|| {
766 if element_exists {
767 EdifactError::MissingRequiredComponent {
768 tag: tag.to_owned(),
769 element_index: elem,
770 component_index: idx,
771 }
772 } else {
773 EdifactError::MissingRequiredElement {
774 tag: tag.to_owned(),
775 element_index: elem,
776 }
777 }
778 })
779 })
780 }
781}
782
783#[derive(Debug)]
805pub struct MessageWindow<'a> {
806 pub message_type: Option<Cow<'a, str>>,
811 pub association_code: Option<Cow<'a, str>>,
816 pub segments: Vec<crate::Segment<'a>>,
818}
819
820impl<'a> MessageWindow<'a> {
821 #[must_use]
849 pub fn body(&self) -> &[crate::Segment<'a>] {
850 body_of(&self.segments, |segment| segment.tag)
851 }
852
853 fn from_segments(segments: Vec<crate::Segment<'a>>) -> Self {
859 let message_type = segments
860 .first()
861 .filter(|s| s.tag == "UNH")
862 .and_then(|unh| unh_component(unh, 0));
863 let association_code = segments
864 .first()
865 .filter(|s| s.tag == "UNH")
866 .and_then(|unh| unh_component(unh, 4));
867 Self {
868 message_type,
869 association_code,
870 segments,
871 }
872 }
873}
874
875fn unh_component<'a, 'b>(seg: &'b crate::Segment<'a>, comp_idx: usize) -> Option<Cow<'a, str>>
883where
884 'a: 'b,
885{
886 seg.elements
887 .get(1)
888 .and_then(|e| e.components.get(comp_idx))
889 .and_then(|(c, _)| if c.is_empty() { None } else { Some(c.clone()) })
890}
891
892fn body_of<S>(segments: &[S], tag: impl Fn(&S) -> &str) -> &[S] {
897 let start = usize::from(segments.first().is_some_and(|s| tag(s) == "UNH"));
898 let end = segments.len().saturating_sub(usize::from(
899 segments.last().is_some_and(|s| tag(s) == "UNT"),
900 ));
901 segments.get(start..end).unwrap_or(&[])
902}
903
904#[derive(Debug, Clone)]
913pub struct OwnedMessageWindow {
914 pub message_type: Option<String>,
916 pub association_code: Option<String>,
918 pub segments: Vec<crate::OwnedSegment>,
920}
921
922impl OwnedMessageWindow {
923 #[must_use]
927 pub fn body(&self) -> &[crate::OwnedSegment] {
928 body_of(&self.segments, |segment| segment.tag.as_str())
929 }
930
931 fn from_segments(segments: Vec<crate::OwnedSegment>) -> Self {
932 let unh = segments.first().filter(|s| s.tag == "UNH");
933 let message_type = unh
934 .and_then(|s| s.elements.get(1))
935 .and_then(|e| e.components.first())
936 .map(|(c, _)| c.as_str())
937 .filter(|s| !s.is_empty())
938 .map(str::to_owned);
939 let association_code = unh
940 .and_then(|s| s.elements.get(1))
941 .and_then(|e| e.components.get(4))
942 .map(|(c, _)| c.as_str())
943 .filter(|s| !s.is_empty())
944 .map(str::to_owned);
945 Self {
946 message_type,
947 association_code,
948 segments,
949 }
950 }
951}
952
953pub struct MessageWindowsSliceIter<'a> {
963 inner: crate::FromBytesIter<'a>,
964 buf: Vec<crate::Segment<'a>>,
965 in_message: bool,
966 done: bool,
967}
968
969impl<'a> MessageWindowsSliceIter<'a> {
970 fn new(inner: crate::FromBytesIter<'a>) -> Self {
971 Self {
972 inner,
973 buf: Vec::new(),
974 in_message: false,
975 done: false,
976 }
977 }
978}
979
980impl<'a> Iterator for MessageWindowsSliceIter<'a> {
981 type Item = Result<MessageWindow<'a>, EdifactError>;
982
983 fn next(&mut self) -> Option<Self::Item> {
984 if self.done {
985 return None;
986 }
987 loop {
988 let segment = match self.inner.next() {
989 Some(Ok(s)) => s,
990 Some(Err(e)) => {
991 self.done = true;
992 return Some(Err(e));
993 }
994 None => {
995 self.done = true;
996 if self.in_message && !self.buf.is_empty() {
997 self.in_message = false;
998 let offset = self.buf.last().map(|s| s.span.end).unwrap_or(0);
999 return Some(Err(EdifactError::UnexpectedEof { offset }));
1000 }
1001 return None;
1002 }
1003 };
1004
1005 match segment.tag {
1006 "UNH" => {
1007 if self.in_message {
1008 self.buf.clear();
1009 self.in_message = false;
1010 self.done = true;
1011 return Some(Err(EdifactError::InvalidSegmentForMessage {
1012 tag: "UNH".to_owned(),
1013 message_type: "ENVELOPE".to_owned(),
1014 span: segment.span,
1015 }));
1016 }
1017 self.buf.clear();
1018 self.in_message = true;
1019 self.buf.push(segment);
1020 }
1021 "UNT" if self.in_message => {
1022 self.buf.push(segment);
1023 self.in_message = false;
1024 let segments = std::mem::take(&mut self.buf);
1025 return Some(Ok(MessageWindow::from_segments(segments)));
1026 }
1027 _ if self.in_message => {
1028 self.buf.push(segment);
1029 }
1030 _ => {
1031 }
1033 }
1034 }
1035 }
1036}
1037
1038pub struct MessageWindowsIter<I> {
1059 inner: I,
1060 buf: Vec<crate::OwnedSegment>,
1061 in_message: bool,
1062 done: bool,
1065}
1066
1067impl<I: Iterator<Item = Result<crate::OwnedSegment, EdifactError>>> MessageWindowsIter<I> {
1068 pub fn new(inner: I) -> Self {
1070 Self {
1071 inner,
1072 buf: Vec::new(),
1073 in_message: false,
1074 done: false,
1075 }
1076 }
1077}
1078
1079impl<I: Iterator<Item = Result<crate::OwnedSegment, EdifactError>>> Iterator
1080 for MessageWindowsIter<I>
1081{
1082 type Item = Result<OwnedMessageWindow, EdifactError>;
1083
1084 fn next(&mut self) -> Option<Self::Item> {
1085 if self.done {
1086 return None;
1087 }
1088 loop {
1089 let segment = match self.inner.next() {
1090 Some(Ok(s)) => s,
1091 Some(Err(e)) => {
1092 self.done = true;
1093 return Some(Err(e));
1094 }
1095 None => {
1096 self.done = true;
1097 if self.in_message && !self.buf.is_empty() {
1100 self.in_message = false;
1101 let offset = self.buf.last().map(|s| s.span.end).unwrap_or(0);
1102 return Some(Err(EdifactError::UnexpectedEof { offset }));
1103 }
1104 return None;
1105 }
1106 };
1107
1108 match segment.tag.as_str() {
1109 "UNH" => {
1110 if self.in_message {
1111 self.buf.clear();
1113 self.in_message = false;
1114 self.done = true;
1115 return Some(Err(EdifactError::InvalidSegmentForMessage {
1116 tag: "UNH".to_owned(),
1117 message_type: "ENVELOPE".to_owned(),
1118 span: segment.span,
1119 }));
1120 }
1121 self.buf.clear();
1122 self.in_message = true;
1123 self.buf.push(segment);
1124 }
1125 "UNT" if self.in_message => {
1126 self.buf.push(segment);
1127 self.in_message = false;
1128 let segments = std::mem::take(&mut self.buf);
1129 return Some(Ok(OwnedMessageWindow::from_segments(segments)));
1130 }
1131 _ if self.in_message => {
1132 self.buf.push(segment);
1133 }
1134 _ => {
1135 }
1137 }
1138 }
1139 }
1140}
1141
1142pub fn message_windows_bytes(input: &[u8]) -> MessageWindowsSliceIter<'_> {
1171 MessageWindowsSliceIter::new(crate::from_bytes(input))
1172}
1173
1174pub fn message_windows_from_reader<R: Read>(
1180 reader: R,
1181) -> MessageWindowsIter<crate::FromReaderIter<R>> {
1182 MessageWindowsIter::new(crate::from_reader(reader))
1183}
1184
1185pub fn deserialize_messages_from_reader<T, R>(
1204 reader: R,
1205) -> impl Iterator<Item = Result<T, EdifactError>>
1206where
1207 T: EdifactDeserialize,
1208 R: Read,
1209{
1210 message_windows_from_reader(reader).map(|window| {
1211 let window = window?;
1212 T::edifact_deserialize_owned(&window.segments)
1213 })
1214}
1215
1216pub fn deserialize_messages_bytes<T>(
1218 input: &[u8],
1219) -> impl Iterator<Item = Result<T, EdifactError>> + '_
1220where
1221 T: EdifactDeserialize,
1222{
1223 message_windows_bytes(input).map(|window| {
1224 let window = window?;
1225 T::edifact_deserialize(&window.segments)
1226 })
1227}
1228
1229pub struct DispatchedMessage {
1233 pub message_type: String,
1235 value: Box<dyn std::any::Any + Send + Sync>,
1236}
1237
1238impl DispatchedMessage {
1239 pub fn downcast<T: std::any::Any + Send + Sync + 'static>(&self) -> Option<&T> {
1243 self.value.downcast_ref::<T>()
1244 }
1245}
1246
1247impl std::fmt::Debug for DispatchedMessage {
1248 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1249 f.debug_struct("DispatchedMessage")
1250 .field("message_type", &self.message_type)
1251 .finish_non_exhaustive()
1252 }
1253}
1254
1255type DispatchHandlerFn = Box<
1256 dyn for<'a> Fn(&[Segment<'a>]) -> Result<Box<dyn std::any::Any + Send + Sync>, EdifactError>
1257 + Send
1258 + Sync,
1259>;
1260
1261type FallbackHandlerFn = Box<
1262 dyn for<'a> Fn(
1263 &[Segment<'a>],
1264 &str,
1265 ) -> Result<Box<dyn std::any::Any + Send + Sync>, EdifactError>
1266 + Send
1267 + Sync,
1268>;
1269
1270pub struct MessageDispatch {
1295 handlers: Vec<(String, DispatchHandlerFn)>,
1296 fallback: Option<FallbackHandlerFn>,
1297}
1298
1299impl Default for MessageDispatch {
1300 fn default() -> Self {
1301 Self::new()
1302 }
1303}
1304
1305impl MessageDispatch {
1306 pub fn new() -> Self {
1308 Self {
1309 handlers: Vec::new(),
1310 fallback: None,
1311 }
1312 }
1313
1314 pub fn on<T, F>(mut self, message_type: &str, handler: F) -> Self
1319 where
1320 T: std::any::Any + Send + Sync + 'static,
1321 F: for<'a> Fn(&[Segment<'a>]) -> Result<T, EdifactError> + Send + Sync + 'static,
1322 {
1323 let erased: DispatchHandlerFn = Box::new(move |segs| {
1324 let val = handler(segs)?;
1325 Ok(Box::new(val) as Box<dyn std::any::Any + Send + Sync>)
1326 });
1327 self.handlers.push((message_type.to_owned(), erased));
1328 self
1329 }
1330
1331 pub fn fallback<T, F>(mut self, handler: F) -> Self
1336 where
1337 T: std::any::Any + Send + Sync + 'static,
1338 F: for<'a> Fn(&[Segment<'a>], &str) -> Result<T, EdifactError> + Send + Sync + 'static,
1339 {
1340 let erased: FallbackHandlerFn = Box::new(move |segs, mt| {
1341 let val = handler(segs, mt)?;
1342 Ok(Box::new(val) as Box<dyn std::any::Any + Send + Sync>)
1343 });
1344 self.fallback = Some(erased);
1345 self
1346 }
1347
1348 pub fn dispatch(&self, window: &[Segment<'_>]) -> Result<DispatchedMessage, EdifactError> {
1353 let message_type = window
1354 .iter()
1355 .find(|s| s.tag == "UNH")
1356 .and_then(|unh| unh.get_element(1))
1357 .and_then(|e| e.get_component(0))
1358 .map(|s| s.to_owned())
1359 .ok_or_else(|| EdifactError::MissingSegment {
1360 tag: "UNH".to_owned(),
1361 expected_position: "first segment of message window".to_owned(),
1362 })?;
1363
1364 for (mt, handler) in &self.handlers {
1365 if *mt == message_type {
1366 let value = handler(window)?;
1367 return Ok(DispatchedMessage {
1368 message_type,
1369 value,
1370 });
1371 }
1372 }
1373
1374 if let Some(fallback) = &self.fallback {
1375 let value = fallback(window, &message_type)?;
1376 return Ok(DispatchedMessage {
1377 message_type,
1378 value,
1379 });
1380 }
1381
1382 Err(EdifactError::UnexpectedMessageType { message_type })
1383 }
1384
1385 pub fn dispatch_all_from_bytes<'a>(
1390 &'a self,
1391 input: &'a [u8],
1392 ) -> impl Iterator<Item = Result<DispatchedMessage, EdifactError>> + 'a {
1393 message_windows_bytes(input).map(move |window| {
1394 let window = window?;
1395 self.dispatch(&window.segments)
1396 })
1397 }
1398
1399 pub fn dispatch_all_from_reader<R: Read + 'static>(
1406 &self,
1407 reader: R,
1408 ) -> impl Iterator<Item = Result<DispatchedMessage, EdifactError>> + '_ {
1409 message_windows_from_reader(reader).map(|window| {
1410 let window = window?;
1411 let borrowed: Vec<Segment<'_>> =
1412 window.segments.iter().map(|s| s.as_borrowed()).collect();
1413 self.dispatch(&borrowed)
1414 })
1415 }
1416}
1417
1418#[cfg(test)]
1419mod tests {
1420 use super::*;
1421
1422 #[derive(Debug, PartialEq)]
1424 struct BgmSegment {
1425 doc_name_code: String,
1426 pruef_id: String,
1427 msg_function: Option<String>,
1428 }
1429
1430 impl EdifactSegmentTag for BgmSegment {
1431 const SEGMENT_TAG: &'static str = "BGM";
1432 }
1433
1434 struct NadM;
1435
1436 impl EdifactSegmentTag for NadM {
1437 const SEGMENT_TAG: &'static str = "NAD";
1438 const QUALIFIER_PATTERN: Option<&'static str> = Some("M*");
1439 }
1440
1441 struct NadWildcard;
1442
1443 impl EdifactSegmentTag for NadWildcard {
1444 const SEGMENT_TAG: &'static str = "NAD";
1445 const QUALIFIER_PATTERN: Option<&'static str> = Some("M*");
1446 }
1447
1448 impl EdifactDeserialize for BgmSegment {
1449 fn edifact_deserialize(segments: &[Segment<'_>]) -> Result<Self, EdifactError> {
1450 let seg = find_segment(segments, "BGM").ok_or_else(|| {
1451 EdifactError::MissingRequiredElement {
1452 tag: "BGM".to_owned(),
1453 element_index: 0,
1454 }
1455 })?;
1456 Ok(Self {
1457 doc_name_code: element_str(seg, 0).to_owned(),
1458 pruef_id: element_str(seg, 1).to_owned(),
1459 msg_function: seg
1460 .element_str(2)
1461 .filter(|s| !s.is_empty())
1462 .map(str::to_owned),
1463 })
1464 }
1465 }
1466
1467 #[test]
1468 fn deserialize_single_segment() {
1469 let input = b"BGM+E03+11042+9'";
1470 let bgm: BgmSegment = deserialize(input).unwrap();
1471 assert_eq!(bgm.doc_name_code, "E03");
1472 assert_eq!(bgm.pruef_id, "11042");
1473 assert_eq!(bgm.msg_function, Some("9".to_owned()));
1474 }
1475
1476 #[test]
1477 fn streaming_deserialize_first_from_bytes() {
1478 let input = b"UNH+1+ORDERS:D:11A:UN'BGM+E03+11042+9'UNT+3+1'";
1479 let bgm: BgmSegment = deserialize_first_streaming(input).unwrap();
1480 assert_eq!(bgm.pruef_id, "11042");
1481 }
1482
1483 #[test]
1484 fn streaming_deserialize_all_from_bytes() {
1485 let input = b"BGM+E03+11042+9'RFF+AA:1'BGM+E01+11043+9'";
1486 let bgms: Vec<BgmSegment> = deserialize_all_streaming(input).unwrap();
1487 assert_eq!(bgms.len(), 2);
1488 assert_eq!(bgms[0].pruef_id, "11042");
1489 assert_eq!(bgms[1].pruef_id, "11043");
1490 }
1491
1492 #[test]
1493 fn streaming_deserialize_first_from_reader() {
1494 let input =
1495 std::io::Cursor::new(b"UNH+1+ORDERS:D:11A:UN'BGM+E03+11042+9'UNT+3+1'".to_vec());
1496 let bgm: BgmSegment = deserialize_first_from_reader(input).unwrap();
1497 assert_eq!(bgm.pruef_id, "11042");
1498 }
1499
1500 #[test]
1501 fn streaming_deserialize_all_from_reader() {
1502 let input = std::io::Cursor::new(b"BGM+E03+11042+9'BGM+E01+11043+9'".to_vec());
1503 let bgms: Vec<BgmSegment> = deserialize_all_from_reader(input).unwrap();
1504 assert_eq!(bgms.len(), 2);
1505 assert_eq!(bgms[0].pruef_id, "11042");
1506 assert_eq!(bgms[1].pruef_id, "11043");
1507 }
1508
1509 #[test]
1510 fn missing_segment_returns_error() {
1511 let input = b"DTM+137:20230401:102'";
1512 let result: Result<BgmSegment, _> = deserialize(input);
1513 assert!(result.is_err());
1514 }
1515
1516 #[test]
1517 fn vec_collects_all_matching_segments() {
1518 let input = b"DTM+137:20230401:102'BGM+E03+11042+9'BGM+E01+11043+9'";
1519 let bgms: Vec<BgmSegment> = deserialize(input).unwrap();
1520 assert_eq!(bgms.len(), 2);
1521 assert_eq!(bgms[0].pruef_id, "11042");
1522 assert_eq!(bgms[1].pruef_id, "11043");
1523 }
1524
1525 #[test]
1526 fn find_qualified_segment_matches_qualifier() {
1527 let input = b"NAD+MS+9900001+293'NAD+MR+9900002+293'";
1528 let segments: Vec<Segment<'_>> =
1529 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1530 let nad_ms = find_qualified_segment(&segments, "NAD", "MS");
1531 let nad_mr = find_qualified_segment(&segments, "NAD", "MR");
1532 assert!(nad_ms.is_some());
1533 assert!(nad_mr.is_some());
1534 assert_eq!(element_str(nad_ms.unwrap(), 0), "MS");
1535 assert_eq!(element_str(nad_mr.unwrap(), 0), "MR");
1536 }
1537
1538 #[test]
1539 fn round_trip_str_api() {
1540 let input = "BGM+E03+11042+9'";
1541 let bgm: BgmSegment = deserialize_str(input).unwrap();
1542 assert_eq!(bgm.pruef_id, "11042");
1543 }
1544
1545 #[test]
1546 fn required_element_extraction() {
1547 let input = b"BGM+E03+11042+9'";
1548 let segments: Vec<Segment<'_>> =
1549 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1550 let seg = &segments[0];
1551
1552 assert_eq!(required_element(seg, 0).unwrap(), "E03");
1553 assert_eq!(required_element(seg, 1).unwrap(), "11042");
1554 assert!(required_element(seg, 5).is_err());
1556 }
1557
1558 #[test]
1559 fn optional_element_extraction() {
1560 let input = b"BGM+E03+11042+9'BGM+E01++absent'";
1561 let segments: Vec<Segment<'_>> =
1562 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1563
1564 assert_eq!(optional_element(&segments[0], 0), Some("E03"));
1566 assert_eq!(optional_element(&segments[0], 1), Some("11042"));
1567 assert_eq!(optional_element(&segments[0], 5), None);
1568
1569 assert_eq!(optional_element(&segments[1], 1), None);
1571 }
1572
1573 #[test]
1574 fn component_extraction() {
1575 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1576 let segments: Vec<Segment<'_>> =
1577 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1578 let seg = &segments[0];
1579
1580 assert_eq!(required_component(seg, 0, 0).unwrap(), "UNOA");
1581 assert_eq!(required_component(seg, 0, 1).unwrap(), "1");
1582 assert!(required_component(seg, 0, 5).is_err());
1584 }
1585
1586 #[test]
1587 fn composite_element_helper() {
1588 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1589 let segments: Vec<Segment<'_>> =
1590 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1591 let seg = &segments[0];
1592
1593 let comp = composite_element(seg, 0).unwrap();
1594 assert_eq!(comp.len(), 2);
1595 assert_eq!(comp.get(0), Some("UNOA"));
1596 assert_eq!(comp.get(1), Some("1"));
1597 assert_eq!(comp.get(5), None);
1598 assert_eq!(comp.get_or_empty(5), "");
1599 }
1600
1601 #[test]
1602 fn get_all_components() {
1603 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1605 let segments: Vec<Segment<'_>> =
1606 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1607 let seg = &segments[0];
1608
1609 let comps: Vec<&str> = get_components_iter(seg, 0).collect(); assert!(!comps.is_empty(), "Expected components but got empty");
1611 assert_eq!(comps.len(), 2);
1612 assert_eq!(comps[0], "UNOA");
1613 assert_eq!(comps[1], "1");
1614 }
1615
1616 #[test]
1617 fn qualifier_pattern_matching_supports_exact_and_wildcard() {
1618 assert!(qualifier_matches_pattern("MS", "MS"));
1620 assert!(!qualifier_matches_pattern("MS", "M")); assert!(qualifier_matches_pattern("MS", "M*"));
1623 assert!(qualifier_matches_pattern("MRY", "M*Y"));
1624 assert!(!qualifier_matches_pattern("AB", "M*"));
1625 }
1626
1627 #[test]
1629 fn qualifier_matches_pattern_table() {
1630 let cases: &[(&str, &str, bool)] = &[
1632 ("", "", true), ("", "*", true), ("A", "", false), ("", "A", false), ("MS", "MS", true),
1639 ("BY", "BY", true),
1640 ("ms", "MS", false), ("MSX", "MS", false), ("M", "MS", false), ("MS", "M*", true),
1645 ("MULTI", "MUL*", true),
1646 ("AB", "M*", false),
1647 ("", "M*", false), ("MSG", "*G", true),
1650 ("G", "*G", true),
1651 ("MSG", "*X", false),
1652 ("", "*G", false),
1653 ("MRY", "M*Y", true),
1655 ("MAY", "M*Y", true),
1656 ("MY", "M*Y", true), ("MYY", "M*Y", true), ("MAYZ", "M*Y", false), ("AB", "M*Y", false),
1660 ("*", "*", true), ("anything", "*", true),
1663 ("", "*", true),
1664 ("ABCDE", "A*C*E", true),
1666 ("ACE", "A*C*E", true), ("AXCYE", "A*C*E", true),
1668 ("ABCDF", "A*C*E", false),
1669 ("AB", "A**B", true), ("AB", "A*B*C", false),
1673 ("XMS", "MS", false),
1675 ];
1676
1677 for (value, pattern, expected) in cases {
1678 let got = qualifier_matches_pattern(value, pattern);
1679 assert_eq!(
1680 got, *expected,
1681 "qualifier_matches_pattern({value:?}, {pattern:?}) expected {expected} but got {got}"
1682 );
1683 }
1684 }
1685
1686 #[test]
1687 fn typed_qualifier_helpers_work() {
1688 let input = b"NAD+MS+9900001+293'NAD+MR+9900002+293'";
1689 let segments: Vec<Segment<'_>> =
1690 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1691
1692 let first = find_segment_typed::<NadM>(&segments).unwrap();
1693 assert_eq!(first.element_str(0), Some("MS"));
1694
1695 let all: Vec<_> = find_segments_typed::<NadWildcard>(&segments).collect();
1696 assert_eq!(all.len(), 2);
1697 }
1698
1699 #[test]
1700 fn segment_accessor_trait_methods_work() {
1701 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1702 let segments: Vec<Segment<'_>> =
1703 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1704 let seg = &segments[0];
1705
1706 assert_eq!(SegmentAccessor::get_element(seg, 1), Some("SENDER"));
1707 assert_eq!(SegmentAccessor::required_composite(seg, 0, 1).unwrap(), "1");
1708 let parsed: i32 = SegmentAccessor::code_element(seg, 4).unwrap();
1709 assert_eq!(parsed, 1);
1710 let reps = SegmentAccessor::component_range(seg, 3, 0, 2).unwrap();
1711 assert_eq!(reps, vec!["200101", "0900"]);
1712 }
1713
1714 #[test]
1715 fn group_helpers_detect_contiguity() {
1716 struct NadAny;
1717 impl EdifactSegmentTag for NadAny {
1718 const SEGMENT_TAG: &'static str = "NAD";
1719 }
1720
1721 let contiguous_input = b"NAD+MS+1'NAD+MR+2'RFF+AA:1'";
1722 let contiguous_segments: Vec<Segment<'_>> = crate::from_bytes(contiguous_input)
1723 .collect::<Result<_, _>>()
1724 .unwrap();
1725 assert!(groups_are_contiguous_by_qualifier::<NadAny>(
1726 &contiguous_segments
1727 ));
1728
1729 let non_contiguous_input = b"NAD+MS+1'RFF+AA:1'NAD+MR+2'";
1730 let non_contiguous_segments: Vec<Segment<'_>> = crate::from_bytes(non_contiguous_input)
1731 .collect::<Result<_, _>>()
1732 .unwrap();
1733 assert!(!groups_are_contiguous_by_qualifier::<NadAny>(
1734 &non_contiguous_segments
1735 ));
1736 }
1737
1738 #[test]
1739 fn group_helpers_collect_contiguous_groups() {
1740 struct NadAny;
1741 impl EdifactSegmentTag for NadAny {
1742 const SEGMENT_TAG: &'static str = "NAD";
1743 }
1744
1745 let input = b"NAD+MS+1'NAD+MR+2'RFF+AA:1'NAD+BY+3'";
1746 let segments: Vec<Segment<'_>> =
1747 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1748 let groups = contiguous_groups_by_qualifier::<NadAny>(&segments);
1749
1750 assert_eq!(groups.len(), 2);
1751 assert_eq!(groups[0].len(), 2);
1752 assert_eq!(groups[1].len(), 1);
1753 }
1754
1755 #[test]
1758 fn message_windows_bytes_yields_complete_windows() {
1759 let input = b"UNB+UNOA:1+S+R+200101:0900+1'\
1760 UNH+1+ORDERS:D:96A:UN'\
1761 BGM+220+PO-001+9'\
1762 UNT+3+1'\
1763 UNZ+1+1'";
1764 let windows: Vec<_> = message_windows_bytes(input)
1765 .collect::<Result<_, _>>()
1766 .unwrap();
1767 assert_eq!(windows.len(), 1);
1768 assert_eq!(windows[0].segments[0].tag, "UNH");
1769 assert_eq!(windows[0].segments.last().unwrap().tag, "UNT");
1770 assert_eq!(windows[0].message_type.as_deref(), Some("ORDERS"));
1771 assert_eq!(windows[0].association_code.as_deref(), None);
1772 }
1773
1774 #[test]
1775 fn message_windows_bytes_preserves_owned_unh_metadata() {
1776 let input = b"UNB+UNOA:1+S+R+200101:0900+1'\
1777 UNH+1+ORD?ERS:D:96A:UN:5??5??3a'\
1778 BGM+220+PO-001+9'\
1779 UNT+3+1'\
1780 UNZ+1+1'";
1781 let windows: Vec<_> = message_windows_bytes(input)
1782 .collect::<Result<_, _>>()
1783 .unwrap();
1784
1785 assert_eq!(windows.len(), 1);
1786 assert_eq!(windows[0].message_type.as_deref(), Some("ORDERS"));
1787 assert_eq!(windows[0].association_code.as_deref(), Some("5?5?3a"));
1788 }
1789
1790 #[test]
1791 fn message_windows_truncated_stream_returns_error() {
1792 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'";
1794 let results: Vec<_> = message_windows_bytes(input).collect();
1795 assert_eq!(results.len(), 1);
1796 assert!(
1797 matches!(results[0], Err(EdifactError::UnexpectedEof { .. })),
1798 "expected UnexpectedEof for truncated window, got: {:?}",
1799 results[0]
1800 );
1801 }
1802
1803 #[test]
1804 fn message_windows_subsequent_calls_return_none_after_truncation() {
1805 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'";
1806 let mut iter = message_windows_bytes(input);
1807 assert!(matches!(
1808 iter.next(),
1809 Some(Err(EdifactError::UnexpectedEof { .. }))
1810 ));
1811 assert!(iter.next().is_none());
1813 }
1814
1815 #[test]
1816 fn message_windows_unh_without_unt_before_next_unh_returns_error() {
1817 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'\
1818 UNH+2+ORDERS:D:96A:UN'BGM+220+PO-002+9'UNT+3+2'";
1819 let results: Vec<_> = message_windows_bytes(input).collect();
1820 assert!(
1822 matches!(
1823 results[0],
1824 Err(EdifactError::InvalidSegmentForMessage { ref tag, .. }) if tag == "UNH"
1825 ),
1826 "expected InvalidSegmentForMessage(UNH), got: {:?}",
1827 results[0]
1828 );
1829 }
1830
1831 fn parse_one(input: &str) -> crate::OwnedSegment {
1834 crate::from_reader_collect(std::io::Cursor::new(input.as_bytes()))
1835 .expect("parse failed")
1836 .into_iter()
1837 .next()
1838 .expect("at least one segment")
1839 }
1840
1841 #[test]
1842 fn segment_accessor_get_element_returns_value() {
1843 let owned = parse_one("BGM+220+PO-001+9'");
1844 let seg = owned.as_borrowed();
1845 assert_eq!(SegmentAccessor::get_element(&seg, 0), Some("220"));
1846 assert_eq!(SegmentAccessor::get_element(&seg, 1), Some("PO-001"));
1847 assert_eq!(SegmentAccessor::get_element(&seg, 2), Some("9"));
1848 assert_eq!(
1849 SegmentAccessor::get_element(&seg, 9),
1850 None,
1851 "out-of-bounds must return None"
1852 );
1853 }
1854
1855 #[test]
1856 fn segment_accessor_get_element_filters_empty() {
1857 let owned = parse_one("TST+++VALUE'");
1858 let seg = owned.as_borrowed();
1859 assert_eq!(
1861 SegmentAccessor::get_element(&seg, 0),
1862 None,
1863 "empty element must return None"
1864 );
1865 assert_eq!(
1866 SegmentAccessor::get_element(&seg, 1),
1867 None,
1868 "empty element must return None"
1869 );
1870 assert_eq!(SegmentAccessor::get_element(&seg, 2), Some("VALUE"));
1871 }
1872
1873 #[test]
1874 fn segment_accessor_get_component_returns_value() {
1875 let owned = parse_one("UNH+1+ORDERS:D:96A:UN'");
1876 let seg = owned.as_borrowed();
1877 assert_eq!(seg.get_component(1, 0), Some("ORDERS"));
1878 assert_eq!(seg.get_component(1, 1), Some("D"));
1879 assert_eq!(seg.get_component(1, 2), Some("96A"));
1880 assert_eq!(seg.get_component(1, 3), Some("UN"));
1881 assert_eq!(
1882 seg.get_component(1, 9),
1883 None,
1884 "out-of-bounds must return None"
1885 );
1886 }
1887
1888 #[test]
1889 fn segment_accessor_text_element_errors_on_missing() {
1890 let owned = parse_one("BGM+'");
1891 let seg = owned.as_borrowed();
1892 let err = seg.text_element(0);
1894 assert!(
1895 matches!(err, Err(EdifactError::MissingRequiredElement { ref tag, element_index: 0 }) if tag == "BGM"),
1896 "expected MissingRequiredElement, got: {err:?}"
1897 );
1898 }
1899
1900 #[test]
1901 fn segment_accessor_required_composite_errors_on_missing() {
1902 let owned = parse_one("DTM+137'");
1903 let seg = owned.as_borrowed();
1904 let err = seg.required_composite(0, 1);
1906 assert!(
1907 matches!(err, Err(EdifactError::MissingRequiredComponent { ref tag, element_index: 0, component_index: 1 }) if tag == "DTM"),
1908 "expected MissingRequiredComponent, got: {err:?}"
1909 );
1910 }
1911
1912 #[test]
1913 fn segment_accessor_code_element_parses_integer() {
1914 let owned = parse_one("QTY+21:100'");
1915 let seg = owned.as_borrowed();
1916 let qty: u32 = seg.code_element(0).expect("should parse qualifier as u32");
1917 assert_eq!(qty, 21);
1918 }
1919
1920 #[test]
1921 fn segment_accessor_optional_element_absent_returns_none() {
1922 let owned = parse_one("BGM+220'");
1923 let seg = owned.as_borrowed();
1924 assert_eq!(seg.optional_element(5), None);
1925 }
1926}