1use std::io::{BufReader, Read};
15
16use edifact_rs::{MessageWindowsIter, OwnedSegment, ReaderConfig, from_bufread_stream_with_config};
17
18use crate::{AnyMessage, Error, MessageType};
19
20fn sanitize_release_code(s: &str) -> std::borrow::Cow<'_, str> {
28 const MAX_LEN: usize = 16;
29 if s.len() <= MAX_LEN && s.chars().all(|c| c.is_ascii_alphanumeric() || c == '.') {
30 std::borrow::Cow::Borrowed(s)
31 } else {
32 std::borrow::Cow::Owned(format!("<invalid-code:{} bytes>", s.len()))
33 }
34}
35
36fn resolve_pid_source(
46 msg_type_code: &str,
47 assoc_code: &str,
48 registry: &crate::registry::ReleaseRegistry,
49) -> crate::registry::PidSource {
50 MessageType::from_unh_code(msg_type_code)
51 .and_then(|mt| {
52 let rel = crate::release::Release::new(assoc_code);
53 registry.profile(mt, &rel).ok()
54 })
55 .map(super::registry::Profile::pid_source)
56 .unwrap_or_default()
57}
58
59pub(crate) fn resolve_pid_source_pub(
61 msg_type_code: &str,
62 assoc_code: &str,
63 registry: &crate::registry::ReleaseRegistry,
64) -> crate::registry::PidSource {
65 resolve_pid_source(msg_type_code, assoc_code, registry)
66}
67
68pub const DEFAULT_MAX_SEGMENT_BYTES: usize = 64 * 1024;
75
76#[derive(Debug, Clone, Copy)]
83pub struct ParseConfig {
84 pub max_segment_bytes: usize,
89 pub max_segments: Option<usize>,
93 pub max_input_bytes: Option<usize>,
97 pub max_messages_per_interchange: Option<usize>,
104 pub max_messages: Option<usize>,
112 pub max_segments_per_message: Option<usize>,
125 pub reference_date: Option<time::Date>,
140}
141
142impl Default for ParseConfig {
143 fn default() -> Self {
144 Self {
145 max_segment_bytes: DEFAULT_MAX_SEGMENT_BYTES,
146 max_segments: Some(10_000),
147 max_input_bytes: Some(10 * 1024 * 1024),
148 max_messages_per_interchange: Some(1_000),
149 max_segments_per_message: Some(500),
150 max_messages: None,
151 reference_date: None,
152 }
153 }
154}
155
156impl ParseConfig {
157 #[must_use]
161 pub fn with_reference_date(mut self, date: time::Date) -> Self {
162 self.reference_date = Some(date);
163 self
164 }
165
166 pub(crate) fn to_reader_config(self) -> ReaderConfig {
167 let mut cfg = ReaderConfig::default().max_segment_bytes(self.max_segment_bytes);
168 if let Some(n) = self.max_segments {
169 cfg = cfg.max_segments(n);
170 }
171 if let Some(n) = self.max_input_bytes {
172 cfg = cfg.max_input_bytes(n as u64);
173 }
174 if let Some(n) = self.max_messages {
175 cfg = cfg.max_messages(n);
176 }
177 cfg
178 }
179}
180
181pub fn parse_envelope_only(input: &[u8]) -> Result<crate::light_message::LightMessage, Error> {
199 let cfg = ParseConfig::default().to_reader_config();
200 let segments: Vec<OwnedSegment> = edifact_rs::from_bytes_owned_with_config(input, cfg)
201 .collect::<Result<_, _>>()
202 .map_err(Error::Parse)?;
203 crate::light_message::LightMessage::from_segments(
204 segments,
205 crate::registry::ReleaseRegistry::global(),
206 )
207}
208
209pub fn parse(input: &[u8]) -> Result<AnyMessage, Error> {
219 parse_with_registry(
220 input,
221 ParseConfig::default(),
222 crate::registry::ReleaseRegistry::global(),
223 )
224}
225
226pub(crate) fn parse_with_registry(
230 input: &[u8],
231 config: ParseConfig,
232 registry: &crate::registry::ReleaseRegistry,
233) -> Result<AnyMessage, Error> {
234 let per_msg_limit = config.max_segments_per_message;
235 let cfg = config.to_reader_config();
236 let segments: Vec<OwnedSegment> = edifact_rs::from_bytes_owned_with_config(input, cfg)
237 .collect::<Result<_, _>>()
238 .map_err(Error::Parse)?;
239 if let Some(lim) = per_msg_limit {
240 let actual = segments.len();
241 if actual > lim {
242 return Err(Error::TooManySegmentsInMessage { limit: lim, actual });
243 }
244 }
245 dispatch_message(segments, registry)
246}
247
248pub fn parse_interchange(reader: impl Read) -> impl Iterator<Item = Result<AnyMessage, Error>> {
260 parse_interchange_with_registry(reader, ParseConfig::default())
261}
262
263pub(crate) fn parse_interchange_with_registry(
268 reader: impl Read,
269 config: ParseConfig,
270) -> impl Iterator<Item = Result<AnyMessage, Error>> {
271 parse_interchange_impl(
272 reader,
273 config,
274 std::sync::Arc::clone(crate::registry::ReleaseRegistry::global_arc()),
275 )
276}
277
278pub(crate) fn parse_interchange_impl(
284 reader: impl Read,
285 config: ParseConfig,
286 registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
287) -> impl Iterator<Item = Result<AnyMessage, Error>> {
288 let limit = config.max_messages_per_interchange;
289 let per_msg_limit = config.max_segments_per_message;
290 let cfg = config.to_reader_config();
291 MessageWindowsIter::new(from_bufread_stream_with_config(BufReader::new(reader), cfg))
292 .enumerate()
293 .map(move |(index, window)| {
294 if let Some(lim) = limit {
295 if index >= lim {
296 return Err(Error::TooManyMessages { limit: lim });
297 }
298 }
299 let window = window.map_err(Error::Parse)?;
300 if let Some(lim) = per_msg_limit {
301 let actual = window.segments.len();
302 if actual > lim {
303 return Err(Error::TooManySegmentsInMessage { limit: lim, actual });
304 }
305 }
306 dispatch_message(window.segments, ®istry)
307 })
308}
309
310pub(crate) fn parse_interchange_with_arc_registry(
315 reader: impl Read,
316 config: ParseConfig,
317 registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
318) -> impl Iterator<Item = Result<AnyMessage, Error>> {
319 parse_interchange_impl(reader, config, registry)
320}
321
322#[cfg_attr(
328 feature = "tracing",
329 tracing::instrument(skip(reader, config, registry))
330)]
331pub(crate) fn parse_interchange_buffered_impl(
332 reader: impl Read,
333 config: ParseConfig,
334 registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
335) -> Result<(crate::interchange::InterchangeHeader, InterchangeIter), Error> {
336 let cfg = config.to_reader_config();
337 let segments: Vec<OwnedSegment> = from_bufread_stream_with_config(BufReader::new(reader), cfg)
338 .collect::<Result<_, _>>()
339 .map_err(Error::Parse)?;
340
341 let header = parse_interchange_header_from_segments(&segments)?;
343
344 let unz_ref = segments
345 .iter()
346 .rfind(|s| s.tag == "UNZ")
347 .and_then(|unz| unz.element_str(1))
348 .map(str::to_owned);
349 let declared_count = segments
350 .iter()
351 .rfind(|s| s.tag == "UNZ")
352 .and_then(|unz| unz.element_str(0))
353 .and_then(|s| s.parse::<usize>().ok())
354 .unwrap_or(0);
355
356 let msg_iter = MessageWindowsIter::new(segments.into_iter().map(
357 Ok::<_, edifact_rs::EdifactError>
358 as fn(OwnedSegment) -> Result<OwnedSegment, edifact_rs::EdifactError>,
359 ));
360
361 let iter = InterchangeIter {
362 inner: msg_iter,
363 header: header.clone(),
364 registry,
365 limit: config.max_messages_per_interchange,
366 index: 0,
367 actual_count: 0,
368 declared_count,
369 unz_ref,
370 unz_checked: false,
371 done: false,
372 };
373
374 Ok((header, iter))
375}
376
377#[derive(Debug, Clone)]
423pub struct Parser {
424 config: ParseConfig,
425}
426
427impl Default for Parser {
428 fn default() -> Self {
429 Self::new()
430 }
431}
432
433impl Parser {
434 #[must_use]
436 pub fn new() -> Self {
437 Self {
438 config: ParseConfig::default(),
439 }
440 }
441
442 #[must_use]
444 pub fn with_config(config: ParseConfig) -> Self {
445 Self { config }
446 }
447
448 #[cfg_attr(
454 feature = "tracing",
455 tracing::instrument(skip(self, input), fields(bytes = input.len()))
456 )]
457 pub fn parse(&self, input: &[u8]) -> Result<AnyMessage, Error> {
458 parse_with_registry(
459 input,
460 self.config,
461 crate::registry::ReleaseRegistry::global(),
462 )
463 }
464
465 pub fn parse_reader(&self, reader: impl Read) -> Result<AnyMessage, Error> {
475 let cfg = self.config.to_reader_config();
476 let segments: Vec<OwnedSegment> =
477 from_bufread_stream_with_config(BufReader::new(reader), cfg)
478 .collect::<Result<_, _>>()
479 .map_err(Error::Parse)?;
480 dispatch_message(segments, crate::registry::ReleaseRegistry::global())
481 }
482
483 pub fn parse_envelope_only(
497 &self,
498 input: &[u8],
499 ) -> Result<crate::light_message::LightMessage, Error> {
500 let cfg = self.config.to_reader_config();
501 let segments: Vec<OwnedSegment> = edifact_rs::from_bytes_owned_with_config(input, cfg)
502 .collect::<Result<_, _>>()
503 .map_err(Error::Parse)?;
504 crate::light_message::LightMessage::from_segments(
505 segments,
506 crate::registry::ReleaseRegistry::global(),
507 )
508 }
509
510 pub fn parse_interchange(
527 &self,
528 reader: impl Read,
529 ) -> impl Iterator<Item = Result<AnyMessage, Error>> {
530 parse_interchange_with_registry(reader, self.config)
531 }
532
533 pub fn parse_interchange_buffered(
553 &self,
554 reader: impl Read,
555 ) -> Result<(crate::interchange::InterchangeHeader, InterchangeIter), Error> {
556 parse_interchange_buffered_impl(
557 reader,
558 self.config,
559 std::sync::Arc::clone(crate::registry::ReleaseRegistry::global_arc()),
560 )
561 }
562
563 pub fn parse_interchange_full(
578 &self,
579 reader: impl Read,
580 ) -> Result<crate::interchange::ParsedInterchange, Error> {
581 let reader_cfg = self.config.to_reader_config();
582 let segments: Vec<OwnedSegment> =
583 from_bufread_stream_with_config(BufReader::new(reader), reader_cfg)
584 .collect::<Result<_, _>>()
585 .map_err(Error::Parse)?;
586 parse_interchange_full_from_segments(segments, &self.config)
587 }
588}
589
590pub struct InterchangeIter {
599 #[expect(clippy::type_complexity)]
600 inner: MessageWindowsIter<
601 std::iter::Map<
602 std::vec::IntoIter<OwnedSegment>,
603 fn(OwnedSegment) -> Result<OwnedSegment, edifact_rs::EdifactError>,
604 >,
605 >,
606 header: crate::interchange::InterchangeHeader,
607 registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
608 limit: Option<usize>,
609 index: usize,
610 actual_count: usize,
611 declared_count: usize,
612 unz_ref: Option<String>,
613 unz_checked: bool,
614 done: bool,
615}
616
617impl Iterator for InterchangeIter {
618 type Item = Result<crate::interchange::MessageEnvelope, Error>;
619
620 fn next(&mut self) -> Option<Self::Item> {
621 if self.done {
622 return None;
623 }
624
625 if let Some(window_result) = self.inner.next() {
627 let index = self.index;
628 self.index += 1;
629 self.actual_count += 1;
630
631 if let Some(lim) = self.limit {
633 if index >= lim {
634 self.done = true;
635 return Some(Err(Error::TooManyMessages { limit: lim }));
636 }
637 }
638
639 let result = (|| {
640 let window = window_result.map_err(Error::Parse)?;
641 let message = dispatch_message(window.segments, &self.registry)?;
642 Ok(crate::interchange::MessageEnvelope {
643 message,
644 header: self.header.clone(),
645 message_index: index,
646 })
647 })();
648 Some(result)
649 } else {
650 if self.unz_checked {
652 self.done = true;
653 return None;
654 }
655 self.unz_checked = true;
656 if let Some(ref uref) = self.unz_ref {
658 if !uref.is_empty() && uref.as_str() != self.header.control_ref.as_ref() {
659 self.done = true;
660 return Some(Err(Error::InterchangeRefMismatch {
661 unb_ref: self.header.control_ref.to_string(),
662 unz_ref: uref.clone(),
663 }));
664 }
665 }
666 if self.declared_count != 0 && self.declared_count != self.actual_count {
668 self.done = true;
669 return Some(Err(Error::InterchangeCountMismatch {
670 declared: self.declared_count,
671 actual: self.actual_count,
672 }));
673 }
674 self.done = true;
675 None
676 }
677 }
678}
679
680fn parse_interchange_header_from_segments(
683 segments: &[OwnedSegment],
684) -> Result<crate::interchange::InterchangeHeader, Error> {
685 use crate::interchange::InterchangeHeader;
686 let unb = segments
687 .iter()
688 .find(|s| s.tag == "UNB")
689 .ok_or(Error::MissingSegment("UNB"))?;
690
691 let syntax_id = unb.component_str(0, 0).unwrap_or("UNOC").to_owned();
692 let syntax_version: u8 = unb
693 .component_str(0, 1)
694 .and_then(|s| s.parse().ok())
695 .unwrap_or(3);
696 let sender_id = unb.component_str(1, 0).unwrap_or("").to_owned();
697 let sender_qualifier = unb.component_str(1, 2).unwrap_or("").to_owned();
698 let receiver_id = unb.component_str(2, 0).unwrap_or("").to_owned();
699 let receiver_qualifier = unb.component_str(2, 2).unwrap_or("").to_owned();
700 let transmission_datetime = parse_unb_datetime(
701 unb.component_str(3, 0).unwrap_or(""),
702 unb.component_str(3, 1).unwrap_or(""),
703 );
704 let control_ref = unb.element_str(4).unwrap_or("").to_owned();
705 let test_indicator = unb.element_str(10).is_some_and(|v| v.trim() == "1");
706
707 Ok(InterchangeHeader {
708 sender_id: sender_id.into_boxed_str(),
709 sender_qualifier: sender_qualifier.into_boxed_str(),
710 receiver_id: receiver_id.into_boxed_str(),
711 receiver_qualifier: receiver_qualifier.into_boxed_str(),
712 transmission_datetime,
713 control_ref: control_ref.into_boxed_str(),
714 syntax_id: syntax_id.into_boxed_str(),
715 syntax_version,
716 test_indicator,
717 })
718}
719
720fn parse_interchange_full_from_segments(
722 segments: Vec<OwnedSegment>,
723 config: &ParseConfig,
724) -> Result<crate::interchange::ParsedInterchange, Error> {
725 parse_interchange_full_from_segments_with_registry(
726 segments,
727 config,
728 std::sync::Arc::clone(crate::registry::ReleaseRegistry::global_arc()),
729 )
730}
731
732pub(crate) fn parse_interchange_full_with_arc_registry(
734 data: &[u8],
735 config: ParseConfig,
736 registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
737) -> Result<crate::interchange::ParsedInterchange, Error> {
738 let cfg = config.to_reader_config();
739 let segments: Vec<OwnedSegment> = edifact_rs::from_bytes_owned_with_config(data, cfg)
740 .collect::<Result<_, _>>()
741 .map_err(Error::Parse)?;
742 parse_interchange_full_from_segments_with_registry(segments, &config, registry)
743}
744
745#[expect(clippy::needless_pass_by_value)]
746fn parse_interchange_full_from_segments_with_registry(
747 segments: Vec<OwnedSegment>,
748 config: &ParseConfig,
749 registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
750) -> Result<crate::interchange::ParsedInterchange, Error> {
751 use crate::interchange::{InterchangeHeader, MessageEnvelope, ParsedInterchange};
752
753 let unb = segments
755 .iter()
756 .find(|s| s.tag == "UNB")
757 .ok_or(Error::MissingSegment("UNB"))?;
758
759 let syntax_id = unb.component_str(0, 0).unwrap_or("UNOC").to_owned();
761 let syntax_version: u8 = unb
762 .component_str(0, 1)
763 .and_then(|s| s.parse().ok())
764 .unwrap_or(3);
765
766 let sender_id = unb.component_str(1, 0).unwrap_or("").to_owned();
768 let sender_qualifier = unb.component_str(1, 2).unwrap_or("").to_owned();
769
770 let receiver_id = unb.component_str(2, 0).unwrap_or("").to_owned();
772 let receiver_qualifier = unb.component_str(2, 2).unwrap_or("").to_owned();
773
774 let transmission_datetime = parse_unb_datetime(
776 unb.component_str(3, 0).unwrap_or(""),
777 unb.component_str(3, 1).unwrap_or(""),
778 );
779
780 let control_ref = unb.element_str(4).unwrap_or("").to_owned();
782
783 #[cfg(feature = "tracing")]
784 let _span = tracing::debug_span!(
785 "parse_interchange",
786 sender = %sender_id,
787 receiver = %receiver_id,
788 control_ref = %control_ref,
789 segment_count = segments.len(),
790 )
791 .entered();
792
793 let test_indicator = unb.element_str(10).is_some_and(|v| v.trim() == "1");
796
797 let header = InterchangeHeader {
798 sender_id: sender_id.into_boxed_str(),
799 sender_qualifier: sender_qualifier.into_boxed_str(),
800 receiver_id: receiver_id.into_boxed_str(),
801 receiver_qualifier: receiver_qualifier.into_boxed_str(),
802 transmission_datetime,
803 control_ref: control_ref.into_boxed_str(),
804 syntax_id: syntax_id.into_boxed_str(),
805 syntax_version,
806 test_indicator,
807 };
808
809 let unz_seg = segments.iter().rfind(|s| s.tag == "UNZ");
811 let (trailer_ref, declared_message_count) = match unz_seg {
812 Some(unz_seg) => {
813 let count: usize = unz_seg
814 .element_str(0)
815 .and_then(|s| s.parse().ok())
816 .unwrap_or(0);
817 let tref = unz_seg.element_str(1).unwrap_or("").to_owned();
818 (tref.into_boxed_str(), count)
819 }
820 None => ("".into(), 0),
821 };
822
823 let msg_iter = edifact_rs::MessageWindowsIter::new(
825 segments.into_iter().map(Ok::<_, edifact_rs::EdifactError>),
826 );
827
828 let mut messages: Vec<MessageEnvelope> = Vec::new();
829 for (index, window_result) in msg_iter.enumerate() {
830 if let Some(limit) = config.max_messages_per_interchange {
832 if index >= limit {
833 return Err(Error::TooManyMessages { limit });
834 }
835 }
836 let window = window_result.map_err(Error::Parse)?;
837 let message = dispatch_message(window.segments, ®istry)?;
838 messages.push(MessageEnvelope {
839 message,
840 header: header.clone(),
841 message_index: index,
842 });
843 }
844
845 if !trailer_ref.is_empty() && trailer_ref.as_ref() != header.control_ref.as_ref() {
847 return Err(Error::InterchangeRefMismatch {
848 unb_ref: header.control_ref.to_string(),
849 unz_ref: trailer_ref.to_string(),
850 });
851 }
852
853 if declared_message_count != 0 && declared_message_count != messages.len() {
855 return Err(Error::InterchangeCountMismatch {
856 declared: declared_message_count,
857 actual: messages.len(),
858 });
859 }
860
861 Ok(ParsedInterchange {
862 header,
863 messages,
864 trailer_ref,
865 declared_message_count,
866 })
867}
868
869fn parse_unb_datetime(date: &str, time: &str) -> Option<time::OffsetDateTime> {
873 use time::{Date, Month, OffsetDateTime, Time, UtcOffset};
874
875 let (year, month_n, day) = match date.len() {
876 6 => {
877 let yy: i32 = date[0..2].parse().ok()?;
879 let mm: u8 = date[2..4].parse().ok()?;
880 let dd: u8 = date[4..6].parse().ok()?;
881 (2000 + yy, mm, dd)
882 }
883 8 => {
884 let yyyy: i32 = date[0..4].parse().ok()?;
885 let mm: u8 = date[4..6].parse().ok()?;
886 let dd: u8 = date[6..8].parse().ok()?;
887 (yyyy, mm, dd)
888 }
889 _ => return None,
890 };
891
892 let month = Month::try_from(month_n).ok()?;
893 let d = Date::from_calendar_date(year, month, day).ok()?;
894
895 let (hh, mi, ss) = match time.len() {
896 4 => {
897 let hh: u8 = time[0..2].parse().ok()?;
898 let mi: u8 = time[2..4].parse().ok()?;
899 (hh, mi, 0u8)
900 }
901 6 => {
902 let hh: u8 = time[0..2].parse().ok()?;
903 let mi: u8 = time[2..4].parse().ok()?;
904 let ss: u8 = time[4..6].parse().ok()?;
905 (hh, mi, ss)
906 }
907 _ => return None,
908 };
909
910 let t = Time::from_hms(hh, mi, ss).ok()?;
911 Some(OffsetDateTime::new_utc(d, t).replace_offset(UtcOffset::UTC))
912}
913
914pub(crate) fn dispatch_message(
919 segments: Vec<OwnedSegment>,
920 registry: &crate::registry::ReleaseRegistry,
921) -> Result<AnyMessage, Error> {
922 let (message_ref, msg_type_code, assoc_code) = {
926 let unh = segments
927 .iter()
928 .find(|s| s.tag == "UNH")
929 .ok_or(Error::MissingSegment("UNH"))?;
930
931 let message_ref = unh.element_str(0).unwrap_or_default().to_owned();
932 let msg_type_code = unh
936 .component_str(1, 0)
937 .ok_or(Error::MalformedSegment("UNH"))?
938 .to_owned();
939 let assoc_code = unh.component_str(1, 4).unwrap_or_default().to_owned();
940 (message_ref, msg_type_code, assoc_code)
941 };
942
943 let pruefidentifikator: Option<u32> =
948 match resolve_pid_source(&msg_type_code, &assoc_code, registry) {
949 crate::registry::PidSource::RffZ13 => segments
950 .iter()
951 .find(|s| s.tag == "RFF" && (s.element_str(0) == Some("Z13")))
952 .and_then(|rff| rff.component_str(0, 1))
953 .and_then(|s| s.parse().ok()),
954 crate::registry::PidSource::BgmDe1004 => segments
955 .iter()
956 .find(|s| s.tag == "BGM")
957 .and_then(|bgm| bgm.element_str(1))
958 .and_then(|s| s.parse().ok()),
959 };
960
961 if matches!(
965 crate::release::Release::new(&assoc_code).kind(),
966 crate::release::ReleaseKind::Opaque(_)
967 ) {
968 let safe_code = sanitize_release_code(&assoc_code);
972 #[cfg(feature = "tracing")]
973 tracing::warn!(
974 release = %safe_code,
975 "unrecognised EDI@Energy release code — validate() will return ProfileNotFound"
976 );
977 #[cfg(not(feature = "tracing"))]
980 eprintln!(
981 "edi-energy: warning: unrecognised release code `{safe_code}` — \
982 validate() will return ProfileNotFound"
983 );
984 } else {
985 #[cfg(feature = "tracing")]
986 tracing::debug!(
987 message_type = %msg_type_code,
988 release = %assoc_code,
989 segment_count = segments.len(),
990 "parsed EDIFACT message"
991 );
992 }
993
994 dispatch_by_type(
995 &msg_type_code,
996 segments,
997 message_ref,
998 assoc_code,
999 pruefidentifikator,
1000 )
1001}
1002
1003#[allow(unused_variables)] #[allow(clippy::too_many_lines)]
1006fn dispatch_by_type(
1007 msg_type_code: &str,
1008 segments: Vec<OwnedSegment>,
1009 message_ref: String,
1010 assoc_code: String,
1011 pruefidentifikator: Option<u32>,
1012) -> Result<AnyMessage, Error> {
1013 match msg_type_code {
1014 #[cfg(feature = "utilmd")]
1015 "UTILMD" => Ok(AnyMessage::Utilmd(
1016 crate::messages::utilmd::UtilmdMessage::from_parts(
1017 segments,
1018 message_ref,
1019 assoc_code,
1020 pruefidentifikator,
1021 ),
1022 )),
1023 #[cfg(feature = "mscons")]
1024 "MSCONS" => Ok(AnyMessage::Mscons(
1025 crate::messages::mscons::MsconsMessage::from_parts(
1026 segments,
1027 message_ref,
1028 assoc_code,
1029 pruefidentifikator,
1030 ),
1031 )),
1032 #[cfg(feature = "aperak")]
1033 "APERAK" => Ok(AnyMessage::Aperak(
1034 crate::messages::aperak::AperakMessage::from_parts(
1035 segments,
1036 message_ref,
1037 assoc_code,
1038 pruefidentifikator,
1039 ),
1040 )),
1041 #[cfg(feature = "contrl")]
1042 "CONTRL" => Ok(AnyMessage::Contrl(
1043 crate::messages::contrl::ContrlMessage::from_parts(
1044 segments,
1045 message_ref,
1046 assoc_code,
1047 pruefidentifikator,
1048 ),
1049 )),
1050 #[cfg(feature = "invoic")]
1051 "INVOIC" => Ok(AnyMessage::Invoic(
1052 crate::messages::invoic::InvoicMessage::from_parts(
1053 segments,
1054 message_ref,
1055 assoc_code,
1056 pruefidentifikator,
1057 ),
1058 )),
1059 #[cfg(feature = "remadv")]
1060 "REMADV" => Ok(AnyMessage::Remadv(
1061 crate::messages::remadv::RemadvMessage::from_parts(
1062 segments,
1063 message_ref,
1064 assoc_code,
1065 pruefidentifikator,
1066 ),
1067 )),
1068 #[cfg(feature = "orders")]
1069 "ORDERS" => Ok(AnyMessage::Orders(
1070 crate::messages::orders::OrdersMessage::from_parts(
1071 segments,
1072 message_ref,
1073 assoc_code,
1074 pruefidentifikator,
1075 ),
1076 )),
1077 #[cfg(feature = "iftsta")]
1078 "IFTSTA" => Ok(AnyMessage::Iftsta(
1079 crate::messages::iftsta::IftstaMessage::from_parts(
1080 segments,
1081 message_ref,
1082 assoc_code,
1083 pruefidentifikator,
1084 ),
1085 )),
1086 #[cfg(feature = "insrpt")]
1087 "INSRPT" => Ok(AnyMessage::Insrpt(
1088 crate::messages::insrpt::InsrptMessage::from_parts(
1089 segments,
1090 message_ref,
1091 assoc_code,
1092 pruefidentifikator,
1093 ),
1094 )),
1095 #[cfg(feature = "reqote")]
1096 "REQOTE" => Ok(AnyMessage::Reqote(
1097 crate::messages::reqote::ReqoteMessage::from_parts(
1098 segments,
1099 message_ref,
1100 assoc_code,
1101 pruefidentifikator,
1102 ),
1103 )),
1104 #[cfg(feature = "partin")]
1105 "PARTIN" => Ok(AnyMessage::Partin(
1106 crate::messages::partin::PartinMessage::from_parts(
1107 segments,
1108 message_ref,
1109 assoc_code,
1110 pruefidentifikator,
1111 ),
1112 )),
1113 #[cfg(feature = "ordchg")]
1114 "ORDCHG" => Ok(AnyMessage::Ordchg(
1115 crate::messages::ordchg::OrdchgMessage::from_parts(
1116 segments,
1117 message_ref,
1118 assoc_code,
1119 pruefidentifikator,
1120 ),
1121 )),
1122 #[cfg(feature = "ordrsp")]
1123 "ORDRSP" => Ok(AnyMessage::Ordrsp(
1124 crate::messages::ordrsp::OrdrespMessage::from_parts(
1125 segments,
1126 message_ref,
1127 assoc_code,
1128 pruefidentifikator,
1129 ),
1130 )),
1131 #[cfg(feature = "quotes")]
1132 "QUOTES" => Ok(AnyMessage::Quotes(
1133 crate::messages::quotes::QuotesMessage::from_parts(
1134 segments,
1135 message_ref,
1136 assoc_code,
1137 pruefidentifikator,
1138 ),
1139 )),
1140 #[cfg(feature = "comdis")]
1141 "COMDIS" => Ok(AnyMessage::Comdis(
1142 crate::messages::comdis::ComdisMessage::from_parts(
1143 segments,
1144 message_ref,
1145 assoc_code,
1146 pruefidentifikator,
1147 ),
1148 )),
1149 #[cfg(feature = "pricat")]
1150 "PRICAT" => Ok(AnyMessage::Pricat(
1151 crate::messages::pricat::PricatMessage::from_parts(
1152 segments,
1153 message_ref,
1154 assoc_code,
1155 pruefidentifikator,
1156 ),
1157 )),
1158 #[cfg(feature = "utilts")]
1159 "UTILTS" => Ok(AnyMessage::Utilts(
1160 crate::messages::utilts::UtiltsMessage::from_parts(
1161 segments,
1162 message_ref,
1163 assoc_code,
1164 pruefidentifikator,
1165 ),
1166 )),
1167 other => {
1168 if let Some(mt) = MessageType::from_unh_code(other) {
1172 if !mt.is_feature_enabled() {
1173 return Err(Error::FeatureNotEnabled {
1174 message_type: other.to_owned(),
1175 feature: mt.as_str().to_lowercase(),
1176 });
1177 }
1178 }
1179 Ok(AnyMessage::Unknown {
1181 message_type_code: other.into(),
1182 release: crate::Release::new(&assoc_code),
1183 message_ref: message_ref.into(),
1184 segments,
1185 })
1186 }
1187 }
1188}
1189
1190impl MessageType {
1193 #[must_use]
1195 pub fn is_feature_enabled(self) -> bool {
1196 match self {
1197 MessageType::Utilmd => cfg!(feature = "utilmd"),
1198 MessageType::Mscons => cfg!(feature = "mscons"),
1199 MessageType::Aperak => cfg!(feature = "aperak"),
1200 MessageType::Contrl => cfg!(feature = "contrl"),
1201 MessageType::Invoic => cfg!(feature = "invoic"),
1202 MessageType::Remadv => cfg!(feature = "remadv"),
1203 MessageType::Orders => cfg!(feature = "orders"),
1204 MessageType::Iftsta => cfg!(feature = "iftsta"),
1205 MessageType::Insrpt => cfg!(feature = "insrpt"),
1206 MessageType::Reqote => cfg!(feature = "reqote"),
1207 MessageType::Partin => cfg!(feature = "partin"),
1208 MessageType::Ordchg => cfg!(feature = "ordchg"),
1209 MessageType::Ordrsp => cfg!(feature = "ordrsp"),
1210 MessageType::Quotes => cfg!(feature = "quotes"),
1211 MessageType::Comdis => cfg!(feature = "comdis"),
1212 MessageType::Pricat => cfg!(feature = "pricat"),
1213 MessageType::Utilts => cfg!(feature = "utilts"),
1214 }
1215 }
1216}