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> {
36 let borrowed: Vec<Segment<'_>> = segments.iter().map(|s| s.as_borrowed()).collect();
37 Self::edifact_deserialize(&borrowed)
38 }
39}
40
41pub trait EdifactCompositeDeserialize: Sized {
46 fn edifact_deserialize_composite(composite: CompositeElement<'_>)
48 -> Result<Self, EdifactError>;
49}
50
51impl EdifactCompositeDeserialize for Vec<String> {
52 fn edifact_deserialize_composite(
53 composite: CompositeElement<'_>,
54 ) -> Result<Self, EdifactError> {
55 Ok(composite.iter().map(str::to_owned).collect())
56 }
57}
58
59pub trait EdifactSegmentTag {
64 const SEGMENT_TAG: &'static str;
66
67 const QUALIFIER_PATTERN: Option<&'static str> = None;
73
74 fn matches_qualifier(seg: &Segment<'_>) -> bool {
76 match Self::QUALIFIER_PATTERN {
77 Some(pattern) => seg
78 .element_str(0)
79 .is_some_and(|q| qualifier_matches_pattern(q, pattern)),
80 None => true,
81 }
82 }
83
84 fn matches_segment(seg: &Segment<'_>) -> bool {
89 seg.tag == Self::SEGMENT_TAG && Self::matches_qualifier(seg)
90 }
91
92 fn matches_owned_segment(seg: &crate::OwnedSegment) -> bool {
96 if seg.tag != Self::SEGMENT_TAG {
97 return false;
98 }
99 match Self::QUALIFIER_PATTERN {
100 None => true,
101 Some(pattern) => {
102 let q = seg
103 .elements
104 .first()
105 .and_then(|e| e.components.first())
106 .map(|c| c.as_str())
107 .unwrap_or("");
108 qualifier_matches_pattern(q, pattern)
109 }
110 }
111 }
112}
113
114impl<T> EdifactDeserialize for Vec<T>
119where
120 T: EdifactDeserialize + EdifactSegmentTag,
121{
122 fn edifact_deserialize(segments: &[Segment<'_>]) -> Result<Self, EdifactError> {
123 segments
124 .iter()
125 .filter(|s| T::matches_segment(s))
126 .map(|seg| T::edifact_deserialize(std::slice::from_ref(seg)))
127 .collect()
128 }
129
130 fn edifact_deserialize_owned(segments: &[crate::OwnedSegment]) -> Result<Self, EdifactError> {
131 segments
132 .iter()
133 .filter(|s| T::matches_owned_segment(s))
134 .map(|seg| T::edifact_deserialize_owned(std::slice::from_ref(seg)))
135 .collect()
136 }
137}
138
139pub fn deserialize<T: EdifactDeserialize>(input: &[u8]) -> Result<T, EdifactError> {
149 let segments: Vec<Segment<'_>> = crate::from_bytes(input).collect::<Result<_, _>>()?;
150 T::edifact_deserialize(&segments)
151}
152
153pub fn deserialize_first_streaming<T>(input: &[u8]) -> Result<T, EdifactError>
158where
159 T: EdifactDeserialize + EdifactSegmentTag,
160{
161 for segment in crate::from_bytes(input) {
162 let segment = segment?;
163 if T::matches_segment(&segment) {
164 return T::edifact_deserialize(std::slice::from_ref(&segment));
165 }
166 }
167
168 Err(EdifactError::MissingSegment {
169 tag: T::SEGMENT_TAG.to_owned(),
170 expected_position: "any position in input".to_owned(),
171 })
172}
173
174pub fn deserialize_all_streaming<T>(input: &[u8]) -> Result<Vec<T>, EdifactError>
178where
179 T: EdifactDeserialize + EdifactSegmentTag,
180{
181 let mut out = Vec::new();
182 for segment in crate::from_bytes(input) {
183 let segment = segment?;
184 if T::matches_segment(&segment) {
185 out.push(T::edifact_deserialize(std::slice::from_ref(&segment))?);
186 }
187 }
188 Ok(out)
189}
190
191pub fn deserialize_first_from_reader<T, R>(reader: R) -> Result<T, EdifactError>
195where
196 T: EdifactDeserialize + EdifactSegmentTag,
197 R: Read,
198{
199 for segment in crate::from_reader_iter(reader) {
200 let segment = segment?;
201 if !T::matches_owned_segment(&segment) {
203 continue;
204 }
205 let borrowed = segment.as_borrowed();
206 return T::edifact_deserialize(std::slice::from_ref(&borrowed));
207 }
208
209 Err(EdifactError::MissingSegment {
210 tag: T::SEGMENT_TAG.to_owned(),
211 expected_position: "any position in input".to_owned(),
212 })
213}
214
215pub fn deserialize_all_from_reader<T, R>(reader: R) -> Result<Vec<T>, EdifactError>
217where
218 T: EdifactDeserialize + EdifactSegmentTag,
219 R: Read,
220{
221 let mut out = Vec::new();
222 for segment in crate::from_reader_iter(reader) {
223 let segment = segment?;
224 if !T::matches_owned_segment(&segment) {
226 continue;
227 }
228 let borrowed = segment.as_borrowed();
229 out.push(T::edifact_deserialize(std::slice::from_ref(&borrowed))?);
230 }
231 Ok(out)
232}
233
234pub fn deserialize_str<T: EdifactDeserialize>(input: &str) -> Result<T, EdifactError> {
236 deserialize(input.as_bytes())
237}
238
239pub fn find_segment<'s, 'd>(segments: &'s [Segment<'d>], tag: &str) -> Option<&'s Segment<'d>> {
243 segments.iter().find(|s| s.tag == tag)
244}
245
246pub fn find_segments_iter<'s, 'd: 's>(
248 segments: &'s [Segment<'d>],
249 tag: &'s str,
250) -> impl Iterator<Item = &'s Segment<'d>> {
251 segments.iter().filter(move |s| s.tag == tag)
252}
253
254pub fn find_qualified_segment<'s, 'd>(
256 segments: &'s [Segment<'d>],
257 tag: &str,
258 qualifier: &str,
259) -> Option<&'s Segment<'d>> {
260 segments
261 .iter()
262 .find(|s| s.tag == tag && s.element_str(0).unwrap_or("") == qualifier)
263}
264
265pub fn find_segment_typed<'s, 'd, T>(segments: &'s [Segment<'d>]) -> Option<&'s Segment<'d>>
267where
268 T: EdifactSegmentTag,
269{
270 segments.iter().find(|s| T::matches_segment(s))
271}
272
273pub fn find_segments_typed<'s, 'd, T>(
275 segments: &'s [Segment<'d>],
276) -> Vec<&'s Segment<'d>>
277where
278 T: EdifactSegmentTag,
279{
280 segments.iter().filter(|s| T::matches_segment(s)).collect()
281}
282
283pub fn contiguous_groups_by_qualifier<'s, 'd, T>(
285 segments: &'s [Segment<'d>],
286) -> Vec<&'s [Segment<'d>]>
287where
288 T: EdifactSegmentTag,
289{
290 let mut groups = Vec::new();
291 let mut idx = 0;
292
293 while idx < segments.len() {
294 if T::matches_segment(&segments[idx]) {
295 let start = idx;
296 idx += 1;
297 while idx < segments.len() && T::matches_segment(&segments[idx]) {
298 idx += 1;
299 }
300 groups.push(&segments[start..idx]);
301 } else {
302 idx += 1;
303 }
304 }
305
306 groups
307}
308
309pub fn groups_are_contiguous_by_qualifier<T>(segments: &[Segment<'_>]) -> bool
311where
312 T: EdifactSegmentTag,
313{
314 let mut seen_match = false;
315 let mut seen_gap_after_match = false;
316
317 for seg in segments {
318 if T::matches_segment(seg) {
319 if seen_gap_after_match {
320 return false;
321 }
322 seen_match = true;
323 } else if seen_match {
324 seen_gap_after_match = true;
325 }
326 }
327
328 true
329}
330
331pub fn qualifier_matches_pattern(value: &str, pattern: &str) -> bool {
340 if pattern.is_empty() {
341 return value.is_empty();
342 }
343
344 if !pattern.contains('*') {
345 return value == pattern;
346 }
347
348 if let Some((prefix, suffix)) = pattern.split_once('*') {
350 if !pattern[prefix.len() + 1..].contains('*') {
352 return value.len() >= prefix.len() + suffix.len()
353 && value.starts_with(prefix)
354 && value.ends_with(suffix)
355 && {
356 let mid_start = prefix.len();
358 let mid_end = value.len().saturating_sub(suffix.len());
359 mid_start <= mid_end
360 };
361 }
362 }
363
364 let parts: smallvec::SmallVec<[&str; 4]> = pattern.split('*').collect();
366 let prefix = parts[0];
367 let suffix = parts[parts.len() - 1];
368
369 if !value.starts_with(prefix) || !value.ends_with(suffix) {
370 return false;
371 }
372
373 let mid_start = prefix.len();
374 let mid_end = value.len().saturating_sub(suffix.len());
375
376 if mid_start > mid_end {
377 return parts[1..parts.len() - 1].iter().all(|p| p.is_empty());
378 }
379
380 let mut remaining = &value[mid_start..mid_end];
381
382 for part in &parts[1..parts.len() - 1] {
383 if part.is_empty() {
384 continue;
385 }
386 match remaining.find(part) {
387 Some(idx) => remaining = &remaining[idx + part.len()..],
388 None => return false,
389 }
390 }
391
392 true
393}
394
395#[inline]
397pub fn element_str<'s>(seg: &'s Segment<'_>, idx: usize) -> &'s str {
398 seg.element_str(idx).unwrap_or("")
399}
400
401pub fn required_element<'a>(seg: &'a Segment<'_>, idx: usize) -> Result<&'a str, EdifactError> {
407 seg.element_str(idx)
408 .filter(|s| !s.is_empty())
409 .ok_or_else(|| EdifactError::MissingRequiredElement {
410 tag: seg.tag.to_owned(),
411 element_index: idx,
412 })
413}
414
415pub fn optional_element<'a>(seg: &'a Segment<'_>, idx: usize) -> Option<&'a str> {
419 seg.element_str(idx)
420 .filter(|s| !s.is_empty())
421}
422
423pub fn required_component<'a>(
427 seg: &'a Segment<'_>,
428 elem_idx: usize,
429 comp_idx: usize,
430) -> Result<&'a str, EdifactError> {
431 seg.elements
432 .get(elem_idx)
433 .and_then(|elem| elem.get_component(comp_idx))
434 .filter(|s| !s.is_empty())
435 .ok_or_else(|| EdifactError::MissingRequiredElement {
436 tag: seg.tag.to_owned(),
437 element_index: elem_idx,
438 })
439}
440
441pub fn optional_component<'a>(seg: &'a Segment<'_>, elem_idx: usize, comp_idx: usize) -> Option<&'a str> {
445 seg.elements
446 .get(elem_idx)
447 .and_then(|elem| elem.get_component(comp_idx))
448 .filter(|s| !s.is_empty())
449}
450
451pub fn get_components_iter<'a>(
455 seg: &'a Segment<'_>,
456 idx: usize,
457) -> impl Iterator<Item = &'a str> {
458 seg.elements
459 .get(idx)
460 .into_iter()
461 .flat_map(|elem| elem.components.iter().map(|c| c.as_ref()))
462}
463
464pub struct CompositeElement<'a> {
466 components: &'a [std::borrow::Cow<'a, str>],
467}
468
469impl<'a> CompositeElement<'a> {
470 pub fn get(&self, i: usize) -> Option<&'a str> {
472 self.components.get(i).map(|c| c.as_ref())
473 }
474
475 pub fn get_or_empty(&self, i: usize) -> &'a str {
477 self.get(i).unwrap_or("")
478 }
479
480 pub fn len(&self) -> usize {
482 self.components.len()
483 }
484
485 pub fn is_empty(&self) -> bool {
487 self.components.is_empty()
488 }
489
490 pub fn iter(&self) -> impl Iterator<Item = &'a str> {
492 self.components.iter().map(|c| c.as_ref())
493 }
494
495 pub fn from_slice(components: &'a [std::borrow::Cow<'a, str>]) -> Self {
500 Self { components }
501 }
502}
503
504pub fn composite_element<'a>(seg: &'a Segment<'_>, idx: usize) -> Option<CompositeElement<'a>> {
506 seg.elements.get(idx).map(|elem| CompositeElement {
507 components: &elem.components,
508 })
509}
510
511pub fn find_segment_owned<'s>(
518 segments: &'s [crate::OwnedSegment],
519 tag: &str,
520) -> Option<&'s crate::OwnedSegment> {
521 segments.iter().find(|s| s.tag == tag)
522}
523
524pub fn find_qualified_segment_owned<'s>(
532 segments: &'s [crate::OwnedSegment],
533 tag: &str,
534 qualifier: &str,
535) -> Option<&'s crate::OwnedSegment> {
536 segments.iter().find(|s| {
537 s.tag == tag && s.element_str(0).unwrap_or("") == qualifier
538 })
539}
540
541pub trait SegmentAccessor<'a> {
543 fn get_element(&'a self, idx: usize) -> Option<&'a str>;
545 fn get_component(&'a self, elem: usize, comp: usize) -> Option<&'a str>;
547 fn get_composite(&'a self, idx: usize) -> Option<CompositeElement<'a>>;
549
550 fn text_element(&'a self, idx: usize) -> Result<&'a str, EdifactError>;
552 fn optional_element(&'a self, idx: usize) -> Option<&'a str>;
554 fn code_element<T: FromStr>(&'a self, idx: usize) -> Result<T, EdifactError>;
556 fn required_composite(&'a self, elem: usize, comp: usize) -> Result<&'a str, EdifactError>;
558 fn repeating_components(
560 &'a self,
561 elem: usize,
562 start_idx: usize,
563 count: usize,
564 ) -> Result<Vec<&'a str>, EdifactError>;
565
566 fn repeating_components_iter(
571 &'a self,
572 elem: usize,
573 start_idx: usize,
574 count: usize,
575 ) -> impl Iterator<Item = Result<&'a str, EdifactError>> + 'a;
576}
577
578impl<'s, 'd> SegmentAccessor<'s> for Segment<'d>
579where
580 'd: 's,
581{
582 fn get_element(&'s self, idx: usize) -> Option<&'s str> {
583 self.element_str(idx).filter(|s| !s.is_empty())
584 }
585
586 fn get_component(&'s self, elem: usize, comp: usize) -> Option<&'s str> {
587 self.elements
588 .get(elem)
589 .and_then(|e| e.get_component(comp))
590 .filter(|s| !s.is_empty())
591 }
592
593 fn get_composite(&'s self, idx: usize) -> Option<CompositeElement<'s>> {
594 composite_element(self, idx)
595 }
596
597 fn text_element(&'s self, idx: usize) -> Result<&'s str, EdifactError> {
598 <Self as SegmentAccessor>::get_element(self, idx).ok_or_else(|| {
599 EdifactError::MissingRequiredElement {
600 tag: self.tag.to_owned(),
601 element_index: idx,
602 }
603 })
604 }
605
606 fn optional_element(&'s self, idx: usize) -> Option<&'s str> {
607 <Self as SegmentAccessor>::get_element(self, idx)
608 }
609
610 fn code_element<T: FromStr>(&'s self, idx: usize) -> Result<T, EdifactError> {
611 let raw = self.text_element(idx)?;
612 raw.parse::<T>().map_err(|_| EdifactError::InvalidText {
613 offset: self.element_span(idx).map(|s| s.start).unwrap_or(self.span.start),
614 })
615 }
616
617 fn required_composite(&'s self, elem: usize, comp: usize) -> Result<&'s str, EdifactError> {
618 <Self as SegmentAccessor>::get_component(self, elem, comp).ok_or_else(|| {
619 EdifactError::MissingRequiredElement {
620 tag: self.tag.to_owned(),
621 element_index: elem,
622 }
623 })
624 }
625
626 fn repeating_components(
627 &'s self,
628 elem: usize,
629 start_idx: usize,
630 count: usize,
631 ) -> Result<Vec<&'s str>, EdifactError> {
632 let comp =
633 self.get_composite(elem)
634 .ok_or_else(|| EdifactError::MissingRequiredElement {
635 tag: self.tag.to_owned(),
636 element_index: elem,
637 })?;
638
639 (start_idx..start_idx + count)
640 .map(|idx| {
641 comp.get(idx).filter(|s| !s.is_empty()).ok_or_else(|| {
642 EdifactError::MissingRequiredElement {
643 tag: self.tag.to_owned(),
644 element_index: elem,
645 }
646 })
647 })
648 .collect()
649 }
650
651 fn repeating_components_iter(
652 &'s self,
653 elem: usize,
654 start_idx: usize,
655 count: usize,
656 ) -> impl Iterator<Item = Result<&'s str, EdifactError>> + 's {
657 let tag = self.tag;
658 let components = self
659 .elements
660 .get(elem)
661 .map(|e| e.components.as_slice())
662 .unwrap_or(&[]);
663 (start_idx..start_idx + count).map(move |idx| {
664 components
665 .get(idx)
666 .map(|c| c.as_ref())
667 .filter(|s| !s.is_empty())
668 .ok_or_else(|| EdifactError::MissingRequiredElement {
669 tag: tag.to_owned(),
670 element_index: elem,
671 })
672 })
673 }
674}
675
676pub struct MessageWindowsSliceIter<'a> {
687 inner: crate::FromBytesIter<'a>,
688 buf: Vec<crate::Segment<'a>>,
689 in_message: bool,
690 done: bool,
691}
692
693impl<'a> MessageWindowsSliceIter<'a> {
694 fn new(inner: crate::FromBytesIter<'a>) -> Self {
695 Self {
696 inner,
697 buf: Vec::new(),
698 in_message: false,
699 done: false,
700 }
701 }
702}
703
704impl<'a> Iterator for MessageWindowsSliceIter<'a> {
705 type Item = Result<Vec<crate::Segment<'a>>, EdifactError>;
706
707 fn next(&mut self) -> Option<Self::Item> {
708 if self.done {
709 return None;
710 }
711 loop {
712 let segment = match self.inner.next() {
713 Some(Ok(s)) => s,
714 Some(Err(e)) => {
715 self.done = true;
716 return Some(Err(e));
717 }
718 None => {
719 self.done = true;
720 if self.in_message && !self.buf.is_empty() {
721 self.in_message = false;
722 let offset = self.buf.last().map(|s| s.span.end).unwrap_or(0);
723 return Some(Err(EdifactError::UnexpectedEof { offset }));
724 }
725 return None;
726 }
727 };
728
729 match segment.tag {
730 "UNH" => {
731 if self.in_message {
732 self.buf.clear();
733 self.in_message = false;
734 self.done = true;
735 return Some(Err(EdifactError::ValidationFailed {
736 error_count: 1,
737 first_message:
738 "UNH seen while a message window is already open (missing UNT)"
739 .to_owned(),
740 }));
741 }
742 self.buf.clear();
743 self.in_message = true;
744 self.buf.push(segment);
745 }
746 "UNT" if self.in_message => {
747 self.buf.push(segment);
748 self.in_message = false;
749 return Some(Ok(std::mem::take(&mut self.buf)));
750 }
751 _ if self.in_message => {
752 self.buf.push(segment);
753 }
754 _ => {
755 }
757 }
758 }
759 }
760}
761
762pub struct MessageWindowsIter<I> {
783 inner: I,
784 buf: Vec<crate::OwnedSegment>,
785 in_message: bool,
786 done: bool,
789}
790
791impl<I: Iterator<Item = Result<crate::OwnedSegment, EdifactError>>> MessageWindowsIter<I> {
792 pub fn new(inner: I) -> Self {
794 Self {
795 inner,
796 buf: Vec::new(),
797 in_message: false,
798 done: false,
799 }
800 }
801}
802
803impl<I: Iterator<Item = Result<crate::OwnedSegment, EdifactError>>> Iterator
804 for MessageWindowsIter<I>
805{
806 type Item = Result<Vec<crate::OwnedSegment>, EdifactError>;
807
808 fn next(&mut self) -> Option<Self::Item> {
809 if self.done {
810 return None;
811 }
812 loop {
813 let segment = match self.inner.next() {
814 Some(Ok(s)) => s,
815 Some(Err(e)) => {
816 self.done = true;
817 return Some(Err(e));
818 }
819 None => {
820 self.done = true;
821 if self.in_message && !self.buf.is_empty() {
824 self.in_message = false;
825 let offset = self.buf.last().map(|s| s.span.end).unwrap_or(0);
826 return Some(Err(EdifactError::UnexpectedEof { offset }));
827 }
828 return None;
829 }
830 };
831
832 match segment.tag.as_str() {
833 "UNH" => {
834 if self.in_message {
835 self.buf.clear();
837 self.in_message = false;
838 self.done = true;
839 return Some(Err(EdifactError::ValidationFailed {
840 error_count: 1,
841 first_message:
842 "UNH seen while a message window is already open (missing UNT)"
843 .to_owned(),
844 }));
845 }
846 self.buf.clear();
847 self.in_message = true;
848 self.buf.push(segment);
849 }
850 "UNT" if self.in_message => {
851 self.buf.push(segment);
852 self.in_message = false;
853 return Some(Ok(std::mem::take(&mut self.buf)));
854 }
855 _ if self.in_message => {
856 self.buf.push(segment);
857 }
858 _ => {
859 }
861 }
862 }
863 }
864}
865
866pub fn message_windows_bytes(input: &[u8]) -> MessageWindowsSliceIter<'_> {
889 MessageWindowsSliceIter::new(crate::from_bytes(input))
890}
891
892pub fn message_windows_from_reader<R: Read>(
898 reader: R,
899) -> MessageWindowsIter<crate::FromReaderIter<R>> {
900 MessageWindowsIter::new(crate::from_reader_iter(reader))
901}
902
903pub fn deserialize_messages_from_reader<T, R>(
922 reader: R,
923) -> impl Iterator<Item = Result<T, EdifactError>>
924where
925 T: EdifactDeserialize,
926 R: Read,
927{
928 message_windows_from_reader(reader).map(|window| {
929 let window = window?;
930 T::edifact_deserialize_owned(&window)
931 })
932}
933
934pub fn deserialize_messages_bytes<T>(
936 input: &[u8],
937) -> impl Iterator<Item = Result<T, EdifactError>> + '_
938where
939 T: EdifactDeserialize,
940{
941 message_windows_bytes(input).map(|window| {
942 let window = window?;
943 T::edifact_deserialize(&window)
944 })
945}
946
947#[cfg(test)]
948mod tests {
949 use super::*;
950
951 #[derive(Debug, PartialEq)]
953 struct BgmSegment {
954 doc_name_code: String,
955 pruef_id: String,
956 msg_function: Option<String>,
957 }
958
959 impl EdifactSegmentTag for BgmSegment {
960 const SEGMENT_TAG: &'static str = "BGM";
961 }
962
963 struct NadM;
964
965 impl EdifactSegmentTag for NadM {
966 const SEGMENT_TAG: &'static str = "NAD";
967 const QUALIFIER_PATTERN: Option<&'static str> = Some("M*");
968 }
969
970 struct NadWildcard;
971
972 impl EdifactSegmentTag for NadWildcard {
973 const SEGMENT_TAG: &'static str = "NAD";
974 const QUALIFIER_PATTERN: Option<&'static str> = Some("M*");
975 }
976
977 impl EdifactDeserialize for BgmSegment {
978 fn edifact_deserialize(segments: &[Segment<'_>]) -> Result<Self, EdifactError> {
979 let seg = find_segment(segments, "BGM").ok_or_else(|| {
980 EdifactError::MissingRequiredElement {
981 tag: "BGM".to_owned(),
982 element_index: 0,
983 }
984 })?;
985 Ok(Self {
986 doc_name_code: element_str(seg, 0).to_owned(),
987 pruef_id: element_str(seg, 1).to_owned(),
988 msg_function: seg
989 .element_str(2)
990 .filter(|s| !s.is_empty())
991 .map(str::to_owned),
992 })
993 }
994 }
995
996 #[test]
997 fn deserialize_single_segment() {
998 let input = b"BGM+E03+11042+9'";
999 let bgm: BgmSegment = deserialize(input).unwrap();
1000 assert_eq!(bgm.doc_name_code, "E03");
1001 assert_eq!(bgm.pruef_id, "11042");
1002 assert_eq!(bgm.msg_function, Some("9".to_owned()));
1003 }
1004
1005 #[test]
1006 fn streaming_deserialize_first_from_bytes() {
1007 let input = b"UNH+1+ORDERS:D:11A:UN'BGM+E03+11042+9'UNT+3+1'";
1008 let bgm: BgmSegment = deserialize_first_streaming(input).unwrap();
1009 assert_eq!(bgm.pruef_id, "11042");
1010 }
1011
1012 #[test]
1013 fn streaming_deserialize_all_from_bytes() {
1014 let input = b"BGM+E03+11042+9'RFF+AA:1'BGM+E01+11043+9'";
1015 let bgms: Vec<BgmSegment> = deserialize_all_streaming(input).unwrap();
1016 assert_eq!(bgms.len(), 2);
1017 assert_eq!(bgms[0].pruef_id, "11042");
1018 assert_eq!(bgms[1].pruef_id, "11043");
1019 }
1020
1021 #[test]
1022 fn streaming_deserialize_first_from_reader() {
1023 let input = std::io::Cursor::new(b"UNH+1+ORDERS:D:11A:UN'BGM+E03+11042+9'UNT+3+1'".to_vec());
1024 let bgm: BgmSegment = deserialize_first_from_reader(input).unwrap();
1025 assert_eq!(bgm.pruef_id, "11042");
1026 }
1027
1028 #[test]
1029 fn streaming_deserialize_all_from_reader() {
1030 let input = std::io::Cursor::new(b"BGM+E03+11042+9'BGM+E01+11043+9'".to_vec());
1031 let bgms: Vec<BgmSegment> = deserialize_all_from_reader(input).unwrap();
1032 assert_eq!(bgms.len(), 2);
1033 assert_eq!(bgms[0].pruef_id, "11042");
1034 assert_eq!(bgms[1].pruef_id, "11043");
1035 }
1036
1037 #[test]
1038 fn missing_segment_returns_error() {
1039 let input = b"DTM+137:20230401:102'";
1040 let result: Result<BgmSegment, _> = deserialize(input);
1041 assert!(result.is_err());
1042 }
1043
1044 #[test]
1045 fn vec_collects_all_matching_segments() {
1046 let input = b"DTM+137:20230401:102'BGM+E03+11042+9'BGM+E01+11043+9'";
1047 let bgms: Vec<BgmSegment> = deserialize(input).unwrap();
1048 assert_eq!(bgms.len(), 2);
1049 assert_eq!(bgms[0].pruef_id, "11042");
1050 assert_eq!(bgms[1].pruef_id, "11043");
1051 }
1052
1053 #[test]
1054 fn find_qualified_segment_matches_qualifier() {
1055 let input = b"NAD+MS+9900001+293'NAD+MR+9900002+293'";
1056 let segments: Vec<Segment<'_>> =
1057 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1058 let nad_ms = find_qualified_segment(&segments, "NAD", "MS");
1059 let nad_mr = find_qualified_segment(&segments, "NAD", "MR");
1060 assert!(nad_ms.is_some());
1061 assert!(nad_mr.is_some());
1062 assert_eq!(element_str(nad_ms.unwrap(), 0), "MS");
1063 assert_eq!(element_str(nad_mr.unwrap(), 0), "MR");
1064 }
1065
1066 #[test]
1067 fn round_trip_str_api() {
1068 let input = "BGM+E03+11042+9'";
1069 let bgm: BgmSegment = deserialize_str(input).unwrap();
1070 assert_eq!(bgm.pruef_id, "11042");
1071 }
1072
1073 #[test]
1074 fn required_element_extraction() {
1075 let input = b"BGM+E03+11042+9'";
1076 let segments: Vec<Segment<'_>> =
1077 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1078 let seg = &segments[0];
1079
1080 assert_eq!(required_element(seg, 0).unwrap(), "E03");
1081 assert_eq!(required_element(seg, 1).unwrap(), "11042");
1082 assert!(required_element(seg, 5).is_err());
1084 }
1085
1086 #[test]
1087 fn optional_element_extraction() {
1088 let input = b"BGM+E03+11042+9'BGM+E01++absent'";
1089 let segments: Vec<Segment<'_>> =
1090 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1091
1092 assert_eq!(optional_element(&segments[0], 0), Some("E03"));
1094 assert_eq!(optional_element(&segments[0], 1), Some("11042"));
1095 assert_eq!(optional_element(&segments[0], 5), None);
1096
1097 assert_eq!(optional_element(&segments[1], 1), None);
1099 }
1100
1101 #[test]
1102 fn component_extraction() {
1103 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1104 let segments: Vec<Segment<'_>> =
1105 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1106 let seg = &segments[0];
1107
1108 assert_eq!(required_component(seg, 0, 0).unwrap(), "UNOA");
1109 assert_eq!(required_component(seg, 0, 1).unwrap(), "1");
1110 assert!(required_component(seg, 0, 5).is_err());
1112 }
1113
1114 #[test]
1115 fn composite_element_helper() {
1116 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1117 let segments: Vec<Segment<'_>> =
1118 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1119 let seg = &segments[0];
1120
1121 let comp = composite_element(seg, 0).unwrap();
1122 assert_eq!(comp.len(), 2);
1123 assert_eq!(comp.get(0), Some("UNOA"));
1124 assert_eq!(comp.get(1), Some("1"));
1125 assert_eq!(comp.get(5), None);
1126 assert_eq!(comp.get_or_empty(5), "");
1127 }
1128
1129 #[test]
1130 fn get_all_components() {
1131 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1133 let segments: Vec<Segment<'_>> =
1134 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1135 let seg = &segments[0];
1136
1137 let comps: Vec<&str> = get_components_iter(seg, 0).collect(); assert!(!comps.is_empty(), "Expected components but got empty");
1139 assert_eq!(comps.len(), 2);
1140 assert_eq!(comps[0], "UNOA");
1141 assert_eq!(comps[1], "1");
1142 }
1143
1144 #[test]
1145 fn qualifier_pattern_matching_supports_exact_and_wildcard() {
1146 assert!(qualifier_matches_pattern("MS", "MS"));
1148 assert!(!qualifier_matches_pattern("MS", "M")); assert!(qualifier_matches_pattern("MS", "M*"));
1151 assert!(qualifier_matches_pattern("MRY", "M*Y"));
1152 assert!(!qualifier_matches_pattern("AB", "M*"));
1153 }
1154
1155 #[test]
1157 fn qualifier_matches_pattern_table() {
1158 let cases: &[(&str, &str, bool)] = &[
1160 ("", "", true), ("", "*", true), ("A", "", false), ("", "A", false), ("MS", "MS", true),
1167 ("BY", "BY", true),
1168 ("ms", "MS", false), ("MSX", "MS", false), ("M", "MS", false), ("MS", "M*", true),
1173 ("MULTI", "MUL*", true),
1174 ("AB", "M*", false),
1175 ("", "M*", false), ("MSG", "*G", true),
1178 ("G", "*G", true),
1179 ("MSG", "*X", false),
1180 ("", "*G", false),
1181 ("MRY", "M*Y", true),
1183 ("MAY", "M*Y", true),
1184 ("MY", "M*Y", true), ("MYY", "M*Y", true), ("MAYZ", "M*Y", false),("AB", "M*Y", false),
1188 ("*", "*", true), ("anything", "*", true),
1191 ("", "*", true),
1192 ("ABCDE", "A*C*E", true),
1194 ("ACE", "A*C*E", true), ("AXCYE", "A*C*E", true),
1196 ("ABCDF", "A*C*E", false),
1197 ("AB", "A**B", true), ("AB", "A*B*C", false),
1201 ("XMS", "MS", false),
1203 ];
1204
1205 for (value, pattern, expected) in cases {
1206 let got = qualifier_matches_pattern(value, pattern);
1207 assert_eq!(
1208 got, *expected,
1209 "qualifier_matches_pattern({value:?}, {pattern:?}) expected {expected} but got {got}"
1210 );
1211 }
1212 }
1213
1214 #[test]
1215 fn typed_qualifier_helpers_work() {
1216 let input = b"NAD+MS+9900001+293'NAD+MR+9900002+293'";
1217 let segments: Vec<Segment<'_>> =
1218 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1219
1220 let first = find_segment_typed::<NadM>(&segments).unwrap();
1221 assert_eq!(first.element_str(0), Some("MS"));
1222
1223 let all = find_segments_typed::<NadWildcard>(&segments);
1224 assert_eq!(all.len(), 2);
1225 }
1226
1227 #[test]
1228 fn segment_accessor_trait_methods_work() {
1229 let input = b"UNB+UNOA:1+SENDER+RECEIVER+200101:0900+1'";
1230 let segments: Vec<Segment<'_>> =
1231 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1232 let seg = &segments[0];
1233
1234 assert_eq!(SegmentAccessor::get_element(seg, 1), Some("SENDER"));
1235 assert_eq!(SegmentAccessor::required_composite(seg, 0, 1).unwrap(), "1");
1236 let parsed: i32 = SegmentAccessor::code_element(seg, 4).unwrap();
1237 assert_eq!(parsed, 1);
1238 let reps = SegmentAccessor::repeating_components(seg, 3, 0, 2).unwrap();
1239 assert_eq!(reps, vec!["200101", "0900"]);
1240 }
1241
1242 #[test]
1243 fn group_helpers_detect_contiguity() {
1244 struct NadAny;
1245 impl EdifactSegmentTag for NadAny {
1246 const SEGMENT_TAG: &'static str = "NAD";
1247 }
1248
1249 let contiguous_input = b"NAD+MS+1'NAD+MR+2'RFF+AA:1'";
1250 let contiguous_segments: Vec<Segment<'_>> = crate::from_bytes(contiguous_input)
1251 .collect::<Result<_, _>>()
1252 .unwrap();
1253 assert!(groups_are_contiguous_by_qualifier::<NadAny>(
1254 &contiguous_segments
1255 ));
1256
1257 let non_contiguous_input = b"NAD+MS+1'RFF+AA:1'NAD+MR+2'";
1258 let non_contiguous_segments: Vec<Segment<'_>> = crate::from_bytes(non_contiguous_input)
1259 .collect::<Result<_, _>>()
1260 .unwrap();
1261 assert!(!groups_are_contiguous_by_qualifier::<NadAny>(
1262 &non_contiguous_segments
1263 ));
1264 }
1265
1266 #[test]
1267 fn group_helpers_collect_contiguous_groups() {
1268 struct NadAny;
1269 impl EdifactSegmentTag for NadAny {
1270 const SEGMENT_TAG: &'static str = "NAD";
1271 }
1272
1273 let input = b"NAD+MS+1'NAD+MR+2'RFF+AA:1'NAD+BY+3'";
1274 let segments: Vec<Segment<'_>> =
1275 crate::from_bytes(input).collect::<Result<_, _>>().unwrap();
1276 let groups = contiguous_groups_by_qualifier::<NadAny>(&segments);
1277
1278 assert_eq!(groups.len(), 2);
1279 assert_eq!(groups[0].len(), 2);
1280 assert_eq!(groups[1].len(), 1);
1281 }
1282
1283 #[test]
1286 fn message_windows_bytes_yields_complete_windows() {
1287 let input = b"UNB+UNOA:1+S+R+200101:0900+1'\
1288 UNH+1+ORDERS:D:96A:UN'\
1289 BGM+220+PO-001+9'\
1290 UNT+3+1'\
1291 UNZ+1+1'";
1292 let windows: Vec<_> = message_windows_bytes(input)
1293 .collect::<Result<_, _>>()
1294 .unwrap();
1295 assert_eq!(windows.len(), 1);
1296 assert_eq!(windows[0][0].tag, "UNH");
1297 assert_eq!(windows[0].last().unwrap().tag, "UNT");
1298 }
1299
1300 #[test]
1301 fn message_windows_truncated_stream_returns_error() {
1302 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'";
1304 let results: Vec<_> = message_windows_bytes(input).collect();
1305 assert_eq!(results.len(), 1);
1306 assert!(
1307 matches!(results[0], Err(EdifactError::UnexpectedEof { .. })),
1308 "expected UnexpectedEof for truncated window, got: {:?}",
1309 results[0]
1310 );
1311 }
1312
1313 #[test]
1314 fn message_windows_subsequent_calls_return_none_after_truncation() {
1315 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'";
1316 let mut iter = message_windows_bytes(input);
1317 assert!(matches!(
1318 iter.next(),
1319 Some(Err(EdifactError::UnexpectedEof { .. }))
1320 ));
1321 assert!(iter.next().is_none());
1323 }
1324
1325 #[test]
1326 fn message_windows_unh_without_unt_before_next_unh_returns_error() {
1327 let input = b"UNH+1+ORDERS:D:96A:UN'BGM+220+PO-001+9'\
1328 UNH+2+ORDERS:D:96A:UN'BGM+220+PO-002+9'UNT+3+2'";
1329 let results: Vec<_> = message_windows_bytes(input).collect();
1330 assert!(
1332 matches!(results[0], Err(EdifactError::ValidationFailed { .. })),
1333 "expected ValidationFailed, got: {:?}",
1334 results[0]
1335 );
1336 }
1337}