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 fn repeated_components<'a>(
575 seg: &'a Segment<'_>,
576 element: usize,
577 component: usize,
578) -> impl Iterator<Item = &'a str> {
579 seg.elements.get(element).into_iter().flat_map(move |elem| {
580 elem.repetitions()
581 .map(move |occurrence| occurrence.get(component).map_or("", |(c, _)| c.as_ref()))
582 })
583}
584
585pub fn repeated_components_owned(
587 seg: &crate::OwnedSegment,
588 element: usize,
589 component: usize,
590) -> impl Iterator<Item = &str> {
591 seg.elements.get(element).into_iter().flat_map(move |elem| {
592 elem.repetitions()
593 .map(move |occurrence| occurrence.get(component).map_or("", |(c, _)| c.as_str()))
594 })
595}
596
597pub struct CompositeElement<'a> {
607 components: smallvec::SmallVec<[&'a str; 4]>,
608}
609
610impl<'a> CompositeElement<'a> {
611 pub fn from_slice(components: &'a [std::borrow::Cow<'a, str>]) -> Self {
615 Self {
616 components: components.iter().map(|c| c.as_ref()).collect(),
617 }
618 }
619
620 pub(crate) fn from_strs(components: smallvec::SmallVec<[&'a str; 4]>) -> Self {
622 Self { components }
623 }
624
625 pub fn get(&self, i: usize) -> Option<&'a str> {
627 self.components.get(i).copied()
628 }
629
630 pub fn get_or_empty(&self, i: usize) -> &'a str {
632 self.get(i).unwrap_or("")
633 }
634
635 pub fn len(&self) -> usize {
637 self.components.len()
638 }
639
640 pub fn is_empty(&self) -> bool {
642 self.components.is_empty()
643 }
644
645 pub fn iter(&self) -> impl Iterator<Item = &'a str> + '_ {
647 self.components.iter().copied()
648 }
649}
650
651pub fn composite_element<'a, 'd: 'a>(
653 seg: &'a Segment<'d>,
654 idx: usize,
655) -> Option<CompositeElement<'a>> {
656 seg.elements.get(idx).map(|elem| {
659 CompositeElement::from_strs(elem.components.iter().map(|(c, _)| c.as_ref()).collect())
660 })
661}
662
663pub fn find_segment_owned<'s>(
670 segments: &'s [crate::OwnedSegment],
671 tag: &str,
672) -> Option<&'s crate::OwnedSegment> {
673 segments.iter().find(|s| s.tag == tag)
674}
675
676pub fn find_qualified_segment_owned<'s>(
684 segments: &'s [crate::OwnedSegment],
685 tag: &str,
686 qualifier: &str,
687) -> Option<&'s crate::OwnedSegment> {
688 segments
689 .iter()
690 .find(|s| s.tag == tag && s.element_str(0).unwrap_or("") == qualifier)
691}
692
693pub trait SegmentAccessor<'a> {
695 fn get_element(&'a self, idx: usize) -> Option<&'a str>;
697 fn get_component(&'a self, elem: usize, comp: usize) -> Option<&'a str>;
699 fn get_composite(&'a self, idx: usize) -> Option<CompositeElement<'a>>;
701
702 fn text_element(&'a self, idx: usize) -> Result<&'a str, EdifactError>;
704 fn optional_element(&'a self, idx: usize) -> Option<&'a str>;
706 fn code_element<T: FromStr>(&'a self, idx: usize) -> Result<T, EdifactError>;
708 fn required_composite(&'a self, elem: usize, comp: usize) -> Result<&'a str, EdifactError>;
710 fn component_range(
721 &'a self,
722 elem: usize,
723 start_idx: usize,
724 count: usize,
725 ) -> Result<Vec<&'a str>, EdifactError> {
726 self.component_range_iter(elem, start_idx, count).collect()
729 }
730
731 fn component_range_iter(
736 &'a self,
737 elem: usize,
738 start_idx: usize,
739 count: usize,
740 ) -> impl Iterator<Item = Result<&'a str, EdifactError>> + 'a;
741}
742
743impl<'s, 'd> SegmentAccessor<'s> for Segment<'d>
744where
745 'd: 's,
746{
747 fn get_element(&'s self, idx: usize) -> Option<&'s str> {
748 self.element_str(idx).filter(|s| !s.is_empty())
749 }
750
751 fn get_component(&'s self, elem: usize, comp: usize) -> Option<&'s str> {
752 self.elements
753 .get(elem)
754 .and_then(|e| e.get_component(comp))
755 .filter(|s| !s.is_empty())
756 }
757
758 fn get_composite(&'s self, idx: usize) -> Option<CompositeElement<'s>> {
759 composite_element(self, idx)
760 }
761
762 fn text_element(&'s self, idx: usize) -> Result<&'s str, EdifactError> {
763 <Self as SegmentAccessor>::get_element(self, idx).ok_or_else(|| {
764 EdifactError::MissingRequiredElement {
765 tag: self.tag.to_owned(),
766 element_index: idx,
767 }
768 })
769 }
770
771 fn optional_element(&'s self, idx: usize) -> Option<&'s str> {
772 <Self as SegmentAccessor>::get_element(self, idx)
773 }
774
775 fn code_element<T: FromStr>(&'s self, idx: usize) -> Result<T, EdifactError> {
776 let raw = self.text_element(idx)?;
777 raw.parse::<T>().map_err(|_| EdifactError::InvalidText {
778 offset: self
779 .element_span(idx)
780 .map(|s| s.start)
781 .unwrap_or(self.span.start),
782 })
783 }
784
785 fn required_composite(&'s self, elem: usize, comp: usize) -> Result<&'s str, EdifactError> {
786 match self.elements.get(elem) {
787 None => Err(EdifactError::MissingRequiredElement {
788 tag: self.tag.to_owned(),
789 element_index: elem,
790 }),
791 Some(e) => e
792 .get_component(comp)
793 .filter(|s| !s.is_empty())
794 .ok_or_else(|| EdifactError::MissingRequiredComponent {
795 tag: self.tag.to_owned(),
796 element_index: elem,
797 component_index: comp,
798 }),
799 }
800 }
801
802 fn component_range_iter(
803 &'s self,
804 elem: usize,
805 start_idx: usize,
806 count: usize,
807 ) -> impl Iterator<Item = Result<&'s str, EdifactError>> + 's {
808 let tag = self.tag;
809 let element_exists = self.elements.get(elem).is_some();
810 let components = self
811 .elements
812 .get(elem)
813 .map(|e| e.components.as_slice())
814 .unwrap_or(&[]);
815 (start_idx..start_idx + count).map(move |idx| {
816 components
817 .get(idx)
818 .map(|(c, _)| c.as_ref())
819 .filter(|s| !s.is_empty())
820 .ok_or_else(|| {
821 if element_exists {
822 EdifactError::MissingRequiredComponent {
823 tag: tag.to_owned(),
824 element_index: elem,
825 component_index: idx,
826 }
827 } else {
828 EdifactError::MissingRequiredElement {
829 tag: tag.to_owned(),
830 element_index: elem,
831 }
832 }
833 })
834 })
835 }
836}
837
838#[derive(Debug)]
860pub struct MessageWindow<'a> {
861 pub message_type: Option<Cow<'a, str>>,
866 pub association_code: Option<Cow<'a, str>>,
871 pub segments: Vec<crate::Segment<'a>>,
873}
874
875impl<'a> MessageWindow<'a> {
876 #[must_use]
904 pub fn body(&self) -> &[crate::Segment<'a>] {
905 body_of(&self.segments, |segment| segment.tag)
906 }
907
908 fn from_segments(segments: Vec<crate::Segment<'a>>) -> Self {
914 let message_type = segments
915 .first()
916 .filter(|s| s.tag == "UNH")
917 .and_then(|unh| unh_component(unh, 0));
918 let association_code = segments
919 .first()
920 .filter(|s| s.tag == "UNH")
921 .and_then(|unh| unh_component(unh, 4));
922 Self {
923 message_type,
924 association_code,
925 segments,
926 }
927 }
928}
929
930fn unh_component<'a, 'b>(seg: &'b crate::Segment<'a>, comp_idx: usize) -> Option<Cow<'a, str>>
938where
939 'a: 'b,
940{
941 seg.elements
942 .get(1)
943 .and_then(|e| e.components.get(comp_idx))
944 .and_then(|(c, _)| if c.is_empty() { None } else { Some(c.clone()) })
945}
946
947fn body_of<S>(segments: &[S], tag: impl Fn(&S) -> &str) -> &[S] {
952 let start = usize::from(segments.first().is_some_and(|s| tag(s) == "UNH"));
953 let end = segments.len().saturating_sub(usize::from(
954 segments.last().is_some_and(|s| tag(s) == "UNT"),
955 ));
956 segments.get(start..end).unwrap_or(&[])
957}
958
959#[derive(Debug, Clone)]
968pub struct OwnedMessageWindow {
969 pub message_type: Option<String>,
971 pub association_code: Option<String>,
973 pub segments: Vec<crate::OwnedSegment>,
975}
976
977impl OwnedMessageWindow {
978 #[must_use]
982 pub fn body(&self) -> &[crate::OwnedSegment] {
983 body_of(&self.segments, |segment| segment.tag.as_str())
984 }
985
986 fn from_segments(segments: Vec<crate::OwnedSegment>) -> Self {
987 let unh = segments.first().filter(|s| s.tag == "UNH");
988 let message_type = unh
989 .and_then(|s| s.elements.get(1))
990 .and_then(|e| e.components.first())
991 .map(|(c, _)| c.as_str())
992 .filter(|s| !s.is_empty())
993 .map(str::to_owned);
994 let association_code = unh
995 .and_then(|s| s.elements.get(1))
996 .and_then(|e| e.components.get(4))
997 .map(|(c, _)| c.as_str())
998 .filter(|s| !s.is_empty())
999 .map(str::to_owned);
1000 Self {
1001 message_type,
1002 association_code,
1003 segments,
1004 }
1005 }
1006}
1007
1008pub struct MessageWindowsSliceIter<'a> {
1018 inner: crate::FromBytesIter<'a>,
1019 buf: Vec<crate::Segment<'a>>,
1020 in_message: bool,
1021 done: bool,
1022}
1023
1024impl<'a> MessageWindowsSliceIter<'a> {
1025 fn new(inner: crate::FromBytesIter<'a>) -> Self {
1026 Self {
1027 inner,
1028 buf: Vec::new(),
1029 in_message: false,
1030 done: false,
1031 }
1032 }
1033}
1034
1035impl<'a> Iterator for MessageWindowsSliceIter<'a> {
1036 type Item = Result<MessageWindow<'a>, EdifactError>;
1037
1038 fn next(&mut self) -> Option<Self::Item> {
1039 if self.done {
1040 return None;
1041 }
1042 loop {
1043 let segment = match self.inner.next() {
1044 Some(Ok(s)) => s,
1045 Some(Err(e)) => {
1046 self.done = true;
1047 return Some(Err(e));
1048 }
1049 None => {
1050 self.done = true;
1051 if self.in_message && !self.buf.is_empty() {
1052 self.in_message = false;
1053 let offset = self.buf.last().map(|s| s.span.end).unwrap_or(0);
1054 return Some(Err(EdifactError::UnexpectedEof { offset }));
1055 }
1056 return None;
1057 }
1058 };
1059
1060 match segment.tag {
1061 "UNH" => {
1062 if self.in_message {
1063 self.buf.clear();
1064 self.in_message = false;
1065 self.done = true;
1066 return Some(Err(EdifactError::InvalidSegmentForMessage {
1067 tag: "UNH".to_owned(),
1068 message_type: "ENVELOPE".to_owned(),
1069 span: segment.span,
1070 }));
1071 }
1072 self.buf.clear();
1073 self.in_message = true;
1074 self.buf.push(segment);
1075 }
1076 "UNT" if self.in_message => {
1077 self.buf.push(segment);
1078 self.in_message = false;
1079 let segments = std::mem::take(&mut self.buf);
1080 return Some(Ok(MessageWindow::from_segments(segments)));
1081 }
1082 _ if self.in_message => {
1083 self.buf.push(segment);
1084 }
1085 _ => {
1086 }
1088 }
1089 }
1090 }
1091}
1092
1093pub struct MessageWindowsIter<I> {
1114 inner: I,
1115 buf: Vec<crate::OwnedSegment>,
1116 in_message: bool,
1117 done: bool,
1120}
1121
1122impl<I: Iterator<Item = Result<crate::OwnedSegment, EdifactError>>> MessageWindowsIter<I> {
1123 pub fn new(inner: I) -> Self {
1125 Self {
1126 inner,
1127 buf: Vec::new(),
1128 in_message: false,
1129 done: false,
1130 }
1131 }
1132}
1133
1134impl<I: Iterator<Item = Result<crate::OwnedSegment, EdifactError>>> Iterator
1135 for MessageWindowsIter<I>
1136{
1137 type Item = Result<OwnedMessageWindow, EdifactError>;
1138
1139 fn next(&mut self) -> Option<Self::Item> {
1140 if self.done {
1141 return None;
1142 }
1143 loop {
1144 let segment = match self.inner.next() {
1145 Some(Ok(s)) => s,
1146 Some(Err(e)) => {
1147 self.done = true;
1148 return Some(Err(e));
1149 }
1150 None => {
1151 self.done = true;
1152 if self.in_message && !self.buf.is_empty() {
1155 self.in_message = false;
1156 let offset = self.buf.last().map(|s| s.span.end).unwrap_or(0);
1157 return Some(Err(EdifactError::UnexpectedEof { offset }));
1158 }
1159 return None;
1160 }
1161 };
1162
1163 match segment.tag.as_str() {
1164 "UNH" => {
1165 if self.in_message {
1166 self.buf.clear();
1168 self.in_message = false;
1169 self.done = true;
1170 return Some(Err(EdifactError::InvalidSegmentForMessage {
1171 tag: "UNH".to_owned(),
1172 message_type: "ENVELOPE".to_owned(),
1173 span: segment.span,
1174 }));
1175 }
1176 self.buf.clear();
1177 self.in_message = true;
1178 self.buf.push(segment);
1179 }
1180 "UNT" if self.in_message => {
1181 self.buf.push(segment);
1182 self.in_message = false;
1183 let segments = std::mem::take(&mut self.buf);
1184 return Some(Ok(OwnedMessageWindow::from_segments(segments)));
1185 }
1186 _ if self.in_message => {
1187 self.buf.push(segment);
1188 }
1189 _ => {
1190 }
1192 }
1193 }
1194 }
1195}
1196
1197pub fn message_windows_bytes(input: &[u8]) -> MessageWindowsSliceIter<'_> {
1226 MessageWindowsSliceIter::new(crate::from_bytes(input))
1227}
1228
1229pub fn message_windows_from_reader<R: Read>(
1235 reader: R,
1236) -> MessageWindowsIter<crate::FromReaderIter<R>> {
1237 MessageWindowsIter::new(crate::from_reader(reader))
1238}
1239
1240pub fn deserialize_messages_from_reader<T, R>(
1259 reader: R,
1260) -> impl Iterator<Item = Result<T, EdifactError>>
1261where
1262 T: EdifactDeserialize,
1263 R: Read,
1264{
1265 message_windows_from_reader(reader).map(|window| {
1266 let window = window?;
1267 T::edifact_deserialize_owned(&window.segments)
1268 })
1269}
1270
1271pub fn deserialize_messages_bytes<T>(
1273 input: &[u8],
1274) -> impl Iterator<Item = Result<T, EdifactError>> + '_
1275where
1276 T: EdifactDeserialize,
1277{
1278 message_windows_bytes(input).map(|window| {
1279 let window = window?;
1280 T::edifact_deserialize(&window.segments)
1281 })
1282}
1283
1284pub struct DispatchedMessage {
1288 pub message_type: String,
1290 value: Box<dyn std::any::Any + Send + Sync>,
1291}
1292
1293impl DispatchedMessage {
1294 pub fn downcast<T: std::any::Any + Send + Sync + 'static>(&self) -> Option<&T> {
1298 self.value.downcast_ref::<T>()
1299 }
1300}
1301
1302impl std::fmt::Debug for DispatchedMessage {
1303 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1304 f.debug_struct("DispatchedMessage")
1305 .field("message_type", &self.message_type)
1306 .finish_non_exhaustive()
1307 }
1308}
1309
1310type DispatchHandlerFn = Box<
1311 dyn for<'a> Fn(&[Segment<'a>]) -> Result<Box<dyn std::any::Any + Send + Sync>, EdifactError>
1312 + Send
1313 + Sync,
1314>;
1315
1316type FallbackHandlerFn = Box<
1317 dyn for<'a> Fn(
1318 &[Segment<'a>],
1319 &str,
1320 ) -> Result<Box<dyn std::any::Any + Send + Sync>, EdifactError>
1321 + Send
1322 + Sync,
1323>;
1324
1325pub struct MessageDispatch {
1350 handlers: Vec<(String, DispatchHandlerFn)>,
1351 fallback: Option<FallbackHandlerFn>,
1352}
1353
1354impl Default for MessageDispatch {
1355 fn default() -> Self {
1356 Self::new()
1357 }
1358}
1359
1360impl MessageDispatch {
1361 pub fn new() -> Self {
1363 Self {
1364 handlers: Vec::new(),
1365 fallback: None,
1366 }
1367 }
1368
1369 pub fn on<T, F>(mut self, message_type: &str, handler: F) -> Self
1374 where
1375 T: std::any::Any + Send + Sync + 'static,
1376 F: for<'a> Fn(&[Segment<'a>]) -> Result<T, EdifactError> + Send + Sync + 'static,
1377 {
1378 let erased: DispatchHandlerFn = Box::new(move |segs| {
1379 let val = handler(segs)?;
1380 Ok(Box::new(val) as Box<dyn std::any::Any + Send + Sync>)
1381 });
1382 self.handlers.push((message_type.to_owned(), erased));
1383 self
1384 }
1385
1386 pub fn fallback<T, F>(mut self, handler: F) -> Self
1391 where
1392 T: std::any::Any + Send + Sync + 'static,
1393 F: for<'a> Fn(&[Segment<'a>], &str) -> Result<T, EdifactError> + Send + Sync + 'static,
1394 {
1395 let erased: FallbackHandlerFn = Box::new(move |segs, mt| {
1396 let val = handler(segs, mt)?;
1397 Ok(Box::new(val) as Box<dyn std::any::Any + Send + Sync>)
1398 });
1399 self.fallback = Some(erased);
1400 self
1401 }
1402
1403 pub fn dispatch(&self, window: &[Segment<'_>]) -> Result<DispatchedMessage, EdifactError> {
1408 let message_type = window
1409 .iter()
1410 .find(|s| s.tag == "UNH")
1411 .and_then(|unh| unh.get_element(1))
1412 .and_then(|e| e.get_component(0))
1413 .map(|s| s.to_owned())
1414 .ok_or_else(|| EdifactError::MissingSegment {
1415 tag: "UNH".to_owned(),
1416 expected_position: "first segment of message window".to_owned(),
1417 })?;
1418
1419 for (mt, handler) in &self.handlers {
1420 if *mt == message_type {
1421 let value = handler(window)?;
1422 return Ok(DispatchedMessage {
1423 message_type,
1424 value,
1425 });
1426 }
1427 }
1428
1429 if let Some(fallback) = &self.fallback {
1430 let value = fallback(window, &message_type)?;
1431 return Ok(DispatchedMessage {
1432 message_type,
1433 value,
1434 });
1435 }
1436
1437 Err(EdifactError::UnexpectedMessageType { message_type })
1438 }
1439
1440 pub fn dispatch_all_from_bytes<'a>(
1445 &'a self,
1446 input: &'a [u8],
1447 ) -> impl Iterator<Item = Result<DispatchedMessage, EdifactError>> + 'a {
1448 message_windows_bytes(input).map(move |window| {
1449 let window = window?;
1450 self.dispatch(&window.segments)
1451 })
1452 }
1453
1454 pub fn dispatch_all_from_reader<R: Read + 'static>(
1461 &self,
1462 reader: R,
1463 ) -> impl Iterator<Item = Result<DispatchedMessage, EdifactError>> + '_ {
1464 message_windows_from_reader(reader).map(|window| {
1465 let window = window?;
1466 let borrowed: Vec<Segment<'_>> =
1467 window.segments.iter().map(|s| s.as_borrowed()).collect();
1468 self.dispatch(&borrowed)
1469 })
1470 }
1471}
1472
1473#[cfg(test)]
1474mod tests {
1475 use super::*;
1476
1477 #[derive(Debug, PartialEq)]
1479 struct BgmSegment {
1480 doc_name_code: String,
1481 pruef_id: String,
1482 msg_function: Option<String>,
1483 }
1484
1485 impl EdifactSegmentTag for BgmSegment {
1486 const SEGMENT_TAG: &'static str = "BGM";
1487 }
1488
1489 struct NadM;
1490
1491 impl EdifactSegmentTag for NadM {
1492 const SEGMENT_TAG: &'static str = "NAD";
1493 const QUALIFIER_PATTERN: Option<&'static str> = Some("M*");
1494 }
1495
1496 struct NadWildcard;
1497
1498 impl EdifactSegmentTag for NadWildcard {
1499 const SEGMENT_TAG: &'static str = "NAD";
1500 const QUALIFIER_PATTERN: Option<&'static str> = Some("M*");
1501 }
1502
1503 impl EdifactDeserialize for BgmSegment {
1504 fn edifact_deserialize(segments: &[Segment<'_>]) -> Result<Self, EdifactError> {
1505 let seg = find_segment(segments, "BGM").ok_or_else(|| {
1506 EdifactError::MissingRequiredElement {
1507 tag: "BGM".to_owned(),
1508 element_index: 0,
1509 }
1510 })?;
1511 Ok(Self {
1512 doc_name_code: element_str(seg, 0).to_owned(),
1513 pruef_id: element_str(seg, 1).to_owned(),
1514 msg_function: seg
1515 .element_str(2)
1516 .filter(|s| !s.is_empty())
1517 .map(str::to_owned),
1518 })
1519 }
1520 }
1521
1522 #[test]
1523 fn deserialize_single_segment() {
1524 let input = b"BGM+E03+11042+9'";
1525 let bgm: BgmSegment = deserialize(input).unwrap();
1526 assert_eq!(bgm.doc_name_code, "E03");
1527 assert_eq!(bgm.pruef_id, "11042");
1528 assert_eq!(bgm.msg_function, Some("9".to_owned()));
1529 }
1530
1531 #[test]
1532 fn streaming_deserialize_first_from_bytes() {
1533 let input = b"UNH+1+ORDERS:D:11A:UN'BGM+E03+11042+9'UNT+3+1'";
1534 let bgm: BgmSegment = deserialize_first_streaming(input).unwrap();
1535 assert_eq!(bgm.pruef_id, "11042");
1536 }
1537
1538 #[test]
1539 fn streaming_deserialize_all_from_bytes() {
1540 let input = b"BGM+E03+11042+9'RFF+AA:1'BGM+E01+11043+9'";
1541 let bgms: Vec<BgmSegment> = deserialize_all_streaming(input).unwrap();
1542 assert_eq!(bgms.len(), 2);
1543 assert_eq!(bgms[0].pruef_id, "11042");
1544 assert_eq!(bgms[1].pruef_id, "11043");
1545 }
1546
1547 #[test]
1548 fn streaming_deserialize_first_from_reader() {
1549 let input =
1550 std::io::Cursor::new(b"UNH+1+ORDERS:D:11A:UN'BGM+E03+11042+9'UNT+3+1'".to_vec());
1551 let bgm: BgmSegment = deserialize_first_from_reader(input).unwrap();
1552 assert_eq!(bgm.pruef_id, "11042");
1553 }
1554
1555 #[test]
1556 fn streaming_deserialize_all_from_reader() {
1557 let input = std::io::Cursor::new(b"BGM+E03+11042+9'BGM+E01+11043+9'".to_vec());
1558 let bgms: Vec<BgmSegment> = deserialize_all_from_reader(input).unwrap();
1559 assert_eq!(bgms.len(), 2);
1560 assert_eq!(bgms[0].pruef_id, "11042");
1561 assert_eq!(bgms[1].pruef_id, "11043");
1562 }
1563
1564 #[test]
1565 fn missing_segment_returns_error() {
1566 let input = b"DTM+137:20230401:102'";
1567 let result: Result<BgmSegment, _> = deserialize(input);
1568 assert!(result.is_err());
1569 }
1570
1571 #[test]
1572 fn vec_collects_all_matching_segments() {
1573 let input = b"DTM+137:20230401:102'BGM+E03+11042+9'BGM+E01+11043+9'";
1574 let bgms: Vec<BgmSegment> = deserialize(input).unwrap();
1575 assert_eq!(bgms.len(), 2);
1576 assert_eq!(bgms[0].pruef_id, "11042");
1577 assert_eq!(bgms[1].pruef_id, "11043");
1578 }
1579
1580 #[test]
1581 fn find_qualified_segment_matches_qualifier() {
1582 let input = b"NAD+MS+9900001+293'NAD+MR+9900002+293'";
1583 let segments: Vec<Segment<'_>> =
1584 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1585 let nad_ms = find_qualified_segment(&segments, "NAD", "MS");
1586 let nad_mr = find_qualified_segment(&segments, "NAD", "MR");
1587 assert!(nad_ms.is_some());
1588 assert!(nad_mr.is_some());
1589 assert_eq!(element_str(nad_ms.unwrap(), 0), "MS");
1590 assert_eq!(element_str(nad_mr.unwrap(), 0), "MR");
1591 }
1592
1593 #[test]
1594 fn round_trip_str_api() {
1595 let input = "BGM+E03+11042+9'";
1596 let bgm: BgmSegment = deserialize_str(input).unwrap();
1597 assert_eq!(bgm.pruef_id, "11042");
1598 }
1599
1600 #[test]
1601 fn required_element_extraction() {
1602 let input = b"BGM+E03+11042+9'";
1603 let segments: Vec<Segment<'_>> =
1604 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1605 let seg = &segments[0];
1606
1607 assert_eq!(required_element(seg, 0).unwrap(), "E03");
1608 assert_eq!(required_element(seg, 1).unwrap(), "11042");
1609 assert!(required_element(seg, 5).is_err());
1611 }
1612
1613 #[test]
1614 fn optional_element_extraction() {
1615 let input = b"BGM+E03+11042+9'BGM+E01++absent'";
1616 let segments: Vec<Segment<'_>> =
1617 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1618
1619 assert_eq!(optional_element(&segments[0], 0), Some("E03"));
1621 assert_eq!(optional_element(&segments[0], 1), Some("11042"));
1622 assert_eq!(optional_element(&segments[0], 5), None);
1623
1624 assert_eq!(optional_element(&segments[1], 1), None);
1626 }
1627
1628 #[test]
1629 fn component_extraction() {
1630 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1631 let segments: Vec<Segment<'_>> =
1632 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1633 let seg = &segments[0];
1634
1635 assert_eq!(required_component(seg, 0, 0).unwrap(), "UNOA");
1636 assert_eq!(required_component(seg, 0, 1).unwrap(), "1");
1637 assert!(required_component(seg, 0, 5).is_err());
1639 }
1640
1641 #[test]
1642 fn composite_element_helper() {
1643 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1644 let segments: Vec<Segment<'_>> =
1645 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1646 let seg = &segments[0];
1647
1648 let comp = composite_element(seg, 0).unwrap();
1649 assert_eq!(comp.len(), 2);
1650 assert_eq!(comp.get(0), Some("UNOA"));
1651 assert_eq!(comp.get(1), Some("1"));
1652 assert_eq!(comp.get(5), None);
1653 assert_eq!(comp.get_or_empty(5), "");
1654 }
1655
1656 #[test]
1657 fn get_all_components() {
1658 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1660 let segments: Vec<Segment<'_>> =
1661 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1662 let seg = &segments[0];
1663
1664 let comps: Vec<&str> = get_components_iter(seg, 0).collect(); assert!(!comps.is_empty(), "Expected components but got empty");
1666 assert_eq!(comps.len(), 2);
1667 assert_eq!(comps[0], "UNOA");
1668 assert_eq!(comps[1], "1");
1669 }
1670
1671 #[test]
1672 fn qualifier_pattern_matching_supports_exact_and_wildcard() {
1673 assert!(qualifier_matches_pattern("MS", "MS"));
1675 assert!(!qualifier_matches_pattern("MS", "M")); assert!(qualifier_matches_pattern("MS", "M*"));
1678 assert!(qualifier_matches_pattern("MRY", "M*Y"));
1679 assert!(!qualifier_matches_pattern("AB", "M*"));
1680 }
1681
1682 #[test]
1684 fn qualifier_matches_pattern_table() {
1685 let cases: &[(&str, &str, bool)] = &[
1687 ("", "", true), ("", "*", true), ("A", "", false), ("", "A", false), ("MS", "MS", true),
1694 ("BY", "BY", true),
1695 ("ms", "MS", false), ("MSX", "MS", false), ("M", "MS", false), ("MS", "M*", true),
1700 ("MULTI", "MUL*", true),
1701 ("AB", "M*", false),
1702 ("", "M*", false), ("MSG", "*G", true),
1705 ("G", "*G", true),
1706 ("MSG", "*X", false),
1707 ("", "*G", false),
1708 ("MRY", "M*Y", true),
1710 ("MAY", "M*Y", true),
1711 ("MY", "M*Y", true), ("MYY", "M*Y", true), ("MAYZ", "M*Y", false), ("AB", "M*Y", false),
1715 ("*", "*", true), ("anything", "*", true),
1718 ("", "*", true),
1719 ("ABCDE", "A*C*E", true),
1721 ("ACE", "A*C*E", true), ("AXCYE", "A*C*E", true),
1723 ("ABCDF", "A*C*E", false),
1724 ("AB", "A**B", true), ("AB", "A*B*C", false),
1728 ("XMS", "MS", false),
1730 ];
1731
1732 for (value, pattern, expected) in cases {
1733 let got = qualifier_matches_pattern(value, pattern);
1734 assert_eq!(
1735 got, *expected,
1736 "qualifier_matches_pattern({value:?}, {pattern:?}) expected {expected} but got {got}"
1737 );
1738 }
1739 }
1740
1741 #[test]
1742 fn typed_qualifier_helpers_work() {
1743 let input = b"NAD+MS+9900001+293'NAD+MR+9900002+293'";
1744 let segments: Vec<Segment<'_>> =
1745 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1746
1747 let first = find_segment_typed::<NadM>(&segments).unwrap();
1748 assert_eq!(first.element_str(0), Some("MS"));
1749
1750 let all: Vec<_> = find_segments_typed::<NadWildcard>(&segments).collect();
1751 assert_eq!(all.len(), 2);
1752 }
1753
1754 #[test]
1755 fn segment_accessor_trait_methods_work() {
1756 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1757 let segments: Vec<Segment<'_>> =
1758 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1759 let seg = &segments[0];
1760
1761 assert_eq!(SegmentAccessor::get_element(seg, 1), Some("SENDER"));
1762 assert_eq!(SegmentAccessor::required_composite(seg, 0, 1).unwrap(), "1");
1763 let parsed: i32 = SegmentAccessor::code_element(seg, 4).unwrap();
1764 assert_eq!(parsed, 1);
1765 let reps = SegmentAccessor::component_range(seg, 3, 0, 2).unwrap();
1766 assert_eq!(reps, vec!["200101", "0900"]);
1767 }
1768
1769 #[test]
1770 fn group_helpers_detect_contiguity() {
1771 struct NadAny;
1772 impl EdifactSegmentTag for NadAny {
1773 const SEGMENT_TAG: &'static str = "NAD";
1774 }
1775
1776 let contiguous_input = b"NAD+MS+1'NAD+MR+2'RFF+AA:1'";
1777 let contiguous_segments: Vec<Segment<'_>> = crate::from_bytes(contiguous_input)
1778 .collect::<Result<_, _>>()
1779 .unwrap();
1780 assert!(groups_are_contiguous_by_qualifier::<NadAny>(
1781 &contiguous_segments
1782 ));
1783
1784 let non_contiguous_input = b"NAD+MS+1'RFF+AA:1'NAD+MR+2'";
1785 let non_contiguous_segments: Vec<Segment<'_>> = crate::from_bytes(non_contiguous_input)
1786 .collect::<Result<_, _>>()
1787 .unwrap();
1788 assert!(!groups_are_contiguous_by_qualifier::<NadAny>(
1789 &non_contiguous_segments
1790 ));
1791 }
1792
1793 #[test]
1794 fn group_helpers_collect_contiguous_groups() {
1795 struct NadAny;
1796 impl EdifactSegmentTag for NadAny {
1797 const SEGMENT_TAG: &'static str = "NAD";
1798 }
1799
1800 let input = b"NAD+MS+1'NAD+MR+2'RFF+AA:1'NAD+BY+3'";
1801 let segments: Vec<Segment<'_>> =
1802 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1803 let groups = contiguous_groups_by_qualifier::<NadAny>(&segments);
1804
1805 assert_eq!(groups.len(), 2);
1806 assert_eq!(groups[0].len(), 2);
1807 assert_eq!(groups[1].len(), 1);
1808 }
1809
1810 #[test]
1813 fn message_windows_bytes_yields_complete_windows() {
1814 let input = b"UNB+UNOA:1+S+R+200101:0900+1'\
1815 UNH+1+ORDERS:D:96A:UN'\
1816 BGM+220+PO-001+9'\
1817 UNT+3+1'\
1818 UNZ+1+1'";
1819 let windows: Vec<_> = message_windows_bytes(input)
1820 .collect::<Result<_, _>>()
1821 .unwrap();
1822 assert_eq!(windows.len(), 1);
1823 assert_eq!(windows[0].segments[0].tag, "UNH");
1824 assert_eq!(windows[0].segments.last().unwrap().tag, "UNT");
1825 assert_eq!(windows[0].message_type.as_deref(), Some("ORDERS"));
1826 assert_eq!(windows[0].association_code.as_deref(), None);
1827 }
1828
1829 #[test]
1830 fn message_windows_bytes_preserves_owned_unh_metadata() {
1831 let input = b"UNB+UNOA:1+S+R+200101:0900+1'\
1832 UNH+1+ORD?ERS:D:96A:UN:5??5??3a'\
1833 BGM+220+PO-001+9'\
1834 UNT+3+1'\
1835 UNZ+1+1'";
1836 let windows: Vec<_> = message_windows_bytes(input)
1837 .collect::<Result<_, _>>()
1838 .unwrap();
1839
1840 assert_eq!(windows.len(), 1);
1841 assert_eq!(windows[0].message_type.as_deref(), Some("ORDERS"));
1842 assert_eq!(windows[0].association_code.as_deref(), Some("5?5?3a"));
1843 }
1844
1845 #[test]
1846 fn message_windows_truncated_stream_returns_error() {
1847 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'";
1849 let results: Vec<_> = message_windows_bytes(input).collect();
1850 assert_eq!(results.len(), 1);
1851 assert!(
1852 matches!(results[0], Err(EdifactError::UnexpectedEof { .. })),
1853 "expected UnexpectedEof for truncated window, got: {:?}",
1854 results[0]
1855 );
1856 }
1857
1858 #[test]
1859 fn message_windows_subsequent_calls_return_none_after_truncation() {
1860 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'";
1861 let mut iter = message_windows_bytes(input);
1862 assert!(matches!(
1863 iter.next(),
1864 Some(Err(EdifactError::UnexpectedEof { .. }))
1865 ));
1866 assert!(iter.next().is_none());
1868 }
1869
1870 #[test]
1871 fn message_windows_unh_without_unt_before_next_unh_returns_error() {
1872 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'\
1873 UNH+2+ORDERS:D:96A:UN'BGM+220+PO-002+9'UNT+3+2'";
1874 let results: Vec<_> = message_windows_bytes(input).collect();
1875 assert!(
1877 matches!(
1878 results[0],
1879 Err(EdifactError::InvalidSegmentForMessage { ref tag, .. }) if tag == "UNH"
1880 ),
1881 "expected InvalidSegmentForMessage(UNH), got: {:?}",
1882 results[0]
1883 );
1884 }
1885
1886 fn parse_one(input: &str) -> crate::OwnedSegment {
1889 crate::from_reader_collect(std::io::Cursor::new(input.as_bytes()))
1890 .expect("parse failed")
1891 .into_iter()
1892 .next()
1893 .expect("at least one segment")
1894 }
1895
1896 #[test]
1897 fn segment_accessor_get_element_returns_value() {
1898 let owned = parse_one("BGM+220+PO-001+9'");
1899 let seg = owned.as_borrowed();
1900 assert_eq!(SegmentAccessor::get_element(&seg, 0), Some("220"));
1901 assert_eq!(SegmentAccessor::get_element(&seg, 1), Some("PO-001"));
1902 assert_eq!(SegmentAccessor::get_element(&seg, 2), Some("9"));
1903 assert_eq!(
1904 SegmentAccessor::get_element(&seg, 9),
1905 None,
1906 "out-of-bounds must return None"
1907 );
1908 }
1909
1910 #[test]
1911 fn segment_accessor_get_element_filters_empty() {
1912 let owned = parse_one("TST+++VALUE'");
1913 let seg = owned.as_borrowed();
1914 assert_eq!(
1916 SegmentAccessor::get_element(&seg, 0),
1917 None,
1918 "empty element must return None"
1919 );
1920 assert_eq!(
1921 SegmentAccessor::get_element(&seg, 1),
1922 None,
1923 "empty element must return None"
1924 );
1925 assert_eq!(SegmentAccessor::get_element(&seg, 2), Some("VALUE"));
1926 }
1927
1928 #[test]
1929 fn segment_accessor_get_component_returns_value() {
1930 let owned = parse_one("UNH+1+ORDERS:D:96A:UN'");
1931 let seg = owned.as_borrowed();
1932 assert_eq!(seg.get_component(1, 0), Some("ORDERS"));
1933 assert_eq!(seg.get_component(1, 1), Some("D"));
1934 assert_eq!(seg.get_component(1, 2), Some("96A"));
1935 assert_eq!(seg.get_component(1, 3), Some("UN"));
1936 assert_eq!(
1937 seg.get_component(1, 9),
1938 None,
1939 "out-of-bounds must return None"
1940 );
1941 }
1942
1943 #[test]
1944 fn segment_accessor_text_element_errors_on_missing() {
1945 let owned = parse_one("BGM+'");
1946 let seg = owned.as_borrowed();
1947 let err = seg.text_element(0);
1949 assert!(
1950 matches!(err, Err(EdifactError::MissingRequiredElement { ref tag, element_index: 0 }) if tag == "BGM"),
1951 "expected MissingRequiredElement, got: {err:?}"
1952 );
1953 }
1954
1955 #[test]
1956 fn segment_accessor_required_composite_errors_on_missing() {
1957 let owned = parse_one("DTM+137'");
1958 let seg = owned.as_borrowed();
1959 let err = seg.required_composite(0, 1);
1961 assert!(
1962 matches!(err, Err(EdifactError::MissingRequiredComponent { ref tag, element_index: 0, component_index: 1 }) if tag == "DTM"),
1963 "expected MissingRequiredComponent, got: {err:?}"
1964 );
1965 }
1966
1967 #[test]
1968 fn segment_accessor_code_element_parses_integer() {
1969 let owned = parse_one("QTY+21:100'");
1970 let seg = owned.as_borrowed();
1971 let qty: u32 = seg.code_element(0).expect("should parse qualifier as u32");
1972 assert_eq!(qty, 21);
1973 }
1974
1975 #[test]
1976 fn segment_accessor_optional_element_absent_returns_none() {
1977 let owned = parse_one("BGM+220'");
1978 let seg = owned.as_borrowed();
1979 assert_eq!(seg.optional_element(5), None);
1980 }
1981}