1use crate::{EdifactError, Segment};
9use std::io::Read;
10use std::str::FromStr;
11
12pub trait EdifactDeserialize: Sized {
19 fn edifact_deserialize(segments: &[Segment<'_>]) -> Result<Self, EdifactError>;
24
25 fn edifact_deserialize_owned(segments: &[crate::OwnedSegment]) -> Result<Self, EdifactError> {
43 let borrowed: Vec<Segment<'_>> = segments.iter().map(|s| s.as_borrowed()).collect();
44 Self::edifact_deserialize(&borrowed)
45 }
46}
47
48pub trait EdifactCompositeDeserialize: Sized {
53 fn edifact_deserialize_composite(composite: CompositeElement<'_>)
55 -> Result<Self, EdifactError>;
56}
57
58impl EdifactCompositeDeserialize for Vec<String> {
59 fn edifact_deserialize_composite(
60 composite: CompositeElement<'_>,
61 ) -> Result<Self, EdifactError> {
62 Ok(composite.iter().map(str::to_owned).collect())
63 }
64}
65
66pub trait EdifactSegmentTag {
71 const SEGMENT_TAG: &'static str;
73
74 const QUALIFIER_PATTERN: Option<&'static str> = None;
80
81 fn matches_qualifier(seg: &Segment<'_>) -> bool {
83 match Self::QUALIFIER_PATTERN {
84 Some(pattern) => seg
85 .element_str(0)
86 .is_some_and(|q| qualifier_matches_pattern(q, pattern)),
87 None => true,
88 }
89 }
90
91 fn matches_segment(seg: &Segment<'_>) -> bool {
96 seg.tag == Self::SEGMENT_TAG && Self::matches_qualifier(seg)
97 }
98
99 fn matches_owned_segment(seg: &crate::OwnedSegment) -> bool {
103 if seg.tag != Self::SEGMENT_TAG {
104 return false;
105 }
106 match Self::QUALIFIER_PATTERN {
107 None => true,
108 Some(pattern) => {
109 let q = seg
110 .elements
111 .first()
112 .and_then(|e| e.components.first())
113 .map(|c| c.as_str())
114 .unwrap_or("");
115 qualifier_matches_pattern(q, pattern)
116 }
117 }
118 }
119}
120
121impl<T> EdifactDeserialize for Vec<T>
126where
127 T: EdifactDeserialize + EdifactSegmentTag,
128{
129 fn edifact_deserialize(segments: &[Segment<'_>]) -> Result<Self, EdifactError> {
130 segments
131 .iter()
132 .filter(|s| T::matches_segment(s))
133 .map(|seg| T::edifact_deserialize(std::slice::from_ref(seg)))
134 .collect()
135 }
136
137 fn edifact_deserialize_owned(segments: &[crate::OwnedSegment]) -> Result<Self, EdifactError> {
138 segments
139 .iter()
140 .filter(|s| T::matches_owned_segment(s))
141 .map(|seg| T::edifact_deserialize_owned(std::slice::from_ref(seg)))
142 .collect()
143 }
144}
145
146pub fn deserialize<T: EdifactDeserialize>(input: &[u8]) -> Result<T, EdifactError> {
156 let segments: Vec<Segment<'_>> = crate::from_bytes(input).collect::<Result<_, _>>()?;
157 T::edifact_deserialize(&segments)
158}
159
160pub fn deserialize_first_streaming<T>(input: &[u8]) -> Result<T, EdifactError>
165where
166 T: EdifactDeserialize + EdifactSegmentTag,
167{
168 for segment in crate::from_bytes(input) {
169 let segment = segment?;
170 if T::matches_segment(&segment) {
171 return T::edifact_deserialize(std::slice::from_ref(&segment));
172 }
173 }
174
175 Err(EdifactError::MissingSegment {
176 tag: T::SEGMENT_TAG.to_owned(),
177 expected_position: "any position in input".to_owned(),
178 })
179}
180
181pub fn deserialize_all_streaming<T>(input: &[u8]) -> Result<Vec<T>, EdifactError>
185where
186 T: EdifactDeserialize + EdifactSegmentTag,
187{
188 let mut out = Vec::new();
189 for segment in crate::from_bytes(input) {
190 let segment = segment?;
191 if T::matches_segment(&segment) {
192 out.push(T::edifact_deserialize(std::slice::from_ref(&segment))?);
193 }
194 }
195 Ok(out)
196}
197
198pub fn deserialize_first_from_reader<T, R>(reader: R) -> Result<T, EdifactError>
202where
203 T: EdifactDeserialize + EdifactSegmentTag,
204 R: Read,
205{
206 for segment in crate::from_reader_iter(reader) {
207 let segment = segment?;
208 if !T::matches_owned_segment(&segment) {
210 continue;
211 }
212 return T::edifact_deserialize_owned(std::slice::from_ref(&segment));
213 }
214
215 Err(EdifactError::MissingSegment {
216 tag: T::SEGMENT_TAG.to_owned(),
217 expected_position: "any position in input".to_owned(),
218 })
219}
220
221pub fn deserialize_all_from_reader<T, R>(reader: R) -> Result<Vec<T>, EdifactError>
223where
224 T: EdifactDeserialize + EdifactSegmentTag,
225 R: Read,
226{
227 let mut out = Vec::new();
228 for segment in crate::from_reader_iter(reader) {
229 let segment = segment?;
230 if !T::matches_owned_segment(&segment) {
232 continue;
233 }
234 out.push(T::edifact_deserialize_owned(std::slice::from_ref(&segment))?);
235 }
236 Ok(out)
237}
238
239pub fn deserialize_str<T: EdifactDeserialize>(input: &str) -> Result<T, EdifactError> {
241 deserialize(input.as_bytes())
242}
243
244pub fn find_segment<'s, 'd>(segments: &'s [Segment<'d>], tag: &str) -> Option<&'s Segment<'d>> {
248 segments.iter().find(|s| s.tag == tag)
249}
250
251pub fn find_segments_iter<'s, 'd: 's>(
253 segments: &'s [Segment<'d>],
254 tag: &'s str,
255) -> impl Iterator<Item = &'s Segment<'d>> {
256 segments.iter().filter(move |s| s.tag == tag)
257}
258
259pub fn find_qualified_segment<'s, 'd>(
261 segments: &'s [Segment<'d>],
262 tag: &str,
263 qualifier: &str,
264) -> Option<&'s Segment<'d>> {
265 segments
266 .iter()
267 .find(|s| s.tag == tag && s.element_str(0).unwrap_or("") == qualifier)
268}
269
270pub fn find_segment_typed<'s, 'd, T>(segments: &'s [Segment<'d>]) -> Option<&'s Segment<'d>>
272where
273 T: EdifactSegmentTag,
274{
275 segments.iter().find(|s| T::matches_segment(s))
276}
277
278pub fn find_segments_typed<'s, 'd: 's, T>(
280 segments: &'s [Segment<'d>],
281) -> impl Iterator<Item = &'s Segment<'d>>
282where
283 T: EdifactSegmentTag,
284{
285 segments.iter().filter(|s| T::matches_segment(s))
286}
287
288pub fn contiguous_groups_by_qualifier<'s, 'd, T>(
293 segments: &'s [Segment<'d>],
294) -> Vec<&'s [Segment<'d>]>
295where
296 T: EdifactSegmentTag,
297{
298 let mut groups = Vec::new();
299 let mut idx = 0;
300 while idx < segments.len() {
301 if T::matches_segment(&segments[idx]) {
302 let start = idx;
303 idx += 1;
304 while idx < segments.len() && T::matches_segment(&segments[idx]) {
305 idx += 1;
306 }
307 groups.push(&segments[start..idx]);
308 } else {
309 idx += 1;
310 }
311 }
312 groups
313}
314
315pub fn contiguous_groups_iter<'s, 'd, T>(
332 segments: &'s [Segment<'d>],
333) -> impl Iterator<Item = &'s [Segment<'d>]> + 's
334where
335 T: EdifactSegmentTag,
336{
337 let mut idx = 0;
338 let len = segments.len();
339 std::iter::from_fn(move || {
340 while idx < len && !T::matches_segment(&segments[idx]) {
342 idx += 1;
343 }
344 if idx >= len {
345 return None;
346 }
347 let start = idx;
348 idx += 1;
349 while idx < len && T::matches_segment(&segments[idx]) {
350 idx += 1;
351 }
352 Some(&segments[start..idx])
353 })
354}
355
356pub fn groups_are_contiguous_by_qualifier<T>(segments: &[Segment<'_>]) -> bool
358where
359 T: EdifactSegmentTag,
360{
361 let mut seen_match = false;
362 let mut seen_gap_after_match = false;
363
364 for seg in segments {
365 if T::matches_segment(seg) {
366 if seen_gap_after_match {
367 return false;
368 }
369 seen_match = true;
370 } else if seen_match {
371 seen_gap_after_match = true;
372 }
373 }
374
375 true
376}
377
378pub fn qualifier_matches_pattern(value: &str, pattern: &str) -> bool {
387 if pattern.is_empty() {
388 return value.is_empty();
389 }
390
391 if !pattern.contains('*') {
392 return value == pattern;
393 }
394
395 if let Some((prefix, suffix)) = pattern.split_once('*') {
397 if !pattern[prefix.len() + 1..].contains('*') {
399 return value.len() >= prefix.len() + suffix.len()
400 && value.starts_with(prefix)
401 && value.ends_with(suffix)
402 && {
403 let mid_start = prefix.len();
405 let mid_end = value.len().saturating_sub(suffix.len());
406 mid_start <= mid_end
407 };
408 }
409 }
410
411 let parts: smallvec::SmallVec<[&str; 4]> = pattern.split('*').collect();
413 let prefix = parts[0];
414 let suffix = parts[parts.len() - 1];
415
416 if !value.starts_with(prefix) || !value.ends_with(suffix) {
417 return false;
418 }
419
420 let mid_start = prefix.len();
421 let mid_end = value.len().saturating_sub(suffix.len());
422
423 if mid_start > mid_end {
424 return parts[1..parts.len() - 1].iter().all(|p| p.is_empty());
425 }
426
427 let mut remaining = &value[mid_start..mid_end];
428
429 for part in &parts[1..parts.len() - 1] {
430 if part.is_empty() {
431 continue;
432 }
433 match remaining.find(part) {
434 Some(idx) => remaining = &remaining[idx + part.len()..],
435 None => return false,
436 }
437 }
438
439 true
440}
441
442#[inline]
444pub fn element_str<'s>(seg: &'s Segment<'_>, idx: usize) -> &'s str {
445 seg.element_str(idx).unwrap_or("")
446}
447
448pub fn required_element<'a>(seg: &'a Segment<'_>, idx: usize) -> Result<&'a str, EdifactError> {
454 seg.element_str(idx)
455 .filter(|s| !s.is_empty())
456 .ok_or_else(|| EdifactError::MissingRequiredElement {
457 tag: seg.tag.to_owned(),
458 element_index: idx,
459 })
460}
461
462pub fn optional_element<'a>(seg: &'a Segment<'_>, idx: usize) -> Option<&'a str> {
466 seg.element_str(idx)
467 .filter(|s| !s.is_empty())
468}
469
470pub fn required_component<'a>(
478 seg: &'a Segment<'_>,
479 elem_idx: usize,
480 comp_idx: usize,
481) -> Result<&'a str, EdifactError> {
482 let elem = seg
483 .elements
484 .get(elem_idx)
485 .ok_or_else(|| EdifactError::MissingRequiredElement {
486 tag: seg.tag.to_owned(),
487 element_index: elem_idx,
488 })?;
489
490 elem.get_component(comp_idx)
491 .filter(|s| !s.is_empty())
492 .ok_or_else(|| EdifactError::MissingRequiredComponent {
493 tag: seg.tag.to_owned(),
494 element_index: elem_idx,
495 component_index: comp_idx,
496 })
497}
498
499pub fn optional_component<'a>(seg: &'a Segment<'_>, elem_idx: usize, comp_idx: usize) -> Option<&'a str> {
503 seg.elements
504 .get(elem_idx)
505 .and_then(|elem| elem.get_component(comp_idx))
506 .filter(|s| !s.is_empty())
507}
508
509pub fn get_components_iter<'a>(
513 seg: &'a Segment<'_>,
514 idx: usize,
515) -> impl Iterator<Item = &'a str> {
516 seg.elements
517 .get(idx)
518 .into_iter()
519 .flat_map(|elem| elem.components.iter().map(|c| c.as_ref()))
520}
521
522pub struct CompositeElement<'a> {
524 components: &'a [std::borrow::Cow<'a, str>],
525}
526
527impl<'a> CompositeElement<'a> {
528 pub fn get(&self, i: usize) -> Option<&'a str> {
530 self.components.get(i).map(|c| c.as_ref())
531 }
532
533 pub fn get_or_empty(&self, i: usize) -> &'a str {
535 self.get(i).unwrap_or("")
536 }
537
538 pub fn len(&self) -> usize {
540 self.components.len()
541 }
542
543 pub fn is_empty(&self) -> bool {
545 self.components.is_empty()
546 }
547
548 pub fn iter(&self) -> impl Iterator<Item = &'a str> {
550 self.components.iter().map(|c| c.as_ref())
551 }
552
553 pub fn from_slice(components: &'a [std::borrow::Cow<'a, str>]) -> Self {
558 Self { components }
559 }
560}
561
562pub fn composite_element<'a>(seg: &'a Segment<'_>, idx: usize) -> Option<CompositeElement<'a>> {
564 seg.elements.get(idx).map(|elem| CompositeElement {
565 components: &elem.components,
566 })
567}
568
569pub fn find_segment_owned<'s>(
576 segments: &'s [crate::OwnedSegment],
577 tag: &str,
578) -> Option<&'s crate::OwnedSegment> {
579 segments.iter().find(|s| s.tag == tag)
580}
581
582pub fn find_qualified_segment_owned<'s>(
590 segments: &'s [crate::OwnedSegment],
591 tag: &str,
592 qualifier: &str,
593) -> Option<&'s crate::OwnedSegment> {
594 segments.iter().find(|s| {
595 s.tag == tag && s.element_str(0).unwrap_or("") == qualifier
596 })
597}
598
599pub trait SegmentAccessor<'a> {
601 fn get_element(&'a self, idx: usize) -> Option<&'a str>;
603 fn get_component(&'a self, elem: usize, comp: usize) -> Option<&'a str>;
605 fn get_composite(&'a self, idx: usize) -> Option<CompositeElement<'a>>;
607
608 fn text_element(&'a self, idx: usize) -> Result<&'a str, EdifactError>;
610 fn optional_element(&'a self, idx: usize) -> Option<&'a str>;
612 fn code_element<T: FromStr>(&'a self, idx: usize) -> Result<T, EdifactError>;
614 fn required_composite(&'a self, elem: usize, comp: usize) -> Result<&'a str, EdifactError>;
616 fn repeating_components(
622 &'a self,
623 elem: usize,
624 start_idx: usize,
625 count: usize,
626 ) -> Result<Vec<&'a str>, EdifactError> {
627 self.repeating_components_iter(elem, start_idx, count).collect()
630 }
631
632 fn repeating_components_iter(
637 &'a self,
638 elem: usize,
639 start_idx: usize,
640 count: usize,
641 ) -> impl Iterator<Item = Result<&'a str, EdifactError>> + 'a;
642}
643
644impl<'s, 'd> SegmentAccessor<'s> for Segment<'d>
645where
646 'd: 's,
647{
648 fn get_element(&'s self, idx: usize) -> Option<&'s str> {
649 self.element_str(idx).filter(|s| !s.is_empty())
650 }
651
652 fn get_component(&'s self, elem: usize, comp: usize) -> Option<&'s str> {
653 self.elements
654 .get(elem)
655 .and_then(|e| e.get_component(comp))
656 .filter(|s| !s.is_empty())
657 }
658
659 fn get_composite(&'s self, idx: usize) -> Option<CompositeElement<'s>> {
660 composite_element(self, idx)
661 }
662
663 fn text_element(&'s self, idx: usize) -> Result<&'s str, EdifactError> {
664 <Self as SegmentAccessor>::get_element(self, idx).ok_or_else(|| {
665 EdifactError::MissingRequiredElement {
666 tag: self.tag.to_owned(),
667 element_index: idx,
668 }
669 })
670 }
671
672 fn optional_element(&'s self, idx: usize) -> Option<&'s str> {
673 <Self as SegmentAccessor>::get_element(self, idx)
674 }
675
676 fn code_element<T: FromStr>(&'s self, idx: usize) -> Result<T, EdifactError> {
677 let raw = self.text_element(idx)?;
678 raw.parse::<T>().map_err(|_| EdifactError::InvalidText {
679 offset: self.element_span(idx).map(|s| s.start).unwrap_or(self.span.start),
680 })
681 }
682
683 fn required_composite(&'s self, elem: usize, comp: usize) -> Result<&'s str, EdifactError> {
684 match self.elements.get(elem) {
685 None => Err(EdifactError::MissingRequiredElement {
686 tag: self.tag.to_owned(),
687 element_index: elem,
688 }),
689 Some(e) => e
690 .get_component(comp)
691 .filter(|s| !s.is_empty())
692 .ok_or_else(|| EdifactError::MissingRequiredComponent {
693 tag: self.tag.to_owned(),
694 element_index: elem,
695 component_index: comp,
696 }),
697 }
698 }
699
700 fn repeating_components_iter(
701 &'s self,
702 elem: usize,
703 start_idx: usize,
704 count: usize,
705 ) -> impl Iterator<Item = Result<&'s str, EdifactError>> + 's {
706 let tag = self.tag;
707 let element_exists = self.elements.get(elem).is_some();
708 let components = self
709 .elements
710 .get(elem)
711 .map(|e| e.components.as_slice())
712 .unwrap_or(&[]);
713 (start_idx..start_idx + count).map(move |idx| {
714 components
715 .get(idx)
716 .map(|c| c.as_ref())
717 .filter(|s| !s.is_empty())
718 .ok_or_else(|| {
719 if element_exists {
720 EdifactError::MissingRequiredComponent {
721 tag: tag.to_owned(),
722 element_index: elem,
723 component_index: idx,
724 }
725 } else {
726 EdifactError::MissingRequiredElement {
727 tag: tag.to_owned(),
728 element_index: elem,
729 }
730 }
731 })
732 })
733 }
734}
735
736pub struct MessageWindowsSliceIter<'a> {
747 inner: crate::FromBytesIter<'a>,
748 buf: Vec<crate::Segment<'a>>,
749 in_message: bool,
750 done: bool,
751}
752
753impl<'a> MessageWindowsSliceIter<'a> {
754 fn new(inner: crate::FromBytesIter<'a>) -> Self {
755 Self {
756 inner,
757 buf: Vec::new(),
758 in_message: false,
759 done: false,
760 }
761 }
762}
763
764impl<'a> Iterator for MessageWindowsSliceIter<'a> {
765 type Item = Result<Vec<crate::Segment<'a>>, EdifactError>;
766
767 fn next(&mut self) -> Option<Self::Item> {
768 if self.done {
769 return None;
770 }
771 loop {
772 let segment = match self.inner.next() {
773 Some(Ok(s)) => s,
774 Some(Err(e)) => {
775 self.done = true;
776 return Some(Err(e));
777 }
778 None => {
779 self.done = true;
780 if self.in_message && !self.buf.is_empty() {
781 self.in_message = false;
782 let offset = self.buf.last().map(|s| s.span.end).unwrap_or(0);
783 return Some(Err(EdifactError::UnexpectedEof { offset }));
784 }
785 return None;
786 }
787 };
788
789 match segment.tag {
790 "UNH" => {
791 if self.in_message {
792 self.buf.clear();
793 self.in_message = false;
794 self.done = true;
795 let offset = segment.span.start;
796 return Some(Err(EdifactError::InvalidSegmentForMessage {
797 tag: "UNH".to_owned(),
798 message_type: "ENVELOPE".to_owned(),
799 offset,
800 }));
801 }
802 self.buf.clear();
803 self.in_message = true;
804 self.buf.push(segment);
805 }
806 "UNT" if self.in_message => {
807 self.buf.push(segment);
808 self.in_message = false;
809 return Some(Ok(std::mem::take(&mut self.buf)));
810 }
811 _ if self.in_message => {
812 self.buf.push(segment);
813 }
814 _ => {
815 }
817 }
818 }
819 }
820}
821
822pub struct MessageWindowsIter<I> {
843 inner: I,
844 buf: Vec<crate::OwnedSegment>,
845 in_message: bool,
846 done: bool,
849}
850
851impl<I: Iterator<Item = Result<crate::OwnedSegment, EdifactError>>> MessageWindowsIter<I> {
852 pub fn new(inner: I) -> Self {
854 Self {
855 inner,
856 buf: Vec::new(),
857 in_message: false,
858 done: false,
859 }
860 }
861}
862
863impl<I: Iterator<Item = Result<crate::OwnedSegment, EdifactError>>> Iterator
864 for MessageWindowsIter<I>
865{
866 type Item = Result<Vec<crate::OwnedSegment>, EdifactError>;
867
868 fn next(&mut self) -> Option<Self::Item> {
869 if self.done {
870 return None;
871 }
872 loop {
873 let segment = match self.inner.next() {
874 Some(Ok(s)) => s,
875 Some(Err(e)) => {
876 self.done = true;
877 return Some(Err(e));
878 }
879 None => {
880 self.done = true;
881 if self.in_message && !self.buf.is_empty() {
884 self.in_message = false;
885 let offset = self.buf.last().map(|s| s.span.end).unwrap_or(0);
886 return Some(Err(EdifactError::UnexpectedEof { offset }));
887 }
888 return None;
889 }
890 };
891
892 match segment.tag.as_str() {
893 "UNH" => {
894 if self.in_message {
895 self.buf.clear();
897 self.in_message = false;
898 self.done = true;
899 let offset = segment.span.start;
900 return Some(Err(EdifactError::InvalidSegmentForMessage {
901 tag: "UNH".to_owned(),
902 message_type: "ENVELOPE".to_owned(),
903 offset,
904 }));
905 }
906 self.buf.clear();
907 self.in_message = true;
908 self.buf.push(segment);
909 }
910 "UNT" if self.in_message => {
911 self.buf.push(segment);
912 self.in_message = false;
913 return Some(Ok(std::mem::take(&mut self.buf)));
914 }
915 _ if self.in_message => {
916 self.buf.push(segment);
917 }
918 _ => {
919 }
921 }
922 }
923 }
924}
925
926pub fn message_windows_bytes(input: &[u8]) -> MessageWindowsSliceIter<'_> {
949 MessageWindowsSliceIter::new(crate::from_bytes(input))
950}
951
952pub fn message_windows_from_reader<R: Read>(
958 reader: R,
959) -> MessageWindowsIter<crate::FromReaderIter<R>> {
960 MessageWindowsIter::new(crate::from_reader_iter(reader))
961}
962
963pub fn deserialize_messages_from_reader<T, R>(
982 reader: R,
983) -> impl Iterator<Item = Result<T, EdifactError>>
984where
985 T: EdifactDeserialize,
986 R: Read,
987{
988 message_windows_from_reader(reader).map(|window| {
989 let window = window?;
990 T::edifact_deserialize_owned(&window)
991 })
992}
993
994pub fn deserialize_messages_bytes<T>(
996 input: &[u8],
997) -> impl Iterator<Item = Result<T, EdifactError>> + '_
998where
999 T: EdifactDeserialize,
1000{
1001 message_windows_bytes(input).map(|window| {
1002 let window = window?;
1003 T::edifact_deserialize(&window)
1004 })
1005}
1006
1007#[cfg(test)]
1008mod tests {
1009 use super::*;
1010
1011 #[derive(Debug, PartialEq)]
1013 struct BgmSegment {
1014 doc_name_code: String,
1015 pruef_id: String,
1016 msg_function: Option<String>,
1017 }
1018
1019 impl EdifactSegmentTag for BgmSegment {
1020 const SEGMENT_TAG: &'static str = "BGM";
1021 }
1022
1023 struct NadM;
1024
1025 impl EdifactSegmentTag for NadM {
1026 const SEGMENT_TAG: &'static str = "NAD";
1027 const QUALIFIER_PATTERN: Option<&'static str> = Some("M*");
1028 }
1029
1030 struct NadWildcard;
1031
1032 impl EdifactSegmentTag for NadWildcard {
1033 const SEGMENT_TAG: &'static str = "NAD";
1034 const QUALIFIER_PATTERN: Option<&'static str> = Some("M*");
1035 }
1036
1037 impl EdifactDeserialize for BgmSegment {
1038 fn edifact_deserialize(segments: &[Segment<'_>]) -> Result<Self, EdifactError> {
1039 let seg = find_segment(segments, "BGM").ok_or_else(|| {
1040 EdifactError::MissingRequiredElement {
1041 tag: "BGM".to_owned(),
1042 element_index: 0,
1043 }
1044 })?;
1045 Ok(Self {
1046 doc_name_code: element_str(seg, 0).to_owned(),
1047 pruef_id: element_str(seg, 1).to_owned(),
1048 msg_function: seg
1049 .element_str(2)
1050 .filter(|s| !s.is_empty())
1051 .map(str::to_owned),
1052 })
1053 }
1054 }
1055
1056 #[test]
1057 fn deserialize_single_segment() {
1058 let input = b"BGM+E03+11042+9'";
1059 let bgm: BgmSegment = deserialize(input).unwrap();
1060 assert_eq!(bgm.doc_name_code, "E03");
1061 assert_eq!(bgm.pruef_id, "11042");
1062 assert_eq!(bgm.msg_function, Some("9".to_owned()));
1063 }
1064
1065 #[test]
1066 fn streaming_deserialize_first_from_bytes() {
1067 let input = b"UNH+1+ORDERS:D:11A:UN'BGM+E03+11042+9'UNT+3+1'";
1068 let bgm: BgmSegment = deserialize_first_streaming(input).unwrap();
1069 assert_eq!(bgm.pruef_id, "11042");
1070 }
1071
1072 #[test]
1073 fn streaming_deserialize_all_from_bytes() {
1074 let input = b"BGM+E03+11042+9'RFF+AA:1'BGM+E01+11043+9'";
1075 let bgms: Vec<BgmSegment> = deserialize_all_streaming(input).unwrap();
1076 assert_eq!(bgms.len(), 2);
1077 assert_eq!(bgms[0].pruef_id, "11042");
1078 assert_eq!(bgms[1].pruef_id, "11043");
1079 }
1080
1081 #[test]
1082 fn streaming_deserialize_first_from_reader() {
1083 let input = std::io::Cursor::new(b"UNH+1+ORDERS:D:11A:UN'BGM+E03+11042+9'UNT+3+1'".to_vec());
1084 let bgm: BgmSegment = deserialize_first_from_reader(input).unwrap();
1085 assert_eq!(bgm.pruef_id, "11042");
1086 }
1087
1088 #[test]
1089 fn streaming_deserialize_all_from_reader() {
1090 let input = std::io::Cursor::new(b"BGM+E03+11042+9'BGM+E01+11043+9'".to_vec());
1091 let bgms: Vec<BgmSegment> = deserialize_all_from_reader(input).unwrap();
1092 assert_eq!(bgms.len(), 2);
1093 assert_eq!(bgms[0].pruef_id, "11042");
1094 assert_eq!(bgms[1].pruef_id, "11043");
1095 }
1096
1097 #[test]
1098 fn missing_segment_returns_error() {
1099 let input = b"DTM+137:20230401:102'";
1100 let result: Result<BgmSegment, _> = deserialize(input);
1101 assert!(result.is_err());
1102 }
1103
1104 #[test]
1105 fn vec_collects_all_matching_segments() {
1106 let input = b"DTM+137:20230401:102'BGM+E03+11042+9'BGM+E01+11043+9'";
1107 let bgms: Vec<BgmSegment> = deserialize(input).unwrap();
1108 assert_eq!(bgms.len(), 2);
1109 assert_eq!(bgms[0].pruef_id, "11042");
1110 assert_eq!(bgms[1].pruef_id, "11043");
1111 }
1112
1113 #[test]
1114 fn find_qualified_segment_matches_qualifier() {
1115 let input = b"NAD+MS+9900001+293'NAD+MR+9900002+293'";
1116 let segments: Vec<Segment<'_>> =
1117 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1118 let nad_ms = find_qualified_segment(&segments, "NAD", "MS");
1119 let nad_mr = find_qualified_segment(&segments, "NAD", "MR");
1120 assert!(nad_ms.is_some());
1121 assert!(nad_mr.is_some());
1122 assert_eq!(element_str(nad_ms.unwrap(), 0), "MS");
1123 assert_eq!(element_str(nad_mr.unwrap(), 0), "MR");
1124 }
1125
1126 #[test]
1127 fn round_trip_str_api() {
1128 let input = "BGM+E03+11042+9'";
1129 let bgm: BgmSegment = deserialize_str(input).unwrap();
1130 assert_eq!(bgm.pruef_id, "11042");
1131 }
1132
1133 #[test]
1134 fn required_element_extraction() {
1135 let input = b"BGM+E03+11042+9'";
1136 let segments: Vec<Segment<'_>> =
1137 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1138 let seg = &segments[0];
1139
1140 assert_eq!(required_element(seg, 0).unwrap(), "E03");
1141 assert_eq!(required_element(seg, 1).unwrap(), "11042");
1142 assert!(required_element(seg, 5).is_err());
1144 }
1145
1146 #[test]
1147 fn optional_element_extraction() {
1148 let input = b"BGM+E03+11042+9'BGM+E01++absent'";
1149 let segments: Vec<Segment<'_>> =
1150 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1151
1152 assert_eq!(optional_element(&segments[0], 0), Some("E03"));
1154 assert_eq!(optional_element(&segments[0], 1), Some("11042"));
1155 assert_eq!(optional_element(&segments[0], 5), None);
1156
1157 assert_eq!(optional_element(&segments[1], 1), None);
1159 }
1160
1161 #[test]
1162 fn component_extraction() {
1163 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1164 let segments: Vec<Segment<'_>> =
1165 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1166 let seg = &segments[0];
1167
1168 assert_eq!(required_component(seg, 0, 0).unwrap(), "UNOA");
1169 assert_eq!(required_component(seg, 0, 1).unwrap(), "1");
1170 assert!(required_component(seg, 0, 5).is_err());
1172 }
1173
1174 #[test]
1175 fn composite_element_helper() {
1176 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1177 let segments: Vec<Segment<'_>> =
1178 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1179 let seg = &segments[0];
1180
1181 let comp = composite_element(seg, 0).unwrap();
1182 assert_eq!(comp.len(), 2);
1183 assert_eq!(comp.get(0), Some("UNOA"));
1184 assert_eq!(comp.get(1), Some("1"));
1185 assert_eq!(comp.get(5), None);
1186 assert_eq!(comp.get_or_empty(5), "");
1187 }
1188
1189 #[test]
1190 fn get_all_components() {
1191 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1193 let segments: Vec<Segment<'_>> =
1194 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1195 let seg = &segments[0];
1196
1197 let comps: Vec<&str> = get_components_iter(seg, 0).collect(); assert!(!comps.is_empty(), "Expected components but got empty");
1199 assert_eq!(comps.len(), 2);
1200 assert_eq!(comps[0], "UNOA");
1201 assert_eq!(comps[1], "1");
1202 }
1203
1204 #[test]
1205 fn qualifier_pattern_matching_supports_exact_and_wildcard() {
1206 assert!(qualifier_matches_pattern("MS", "MS"));
1208 assert!(!qualifier_matches_pattern("MS", "M")); assert!(qualifier_matches_pattern("MS", "M*"));
1211 assert!(qualifier_matches_pattern("MRY", "M*Y"));
1212 assert!(!qualifier_matches_pattern("AB", "M*"));
1213 }
1214
1215 #[test]
1217 fn qualifier_matches_pattern_table() {
1218 let cases: &[(&str, &str, bool)] = &[
1220 ("", "", true), ("", "*", true), ("A", "", false), ("", "A", false), ("MS", "MS", true),
1227 ("BY", "BY", true),
1228 ("ms", "MS", false), ("MSX", "MS", false), ("M", "MS", false), ("MS", "M*", true),
1233 ("MULTI", "MUL*", true),
1234 ("AB", "M*", false),
1235 ("", "M*", false), ("MSG", "*G", true),
1238 ("G", "*G", true),
1239 ("MSG", "*X", false),
1240 ("", "*G", false),
1241 ("MRY", "M*Y", true),
1243 ("MAY", "M*Y", true),
1244 ("MY", "M*Y", true), ("MYY", "M*Y", true), ("MAYZ", "M*Y", false),("AB", "M*Y", false),
1248 ("*", "*", true), ("anything", "*", true),
1251 ("", "*", true),
1252 ("ABCDE", "A*C*E", true),
1254 ("ACE", "A*C*E", true), ("AXCYE", "A*C*E", true),
1256 ("ABCDF", "A*C*E", false),
1257 ("AB", "A**B", true), ("AB", "A*B*C", false),
1261 ("XMS", "MS", false),
1263 ];
1264
1265 for (value, pattern, expected) in cases {
1266 let got = qualifier_matches_pattern(value, pattern);
1267 assert_eq!(
1268 got, *expected,
1269 "qualifier_matches_pattern({value:?}, {pattern:?}) expected {expected} but got {got}"
1270 );
1271 }
1272 }
1273
1274 #[test]
1275 fn typed_qualifier_helpers_work() {
1276 let input = b"NAD+MS+9900001+293'NAD+MR+9900002+293'";
1277 let segments: Vec<Segment<'_>> =
1278 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1279
1280 let first = find_segment_typed::<NadM>(&segments).unwrap();
1281 assert_eq!(first.element_str(0), Some("MS"));
1282
1283 let all: Vec<_> = find_segments_typed::<NadWildcard>(&segments).collect();
1284 assert_eq!(all.len(), 2);
1285 }
1286
1287 #[test]
1288 fn segment_accessor_trait_methods_work() {
1289 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1290 let segments: Vec<Segment<'_>> =
1291 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1292 let seg = &segments[0];
1293
1294 assert_eq!(SegmentAccessor::get_element(seg, 1), Some("SENDER"));
1295 assert_eq!(SegmentAccessor::required_composite(seg, 0, 1).unwrap(), "1");
1296 let parsed: i32 = SegmentAccessor::code_element(seg, 4).unwrap();
1297 assert_eq!(parsed, 1);
1298 let reps = SegmentAccessor::repeating_components(seg, 3, 0, 2).unwrap();
1299 assert_eq!(reps, vec!["200101", "0900"]);
1300 }
1301
1302 #[test]
1303 fn group_helpers_detect_contiguity() {
1304 struct NadAny;
1305 impl EdifactSegmentTag for NadAny {
1306 const SEGMENT_TAG: &'static str = "NAD";
1307 }
1308
1309 let contiguous_input = b"NAD+MS+1'NAD+MR+2'RFF+AA:1'";
1310 let contiguous_segments: Vec<Segment<'_>> = crate::from_bytes(contiguous_input)
1311 .collect::<Result<_, _>>()
1312 .unwrap();
1313 assert!(groups_are_contiguous_by_qualifier::<NadAny>(
1314 &contiguous_segments
1315 ));
1316
1317 let non_contiguous_input = b"NAD+MS+1'RFF+AA:1'NAD+MR+2'";
1318 let non_contiguous_segments: Vec<Segment<'_>> = crate::from_bytes(non_contiguous_input)
1319 .collect::<Result<_, _>>()
1320 .unwrap();
1321 assert!(!groups_are_contiguous_by_qualifier::<NadAny>(
1322 &non_contiguous_segments
1323 ));
1324 }
1325
1326 #[test]
1327 fn group_helpers_collect_contiguous_groups() {
1328 struct NadAny;
1329 impl EdifactSegmentTag for NadAny {
1330 const SEGMENT_TAG: &'static str = "NAD";
1331 }
1332
1333 let input = b"NAD+MS+1'NAD+MR+2'RFF+AA:1'NAD+BY+3'";
1334 let segments: Vec<Segment<'_>> =
1335 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1336 let groups = contiguous_groups_by_qualifier::<NadAny>(&segments);
1337
1338 assert_eq!(groups.len(), 2);
1339 assert_eq!(groups[0].len(), 2);
1340 assert_eq!(groups[1].len(), 1);
1341 }
1342
1343 #[test]
1346 fn message_windows_bytes_yields_complete_windows() {
1347 let input = b"UNB+UNOA:1+S+R+200101:0900+1'\
1348 UNH+1+ORDERS:D:96A:UN'\
1349 BGM+220+PO-001+9'\
1350 UNT+3+1'\
1351 UNZ+1+1'";
1352 let windows: Vec<_> = message_windows_bytes(input)
1353 .collect::<Result<_, _>>()
1354 .unwrap();
1355 assert_eq!(windows.len(), 1);
1356 assert_eq!(windows[0][0].tag, "UNH");
1357 assert_eq!(windows[0].last().unwrap().tag, "UNT");
1358 }
1359
1360 #[test]
1361 fn message_windows_truncated_stream_returns_error() {
1362 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'";
1364 let results: Vec<_> = message_windows_bytes(input).collect();
1365 assert_eq!(results.len(), 1);
1366 assert!(
1367 matches!(results[0], Err(EdifactError::UnexpectedEof { .. })),
1368 "expected UnexpectedEof for truncated window, got: {:?}",
1369 results[0]
1370 );
1371 }
1372
1373 #[test]
1374 fn message_windows_subsequent_calls_return_none_after_truncation() {
1375 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'";
1376 let mut iter = message_windows_bytes(input);
1377 assert!(matches!(
1378 iter.next(),
1379 Some(Err(EdifactError::UnexpectedEof { .. }))
1380 ));
1381 assert!(iter.next().is_none());
1383 }
1384
1385 #[test]
1386 fn message_windows_unh_without_unt_before_next_unh_returns_error() {
1387 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'\
1388 UNH+2+ORDERS:D:96A:UN'BGM+220+PO-002+9'UNT+3+2'";
1389 let results: Vec<_> = message_windows_bytes(input).collect();
1390 assert!(
1392 matches!(
1393 results[0],
1394 Err(EdifactError::InvalidSegmentForMessage { ref tag, .. }) if tag == "UNH"
1395 ),
1396 "expected InvalidSegmentForMessage(UNH), got: {:?}",
1397 results[0]
1398 );
1399 }
1400
1401 fn parse_one(input: &str) -> crate::OwnedSegment {
1404 crate::from_reader(std::io::Cursor::new(input.as_bytes()))
1405 .expect("parse failed")
1406 .into_iter()
1407 .next()
1408 .expect("at least one segment")
1409 }
1410
1411 #[test]
1412 fn segment_accessor_get_element_returns_value() {
1413 let owned = parse_one("BGM+220+PO-001+9'");
1414 let seg = owned.as_borrowed();
1415 assert_eq!(SegmentAccessor::get_element(&seg, 0), Some("220"));
1416 assert_eq!(SegmentAccessor::get_element(&seg, 1), Some("PO-001"));
1417 assert_eq!(SegmentAccessor::get_element(&seg, 2), Some("9"));
1418 assert_eq!(SegmentAccessor::get_element(&seg, 9), None, "out-of-bounds must return None");
1419 }
1420
1421 #[test]
1422 fn segment_accessor_get_element_filters_empty() {
1423 let owned = parse_one("TST+++VALUE'");
1424 let seg = owned.as_borrowed();
1425 assert_eq!(SegmentAccessor::get_element(&seg, 0), None, "empty element must return None");
1427 assert_eq!(SegmentAccessor::get_element(&seg, 1), None, "empty element must return None");
1428 assert_eq!(SegmentAccessor::get_element(&seg, 2), Some("VALUE"));
1429 }
1430
1431 #[test]
1432 fn segment_accessor_get_component_returns_value() {
1433 let owned = parse_one("UNH+1+ORDERS:D:96A:UN'");
1434 let seg = owned.as_borrowed();
1435 assert_eq!(seg.get_component(1, 0), Some("ORDERS"));
1436 assert_eq!(seg.get_component(1, 1), Some("D"));
1437 assert_eq!(seg.get_component(1, 2), Some("96A"));
1438 assert_eq!(seg.get_component(1, 3), Some("UN"));
1439 assert_eq!(seg.get_component(1, 9), None, "out-of-bounds must return None");
1440 }
1441
1442 #[test]
1443 fn segment_accessor_text_element_errors_on_missing() {
1444 let owned = parse_one("BGM+'");
1445 let seg = owned.as_borrowed();
1446 let err = seg.text_element(0);
1448 assert!(
1449 matches!(err, Err(EdifactError::MissingRequiredElement { ref tag, element_index: 0 }) if tag == "BGM"),
1450 "expected MissingRequiredElement, got: {err:?}"
1451 );
1452 }
1453
1454 #[test]
1455 fn segment_accessor_required_composite_errors_on_missing() {
1456 let owned = parse_one("DTM+137'");
1457 let seg = owned.as_borrowed();
1458 let err = seg.required_composite(0, 1);
1460 assert!(
1461 matches!(err, Err(EdifactError::MissingRequiredComponent { ref tag, element_index: 0, component_index: 1 }) if tag == "DTM"),
1462 "expected MissingRequiredComponent, got: {err:?}"
1463 );
1464 }
1465
1466 #[test]
1467 fn segment_accessor_code_element_parses_integer() {
1468 let owned = parse_one("QTY+21:100'");
1469 let seg = owned.as_borrowed();
1470 let qty: u32 = seg.code_element(0).expect("should parse qualifier as u32");
1471 assert_eq!(qty, 21);
1472 }
1473
1474 #[test]
1475 fn segment_accessor_optional_element_absent_returns_none() {
1476 let owned = parse_one("BGM+220'");
1477 let seg = owned.as_borrowed();
1478 assert_eq!(seg.optional_element(5), None);
1479 }
1480}