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('*') {
410 if !pattern[prefix.len() + 1..].contains('*') {
412 return value.len() >= prefix.len() + suffix.len()
413 && value.starts_with(prefix)
414 && value.ends_with(suffix)
415 && {
416 let mid_start = prefix.len();
418 let mid_end = value.len().saturating_sub(suffix.len());
419 mid_start <= mid_end
420 };
421 }
422 }
423
424 let parts: smallvec::SmallVec<[&str; 4]> = pattern.split('*').collect();
426
427 if parts.len() > 4 {
432 return false;
433 }
434
435 let prefix = parts[0];
436 let suffix = parts[parts.len() - 1];
437
438 if !value.starts_with(prefix) || !value.ends_with(suffix) {
439 return false;
440 }
441
442 let mid_start = prefix.len();
443 let mid_end = value.len().saturating_sub(suffix.len());
444
445 if mid_start > mid_end {
446 return parts[1..parts.len() - 1].iter().all(|p| p.is_empty());
447 }
448
449 let mut remaining = &value[mid_start..mid_end];
450
451 for part in &parts[1..parts.len() - 1] {
452 if part.is_empty() {
453 continue;
454 }
455 match remaining.find(part) {
456 Some(idx) => remaining = &remaining[idx + part.len()..],
457 None => return false,
458 }
459 }
460
461 true
462}
463
464#[inline]
466pub fn element_str<'s>(seg: &'s Segment<'_>, idx: usize) -> &'s str {
467 seg.element_str(idx).unwrap_or("")
468}
469
470pub fn required_element<'a>(seg: &'a Segment<'_>, idx: usize) -> Result<&'a str, EdifactError> {
486 seg.text_element(idx)
487}
488
489pub fn optional_element<'a>(seg: &'a Segment<'_>, idx: usize) -> Option<&'a str> {
495 SegmentAccessor::optional_element(seg, idx)
496}
497
498pub fn required_component<'a>(
516 seg: &'a Segment<'_>,
517 elem_idx: usize,
518 comp_idx: usize,
519) -> Result<&'a str, EdifactError> {
520 seg.required_composite(elem_idx, comp_idx)
521}
522
523pub fn optional_component<'a>(
529 seg: &'a Segment<'_>,
530 elem_idx: usize,
531 comp_idx: usize,
532) -> Option<&'a str> {
533 SegmentAccessor::get_component(seg, elem_idx, comp_idx)
534}
535
536pub fn get_components_iter<'a>(seg: &'a Segment<'_>, idx: usize) -> impl Iterator<Item = &'a str> {
540 seg.elements
541 .get(idx)
542 .into_iter()
543 .flat_map(|elem| elem.components.iter().map(|(c, _)| c.as_ref()))
544}
545
546pub struct CompositeElement<'a> {
556 components: smallvec::SmallVec<[&'a str; 4]>,
557}
558
559impl<'a> CompositeElement<'a> {
560 pub fn from_slice(components: &'a [std::borrow::Cow<'a, str>]) -> Self {
564 Self {
565 components: components.iter().map(|c| c.as_ref()).collect(),
566 }
567 }
568
569 pub(crate) fn from_strs(components: smallvec::SmallVec<[&'a str; 4]>) -> Self {
571 Self { components }
572 }
573
574 pub fn get(&self, i: usize) -> Option<&'a str> {
576 self.components.get(i).copied()
577 }
578
579 pub fn get_or_empty(&self, i: usize) -> &'a str {
581 self.get(i).unwrap_or("")
582 }
583
584 pub fn len(&self) -> usize {
586 self.components.len()
587 }
588
589 pub fn is_empty(&self) -> bool {
591 self.components.is_empty()
592 }
593
594 pub fn iter(&self) -> impl Iterator<Item = &'a str> + '_ {
596 self.components.iter().copied()
597 }
598}
599
600pub fn composite_element<'a, 'd: 'a>(
602 seg: &'a Segment<'d>,
603 idx: usize,
604) -> Option<CompositeElement<'a>> {
605 seg.elements.get(idx).map(|elem| {
608 CompositeElement::from_strs(elem.components.iter().map(|(c, _)| c.as_ref()).collect())
609 })
610}
611
612pub fn find_segment_owned<'s>(
619 segments: &'s [crate::OwnedSegment],
620 tag: &str,
621) -> Option<&'s crate::OwnedSegment> {
622 segments.iter().find(|s| s.tag == tag)
623}
624
625pub fn find_qualified_segment_owned<'s>(
633 segments: &'s [crate::OwnedSegment],
634 tag: &str,
635 qualifier: &str,
636) -> Option<&'s crate::OwnedSegment> {
637 segments
638 .iter()
639 .find(|s| s.tag == tag && s.element_str(0).unwrap_or("") == qualifier)
640}
641
642pub trait SegmentAccessor<'a> {
644 fn get_element(&'a self, idx: usize) -> Option<&'a str>;
646 fn get_component(&'a self, elem: usize, comp: usize) -> Option<&'a str>;
648 fn get_composite(&'a self, idx: usize) -> Option<CompositeElement<'a>>;
650
651 fn text_element(&'a self, idx: usize) -> Result<&'a str, EdifactError>;
653 fn optional_element(&'a self, idx: usize) -> Option<&'a str>;
655 fn code_element<T: FromStr>(&'a self, idx: usize) -> Result<T, EdifactError>;
657 fn required_composite(&'a self, elem: usize, comp: usize) -> Result<&'a str, EdifactError>;
659 fn component_range(
670 &'a self,
671 elem: usize,
672 start_idx: usize,
673 count: usize,
674 ) -> Result<Vec<&'a str>, EdifactError> {
675 self.component_range_iter(elem, start_idx, count).collect()
678 }
679
680 fn component_range_iter(
685 &'a self,
686 elem: usize,
687 start_idx: usize,
688 count: usize,
689 ) -> impl Iterator<Item = Result<&'a str, EdifactError>> + 'a;
690}
691
692impl<'s, 'd> SegmentAccessor<'s> for Segment<'d>
693where
694 'd: 's,
695{
696 fn get_element(&'s self, idx: usize) -> Option<&'s str> {
697 self.element_str(idx).filter(|s| !s.is_empty())
698 }
699
700 fn get_component(&'s self, elem: usize, comp: usize) -> Option<&'s str> {
701 self.elements
702 .get(elem)
703 .and_then(|e| e.get_component(comp))
704 .filter(|s| !s.is_empty())
705 }
706
707 fn get_composite(&'s self, idx: usize) -> Option<CompositeElement<'s>> {
708 composite_element(self, idx)
709 }
710
711 fn text_element(&'s self, idx: usize) -> Result<&'s str, EdifactError> {
712 <Self as SegmentAccessor>::get_element(self, idx).ok_or_else(|| {
713 EdifactError::MissingRequiredElement {
714 tag: self.tag.to_owned(),
715 element_index: idx,
716 }
717 })
718 }
719
720 fn optional_element(&'s self, idx: usize) -> Option<&'s str> {
721 <Self as SegmentAccessor>::get_element(self, idx)
722 }
723
724 fn code_element<T: FromStr>(&'s self, idx: usize) -> Result<T, EdifactError> {
725 let raw = self.text_element(idx)?;
726 raw.parse::<T>().map_err(|_| EdifactError::InvalidText {
727 offset: self
728 .element_span(idx)
729 .map(|s| s.start)
730 .unwrap_or(self.span.start),
731 })
732 }
733
734 fn required_composite(&'s self, elem: usize, comp: usize) -> Result<&'s str, EdifactError> {
735 match self.elements.get(elem) {
736 None => Err(EdifactError::MissingRequiredElement {
737 tag: self.tag.to_owned(),
738 element_index: elem,
739 }),
740 Some(e) => e
741 .get_component(comp)
742 .filter(|s| !s.is_empty())
743 .ok_or_else(|| EdifactError::MissingRequiredComponent {
744 tag: self.tag.to_owned(),
745 element_index: elem,
746 component_index: comp,
747 }),
748 }
749 }
750
751 fn component_range_iter(
752 &'s self,
753 elem: usize,
754 start_idx: usize,
755 count: usize,
756 ) -> impl Iterator<Item = Result<&'s str, EdifactError>> + 's {
757 let tag = self.tag;
758 let element_exists = self.elements.get(elem).is_some();
759 let components = self
760 .elements
761 .get(elem)
762 .map(|e| e.components.as_slice())
763 .unwrap_or(&[]);
764 (start_idx..start_idx + count).map(move |idx| {
765 components
766 .get(idx)
767 .map(|(c, _)| c.as_ref())
768 .filter(|s| !s.is_empty())
769 .ok_or_else(|| {
770 if element_exists {
771 EdifactError::MissingRequiredComponent {
772 tag: tag.to_owned(),
773 element_index: elem,
774 component_index: idx,
775 }
776 } else {
777 EdifactError::MissingRequiredElement {
778 tag: tag.to_owned(),
779 element_index: elem,
780 }
781 }
782 })
783 })
784 }
785}
786
787#[derive(Debug)]
809pub struct MessageWindow<'a> {
810 pub message_type: Option<Cow<'a, str>>,
815 pub association_code: Option<Cow<'a, str>>,
820 pub segments: Vec<crate::Segment<'a>>,
822}
823
824impl<'a> MessageWindow<'a> {
825 fn from_segments(segments: Vec<crate::Segment<'a>>) -> Self {
831 let message_type = segments
832 .first()
833 .filter(|s| s.tag == "UNH")
834 .and_then(|unh| unh_component(unh, 0));
835 let association_code = segments
836 .first()
837 .filter(|s| s.tag == "UNH")
838 .and_then(|unh| unh_component(unh, 4));
839 Self {
840 message_type,
841 association_code,
842 segments,
843 }
844 }
845}
846
847fn unh_component<'a, 'b>(seg: &'b crate::Segment<'a>, comp_idx: usize) -> Option<Cow<'a, str>>
855where
856 'a: 'b,
857{
858 seg.elements
859 .get(1)
860 .and_then(|e| e.components.get(comp_idx))
861 .and_then(|(c, _)| if c.is_empty() { None } else { Some(c.clone()) })
862}
863
864#[derive(Debug, Clone)]
873pub struct OwnedMessageWindow {
874 pub message_type: Option<String>,
876 pub association_code: Option<String>,
878 pub segments: Vec<crate::OwnedSegment>,
880}
881
882impl OwnedMessageWindow {
883 fn from_segments(segments: Vec<crate::OwnedSegment>) -> Self {
884 let unh = segments.first().filter(|s| s.tag == "UNH");
885 let message_type = unh
886 .and_then(|s| s.elements.get(1))
887 .and_then(|e| e.components.first())
888 .map(|(c, _)| c.as_str())
889 .filter(|s| !s.is_empty())
890 .map(str::to_owned);
891 let association_code = unh
892 .and_then(|s| s.elements.get(1))
893 .and_then(|e| e.components.get(4))
894 .map(|(c, _)| c.as_str())
895 .filter(|s| !s.is_empty())
896 .map(str::to_owned);
897 Self {
898 message_type,
899 association_code,
900 segments,
901 }
902 }
903}
904
905pub struct MessageWindowsSliceIter<'a> {
915 inner: crate::FromBytesIter<'a>,
916 buf: Vec<crate::Segment<'a>>,
917 in_message: bool,
918 done: bool,
919}
920
921impl<'a> MessageWindowsSliceIter<'a> {
922 fn new(inner: crate::FromBytesIter<'a>) -> Self {
923 Self {
924 inner,
925 buf: Vec::new(),
926 in_message: false,
927 done: false,
928 }
929 }
930}
931
932impl<'a> Iterator for MessageWindowsSliceIter<'a> {
933 type Item = Result<MessageWindow<'a>, EdifactError>;
934
935 fn next(&mut self) -> Option<Self::Item> {
936 if self.done {
937 return None;
938 }
939 loop {
940 let segment = match self.inner.next() {
941 Some(Ok(s)) => s,
942 Some(Err(e)) => {
943 self.done = true;
944 return Some(Err(e));
945 }
946 None => {
947 self.done = true;
948 if self.in_message && !self.buf.is_empty() {
949 self.in_message = false;
950 let offset = self.buf.last().map(|s| s.span.end).unwrap_or(0);
951 return Some(Err(EdifactError::UnexpectedEof { offset }));
952 }
953 return None;
954 }
955 };
956
957 match segment.tag {
958 "UNH" => {
959 if self.in_message {
960 self.buf.clear();
961 self.in_message = false;
962 self.done = true;
963 return Some(Err(EdifactError::InvalidSegmentForMessage {
964 tag: "UNH".to_owned(),
965 message_type: "ENVELOPE".to_owned(),
966 span: segment.span,
967 }));
968 }
969 self.buf.clear();
970 self.in_message = true;
971 self.buf.push(segment);
972 }
973 "UNT" if self.in_message => {
974 self.buf.push(segment);
975 self.in_message = false;
976 let segments = std::mem::take(&mut self.buf);
977 return Some(Ok(MessageWindow::from_segments(segments)));
978 }
979 _ if self.in_message => {
980 self.buf.push(segment);
981 }
982 _ => {
983 }
985 }
986 }
987 }
988}
989
990pub struct MessageWindowsIter<I> {
1011 inner: I,
1012 buf: Vec<crate::OwnedSegment>,
1013 in_message: bool,
1014 done: bool,
1017}
1018
1019impl<I: Iterator<Item = Result<crate::OwnedSegment, EdifactError>>> MessageWindowsIter<I> {
1020 pub fn new(inner: I) -> Self {
1022 Self {
1023 inner,
1024 buf: Vec::new(),
1025 in_message: false,
1026 done: false,
1027 }
1028 }
1029}
1030
1031impl<I: Iterator<Item = Result<crate::OwnedSegment, EdifactError>>> Iterator
1032 for MessageWindowsIter<I>
1033{
1034 type Item = Result<OwnedMessageWindow, EdifactError>;
1035
1036 fn next(&mut self) -> Option<Self::Item> {
1037 if self.done {
1038 return None;
1039 }
1040 loop {
1041 let segment = match self.inner.next() {
1042 Some(Ok(s)) => s,
1043 Some(Err(e)) => {
1044 self.done = true;
1045 return Some(Err(e));
1046 }
1047 None => {
1048 self.done = true;
1049 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.as_str() {
1061 "UNH" => {
1062 if self.in_message {
1063 self.buf.clear();
1065 self.in_message = false;
1066 self.done = true;
1067 return Some(Err(EdifactError::InvalidSegmentForMessage {
1068 tag: "UNH".to_owned(),
1069 message_type: "ENVELOPE".to_owned(),
1070 span: segment.span,
1071 }));
1072 }
1073 self.buf.clear();
1074 self.in_message = true;
1075 self.buf.push(segment);
1076 }
1077 "UNT" if self.in_message => {
1078 self.buf.push(segment);
1079 self.in_message = false;
1080 let segments = std::mem::take(&mut self.buf);
1081 return Some(Ok(OwnedMessageWindow::from_segments(segments)));
1082 }
1083 _ if self.in_message => {
1084 self.buf.push(segment);
1085 }
1086 _ => {
1087 }
1089 }
1090 }
1091 }
1092}
1093
1094pub fn message_windows_bytes(input: &[u8]) -> MessageWindowsSliceIter<'_> {
1123 MessageWindowsSliceIter::new(crate::from_bytes(input))
1124}
1125
1126pub fn message_windows_from_reader<R: Read>(
1132 reader: R,
1133) -> MessageWindowsIter<crate::FromReaderIter<R>> {
1134 MessageWindowsIter::new(crate::from_reader(reader))
1135}
1136
1137pub fn deserialize_messages_from_reader<T, R>(
1156 reader: R,
1157) -> impl Iterator<Item = Result<T, EdifactError>>
1158where
1159 T: EdifactDeserialize,
1160 R: Read,
1161{
1162 message_windows_from_reader(reader).map(|window| {
1163 let window = window?;
1164 T::edifact_deserialize_owned(&window.segments)
1165 })
1166}
1167
1168pub fn deserialize_messages_bytes<T>(
1170 input: &[u8],
1171) -> impl Iterator<Item = Result<T, EdifactError>> + '_
1172where
1173 T: EdifactDeserialize,
1174{
1175 message_windows_bytes(input).map(|window| {
1176 let window = window?;
1177 T::edifact_deserialize(&window.segments)
1178 })
1179}
1180
1181pub struct DispatchedMessage {
1185 pub message_type: String,
1187 value: Box<dyn std::any::Any + Send + Sync>,
1188}
1189
1190impl DispatchedMessage {
1191 pub fn downcast<T: std::any::Any + Send + Sync + 'static>(&self) -> Option<&T> {
1195 self.value.downcast_ref::<T>()
1196 }
1197}
1198
1199impl std::fmt::Debug for DispatchedMessage {
1200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1201 f.debug_struct("DispatchedMessage")
1202 .field("message_type", &self.message_type)
1203 .finish_non_exhaustive()
1204 }
1205}
1206
1207type DispatchHandlerFn = Box<
1208 dyn for<'a> Fn(&[Segment<'a>]) -> Result<Box<dyn std::any::Any + Send + Sync>, EdifactError>
1209 + Send
1210 + Sync,
1211>;
1212
1213type FallbackHandlerFn = Box<
1214 dyn for<'a> Fn(
1215 &[Segment<'a>],
1216 &str,
1217 ) -> Result<Box<dyn std::any::Any + Send + Sync>, EdifactError>
1218 + Send
1219 + Sync,
1220>;
1221
1222pub struct MessageDispatch {
1247 handlers: Vec<(String, DispatchHandlerFn)>,
1248 fallback: Option<FallbackHandlerFn>,
1249}
1250
1251impl Default for MessageDispatch {
1252 fn default() -> Self {
1253 Self::new()
1254 }
1255}
1256
1257impl MessageDispatch {
1258 pub fn new() -> Self {
1260 Self {
1261 handlers: Vec::new(),
1262 fallback: None,
1263 }
1264 }
1265
1266 pub fn on<T, F>(mut self, message_type: &str, handler: F) -> Self
1271 where
1272 T: std::any::Any + Send + Sync + 'static,
1273 F: for<'a> Fn(&[Segment<'a>]) -> Result<T, EdifactError> + Send + Sync + 'static,
1274 {
1275 let erased: DispatchHandlerFn = Box::new(move |segs| {
1276 let val = handler(segs)?;
1277 Ok(Box::new(val) as Box<dyn std::any::Any + Send + Sync>)
1278 });
1279 self.handlers.push((message_type.to_owned(), erased));
1280 self
1281 }
1282
1283 pub fn fallback<T, F>(mut self, handler: F) -> Self
1288 where
1289 T: std::any::Any + Send + Sync + 'static,
1290 F: for<'a> Fn(&[Segment<'a>], &str) -> Result<T, EdifactError> + Send + Sync + 'static,
1291 {
1292 let erased: FallbackHandlerFn = Box::new(move |segs, mt| {
1293 let val = handler(segs, mt)?;
1294 Ok(Box::new(val) as Box<dyn std::any::Any + Send + Sync>)
1295 });
1296 self.fallback = Some(erased);
1297 self
1298 }
1299
1300 pub fn dispatch(&self, window: &[Segment<'_>]) -> Result<DispatchedMessage, EdifactError> {
1305 let message_type = window
1306 .iter()
1307 .find(|s| s.tag == "UNH")
1308 .and_then(|unh| unh.get_element(1))
1309 .and_then(|e| e.get_component(0))
1310 .map(|s| s.to_owned())
1311 .ok_or_else(|| EdifactError::MissingSegment {
1312 tag: "UNH".to_owned(),
1313 expected_position: "first segment of message window".to_owned(),
1314 })?;
1315
1316 for (mt, handler) in &self.handlers {
1317 if *mt == message_type {
1318 let value = handler(window)?;
1319 return Ok(DispatchedMessage {
1320 message_type,
1321 value,
1322 });
1323 }
1324 }
1325
1326 if let Some(fallback) = &self.fallback {
1327 let value = fallback(window, &message_type)?;
1328 return Ok(DispatchedMessage {
1329 message_type,
1330 value,
1331 });
1332 }
1333
1334 Err(EdifactError::UnexpectedMessageType { message_type })
1335 }
1336
1337 pub fn dispatch_all_from_bytes<'a>(
1342 &'a self,
1343 input: &'a [u8],
1344 ) -> impl Iterator<Item = Result<DispatchedMessage, EdifactError>> + 'a {
1345 message_windows_bytes(input).map(move |window| {
1346 let window = window?;
1347 self.dispatch(&window.segments)
1348 })
1349 }
1350
1351 pub fn dispatch_all_from_reader<R: Read + 'static>(
1358 &self,
1359 reader: R,
1360 ) -> impl Iterator<Item = Result<DispatchedMessage, EdifactError>> + '_ {
1361 message_windows_from_reader(reader).map(|window| {
1362 let window = window?;
1363 let borrowed: Vec<Segment<'_>> =
1364 window.segments.iter().map(|s| s.as_borrowed()).collect();
1365 self.dispatch(&borrowed)
1366 })
1367 }
1368}
1369
1370#[cfg(test)]
1371mod tests {
1372 use super::*;
1373
1374 #[derive(Debug, PartialEq)]
1376 struct BgmSegment {
1377 doc_name_code: String,
1378 pruef_id: String,
1379 msg_function: Option<String>,
1380 }
1381
1382 impl EdifactSegmentTag for BgmSegment {
1383 const SEGMENT_TAG: &'static str = "BGM";
1384 }
1385
1386 struct NadM;
1387
1388 impl EdifactSegmentTag for NadM {
1389 const SEGMENT_TAG: &'static str = "NAD";
1390 const QUALIFIER_PATTERN: Option<&'static str> = Some("M*");
1391 }
1392
1393 struct NadWildcard;
1394
1395 impl EdifactSegmentTag for NadWildcard {
1396 const SEGMENT_TAG: &'static str = "NAD";
1397 const QUALIFIER_PATTERN: Option<&'static str> = Some("M*");
1398 }
1399
1400 impl EdifactDeserialize for BgmSegment {
1401 fn edifact_deserialize(segments: &[Segment<'_>]) -> Result<Self, EdifactError> {
1402 let seg = find_segment(segments, "BGM").ok_or_else(|| {
1403 EdifactError::MissingRequiredElement {
1404 tag: "BGM".to_owned(),
1405 element_index: 0,
1406 }
1407 })?;
1408 Ok(Self {
1409 doc_name_code: element_str(seg, 0).to_owned(),
1410 pruef_id: element_str(seg, 1).to_owned(),
1411 msg_function: seg
1412 .element_str(2)
1413 .filter(|s| !s.is_empty())
1414 .map(str::to_owned),
1415 })
1416 }
1417 }
1418
1419 #[test]
1420 fn deserialize_single_segment() {
1421 let input = b"BGM+E03+11042+9'";
1422 let bgm: BgmSegment = deserialize(input).unwrap();
1423 assert_eq!(bgm.doc_name_code, "E03");
1424 assert_eq!(bgm.pruef_id, "11042");
1425 assert_eq!(bgm.msg_function, Some("9".to_owned()));
1426 }
1427
1428 #[test]
1429 fn streaming_deserialize_first_from_bytes() {
1430 let input = b"UNH+1+ORDERS:D:11A:UN'BGM+E03+11042+9'UNT+3+1'";
1431 let bgm: BgmSegment = deserialize_first_streaming(input).unwrap();
1432 assert_eq!(bgm.pruef_id, "11042");
1433 }
1434
1435 #[test]
1436 fn streaming_deserialize_all_from_bytes() {
1437 let input = b"BGM+E03+11042+9'RFF+AA:1'BGM+E01+11043+9'";
1438 let bgms: Vec<BgmSegment> = deserialize_all_streaming(input).unwrap();
1439 assert_eq!(bgms.len(), 2);
1440 assert_eq!(bgms[0].pruef_id, "11042");
1441 assert_eq!(bgms[1].pruef_id, "11043");
1442 }
1443
1444 #[test]
1445 fn streaming_deserialize_first_from_reader() {
1446 let input =
1447 std::io::Cursor::new(b"UNH+1+ORDERS:D:11A:UN'BGM+E03+11042+9'UNT+3+1'".to_vec());
1448 let bgm: BgmSegment = deserialize_first_from_reader(input).unwrap();
1449 assert_eq!(bgm.pruef_id, "11042");
1450 }
1451
1452 #[test]
1453 fn streaming_deserialize_all_from_reader() {
1454 let input = std::io::Cursor::new(b"BGM+E03+11042+9'BGM+E01+11043+9'".to_vec());
1455 let bgms: Vec<BgmSegment> = deserialize_all_from_reader(input).unwrap();
1456 assert_eq!(bgms.len(), 2);
1457 assert_eq!(bgms[0].pruef_id, "11042");
1458 assert_eq!(bgms[1].pruef_id, "11043");
1459 }
1460
1461 #[test]
1462 fn missing_segment_returns_error() {
1463 let input = b"DTM+137:20230401:102'";
1464 let result: Result<BgmSegment, _> = deserialize(input);
1465 assert!(result.is_err());
1466 }
1467
1468 #[test]
1469 fn vec_collects_all_matching_segments() {
1470 let input = b"DTM+137:20230401:102'BGM+E03+11042+9'BGM+E01+11043+9'";
1471 let bgms: Vec<BgmSegment> = deserialize(input).unwrap();
1472 assert_eq!(bgms.len(), 2);
1473 assert_eq!(bgms[0].pruef_id, "11042");
1474 assert_eq!(bgms[1].pruef_id, "11043");
1475 }
1476
1477 #[test]
1478 fn find_qualified_segment_matches_qualifier() {
1479 let input = b"NAD+MS+9900001+293'NAD+MR+9900002+293'";
1480 let segments: Vec<Segment<'_>> =
1481 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1482 let nad_ms = find_qualified_segment(&segments, "NAD", "MS");
1483 let nad_mr = find_qualified_segment(&segments, "NAD", "MR");
1484 assert!(nad_ms.is_some());
1485 assert!(nad_mr.is_some());
1486 assert_eq!(element_str(nad_ms.unwrap(), 0), "MS");
1487 assert_eq!(element_str(nad_mr.unwrap(), 0), "MR");
1488 }
1489
1490 #[test]
1491 fn round_trip_str_api() {
1492 let input = "BGM+E03+11042+9'";
1493 let bgm: BgmSegment = deserialize_str(input).unwrap();
1494 assert_eq!(bgm.pruef_id, "11042");
1495 }
1496
1497 #[test]
1498 fn required_element_extraction() {
1499 let input = b"BGM+E03+11042+9'";
1500 let segments: Vec<Segment<'_>> =
1501 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1502 let seg = &segments[0];
1503
1504 assert_eq!(required_element(seg, 0).unwrap(), "E03");
1505 assert_eq!(required_element(seg, 1).unwrap(), "11042");
1506 assert!(required_element(seg, 5).is_err());
1508 }
1509
1510 #[test]
1511 fn optional_element_extraction() {
1512 let input = b"BGM+E03+11042+9'BGM+E01++absent'";
1513 let segments: Vec<Segment<'_>> =
1514 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1515
1516 assert_eq!(optional_element(&segments[0], 0), Some("E03"));
1518 assert_eq!(optional_element(&segments[0], 1), Some("11042"));
1519 assert_eq!(optional_element(&segments[0], 5), None);
1520
1521 assert_eq!(optional_element(&segments[1], 1), None);
1523 }
1524
1525 #[test]
1526 fn component_extraction() {
1527 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1528 let segments: Vec<Segment<'_>> =
1529 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1530 let seg = &segments[0];
1531
1532 assert_eq!(required_component(seg, 0, 0).unwrap(), "UNOA");
1533 assert_eq!(required_component(seg, 0, 1).unwrap(), "1");
1534 assert!(required_component(seg, 0, 5).is_err());
1536 }
1537
1538 #[test]
1539 fn composite_element_helper() {
1540 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1541 let segments: Vec<Segment<'_>> =
1542 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1543 let seg = &segments[0];
1544
1545 let comp = composite_element(seg, 0).unwrap();
1546 assert_eq!(comp.len(), 2);
1547 assert_eq!(comp.get(0), Some("UNOA"));
1548 assert_eq!(comp.get(1), Some("1"));
1549 assert_eq!(comp.get(5), None);
1550 assert_eq!(comp.get_or_empty(5), "");
1551 }
1552
1553 #[test]
1554 fn get_all_components() {
1555 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1557 let segments: Vec<Segment<'_>> =
1558 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1559 let seg = &segments[0];
1560
1561 let comps: Vec<&str> = get_components_iter(seg, 0).collect(); assert!(!comps.is_empty(), "Expected components but got empty");
1563 assert_eq!(comps.len(), 2);
1564 assert_eq!(comps[0], "UNOA");
1565 assert_eq!(comps[1], "1");
1566 }
1567
1568 #[test]
1569 fn qualifier_pattern_matching_supports_exact_and_wildcard() {
1570 assert!(qualifier_matches_pattern("MS", "MS"));
1572 assert!(!qualifier_matches_pattern("MS", "M")); assert!(qualifier_matches_pattern("MS", "M*"));
1575 assert!(qualifier_matches_pattern("MRY", "M*Y"));
1576 assert!(!qualifier_matches_pattern("AB", "M*"));
1577 }
1578
1579 #[test]
1581 fn qualifier_matches_pattern_table() {
1582 let cases: &[(&str, &str, bool)] = &[
1584 ("", "", true), ("", "*", true), ("A", "", false), ("", "A", false), ("MS", "MS", true),
1591 ("BY", "BY", true),
1592 ("ms", "MS", false), ("MSX", "MS", false), ("M", "MS", false), ("MS", "M*", true),
1597 ("MULTI", "MUL*", true),
1598 ("AB", "M*", false),
1599 ("", "M*", false), ("MSG", "*G", true),
1602 ("G", "*G", true),
1603 ("MSG", "*X", false),
1604 ("", "*G", false),
1605 ("MRY", "M*Y", true),
1607 ("MAY", "M*Y", true),
1608 ("MY", "M*Y", true), ("MYY", "M*Y", true), ("MAYZ", "M*Y", false), ("AB", "M*Y", false),
1612 ("*", "*", true), ("anything", "*", true),
1615 ("", "*", true),
1616 ("ABCDE", "A*C*E", true),
1618 ("ACE", "A*C*E", true), ("AXCYE", "A*C*E", true),
1620 ("ABCDF", "A*C*E", false),
1621 ("AB", "A**B", true), ("AB", "A*B*C", false),
1625 ("XMS", "MS", false),
1627 ];
1628
1629 for (value, pattern, expected) in cases {
1630 let got = qualifier_matches_pattern(value, pattern);
1631 assert_eq!(
1632 got, *expected,
1633 "qualifier_matches_pattern({value:?}, {pattern:?}) expected {expected} but got {got}"
1634 );
1635 }
1636 }
1637
1638 #[test]
1639 fn typed_qualifier_helpers_work() {
1640 let input = b"NAD+MS+9900001+293'NAD+MR+9900002+293'";
1641 let segments: Vec<Segment<'_>> =
1642 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1643
1644 let first = find_segment_typed::<NadM>(&segments).unwrap();
1645 assert_eq!(first.element_str(0), Some("MS"));
1646
1647 let all: Vec<_> = find_segments_typed::<NadWildcard>(&segments).collect();
1648 assert_eq!(all.len(), 2);
1649 }
1650
1651 #[test]
1652 fn segment_accessor_trait_methods_work() {
1653 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1654 let segments: Vec<Segment<'_>> =
1655 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1656 let seg = &segments[0];
1657
1658 assert_eq!(SegmentAccessor::get_element(seg, 1), Some("SENDER"));
1659 assert_eq!(SegmentAccessor::required_composite(seg, 0, 1).unwrap(), "1");
1660 let parsed: i32 = SegmentAccessor::code_element(seg, 4).unwrap();
1661 assert_eq!(parsed, 1);
1662 let reps = SegmentAccessor::component_range(seg, 3, 0, 2).unwrap();
1663 assert_eq!(reps, vec!["200101", "0900"]);
1664 }
1665
1666 #[test]
1667 fn group_helpers_detect_contiguity() {
1668 struct NadAny;
1669 impl EdifactSegmentTag for NadAny {
1670 const SEGMENT_TAG: &'static str = "NAD";
1671 }
1672
1673 let contiguous_input = b"NAD+MS+1'NAD+MR+2'RFF+AA:1'";
1674 let contiguous_segments: Vec<Segment<'_>> = crate::from_bytes(contiguous_input)
1675 .collect::<Result<_, _>>()
1676 .unwrap();
1677 assert!(groups_are_contiguous_by_qualifier::<NadAny>(
1678 &contiguous_segments
1679 ));
1680
1681 let non_contiguous_input = b"NAD+MS+1'RFF+AA:1'NAD+MR+2'";
1682 let non_contiguous_segments: Vec<Segment<'_>> = crate::from_bytes(non_contiguous_input)
1683 .collect::<Result<_, _>>()
1684 .unwrap();
1685 assert!(!groups_are_contiguous_by_qualifier::<NadAny>(
1686 &non_contiguous_segments
1687 ));
1688 }
1689
1690 #[test]
1691 fn group_helpers_collect_contiguous_groups() {
1692 struct NadAny;
1693 impl EdifactSegmentTag for NadAny {
1694 const SEGMENT_TAG: &'static str = "NAD";
1695 }
1696
1697 let input = b"NAD+MS+1'NAD+MR+2'RFF+AA:1'NAD+BY+3'";
1698 let segments: Vec<Segment<'_>> =
1699 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1700 let groups = contiguous_groups_by_qualifier::<NadAny>(&segments);
1701
1702 assert_eq!(groups.len(), 2);
1703 assert_eq!(groups[0].len(), 2);
1704 assert_eq!(groups[1].len(), 1);
1705 }
1706
1707 #[test]
1710 fn message_windows_bytes_yields_complete_windows() {
1711 let input = b"UNB+UNOA:1+S+R+200101:0900+1'\
1712 UNH+1+ORDERS:D:96A:UN'\
1713 BGM+220+PO-001+9'\
1714 UNT+3+1'\
1715 UNZ+1+1'";
1716 let windows: Vec<_> = message_windows_bytes(input)
1717 .collect::<Result<_, _>>()
1718 .unwrap();
1719 assert_eq!(windows.len(), 1);
1720 assert_eq!(windows[0].segments[0].tag, "UNH");
1721 assert_eq!(windows[0].segments.last().unwrap().tag, "UNT");
1722 assert_eq!(windows[0].message_type.as_deref(), Some("ORDERS"));
1723 assert_eq!(windows[0].association_code.as_deref(), None);
1724 }
1725
1726 #[test]
1727 fn message_windows_bytes_preserves_owned_unh_metadata() {
1728 let input = b"UNB+UNOA:1+S+R+200101:0900+1'\
1729 UNH+1+ORD?ERS:D:96A:UN:5??5??3a'\
1730 BGM+220+PO-001+9'\
1731 UNT+3+1'\
1732 UNZ+1+1'";
1733 let windows: Vec<_> = message_windows_bytes(input)
1734 .collect::<Result<_, _>>()
1735 .unwrap();
1736
1737 assert_eq!(windows.len(), 1);
1738 assert_eq!(windows[0].message_type.as_deref(), Some("ORDERS"));
1739 assert_eq!(windows[0].association_code.as_deref(), Some("5?5?3a"));
1740 }
1741
1742 #[test]
1743 fn message_windows_truncated_stream_returns_error() {
1744 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'";
1746 let results: Vec<_> = message_windows_bytes(input).collect();
1747 assert_eq!(results.len(), 1);
1748 assert!(
1749 matches!(results[0], Err(EdifactError::UnexpectedEof { .. })),
1750 "expected UnexpectedEof for truncated window, got: {:?}",
1751 results[0]
1752 );
1753 }
1754
1755 #[test]
1756 fn message_windows_subsequent_calls_return_none_after_truncation() {
1757 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'";
1758 let mut iter = message_windows_bytes(input);
1759 assert!(matches!(
1760 iter.next(),
1761 Some(Err(EdifactError::UnexpectedEof { .. }))
1762 ));
1763 assert!(iter.next().is_none());
1765 }
1766
1767 #[test]
1768 fn message_windows_unh_without_unt_before_next_unh_returns_error() {
1769 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'\
1770 UNH+2+ORDERS:D:96A:UN'BGM+220+PO-002+9'UNT+3+2'";
1771 let results: Vec<_> = message_windows_bytes(input).collect();
1772 assert!(
1774 matches!(
1775 results[0],
1776 Err(EdifactError::InvalidSegmentForMessage { ref tag, .. }) if tag == "UNH"
1777 ),
1778 "expected InvalidSegmentForMessage(UNH), got: {:?}",
1779 results[0]
1780 );
1781 }
1782
1783 fn parse_one(input: &str) -> crate::OwnedSegment {
1786 crate::from_reader_collect(std::io::Cursor::new(input.as_bytes()))
1787 .expect("parse failed")
1788 .into_iter()
1789 .next()
1790 .expect("at least one segment")
1791 }
1792
1793 #[test]
1794 fn segment_accessor_get_element_returns_value() {
1795 let owned = parse_one("BGM+220+PO-001+9'");
1796 let seg = owned.as_borrowed();
1797 assert_eq!(SegmentAccessor::get_element(&seg, 0), Some("220"));
1798 assert_eq!(SegmentAccessor::get_element(&seg, 1), Some("PO-001"));
1799 assert_eq!(SegmentAccessor::get_element(&seg, 2), Some("9"));
1800 assert_eq!(
1801 SegmentAccessor::get_element(&seg, 9),
1802 None,
1803 "out-of-bounds must return None"
1804 );
1805 }
1806
1807 #[test]
1808 fn segment_accessor_get_element_filters_empty() {
1809 let owned = parse_one("TST+++VALUE'");
1810 let seg = owned.as_borrowed();
1811 assert_eq!(
1813 SegmentAccessor::get_element(&seg, 0),
1814 None,
1815 "empty element must return None"
1816 );
1817 assert_eq!(
1818 SegmentAccessor::get_element(&seg, 1),
1819 None,
1820 "empty element must return None"
1821 );
1822 assert_eq!(SegmentAccessor::get_element(&seg, 2), Some("VALUE"));
1823 }
1824
1825 #[test]
1826 fn segment_accessor_get_component_returns_value() {
1827 let owned = parse_one("UNH+1+ORDERS:D:96A:UN'");
1828 let seg = owned.as_borrowed();
1829 assert_eq!(seg.get_component(1, 0), Some("ORDERS"));
1830 assert_eq!(seg.get_component(1, 1), Some("D"));
1831 assert_eq!(seg.get_component(1, 2), Some("96A"));
1832 assert_eq!(seg.get_component(1, 3), Some("UN"));
1833 assert_eq!(
1834 seg.get_component(1, 9),
1835 None,
1836 "out-of-bounds must return None"
1837 );
1838 }
1839
1840 #[test]
1841 fn segment_accessor_text_element_errors_on_missing() {
1842 let owned = parse_one("BGM+'");
1843 let seg = owned.as_borrowed();
1844 let err = seg.text_element(0);
1846 assert!(
1847 matches!(err, Err(EdifactError::MissingRequiredElement { ref tag, element_index: 0 }) if tag == "BGM"),
1848 "expected MissingRequiredElement, got: {err:?}"
1849 );
1850 }
1851
1852 #[test]
1853 fn segment_accessor_required_composite_errors_on_missing() {
1854 let owned = parse_one("DTM+137'");
1855 let seg = owned.as_borrowed();
1856 let err = seg.required_composite(0, 1);
1858 assert!(
1859 matches!(err, Err(EdifactError::MissingRequiredComponent { ref tag, element_index: 0, component_index: 1 }) if tag == "DTM"),
1860 "expected MissingRequiredComponent, got: {err:?}"
1861 );
1862 }
1863
1864 #[test]
1865 fn segment_accessor_code_element_parses_integer() {
1866 let owned = parse_one("QTY+21:100'");
1867 let seg = owned.as_borrowed();
1868 let qty: u32 = seg.code_element(0).expect("should parse qualifier as u32");
1869 assert_eq!(qty, 21);
1870 }
1871
1872 #[test]
1873 fn segment_accessor_optional_element_absent_returns_none() {
1874 let owned = parse_one("BGM+220'");
1875 let seg = owned.as_borrowed();
1876 assert_eq!(seg.optional_element(5), None);
1877 }
1878}