use std::io::{BufReader, Read};
use edifact_rs::{MessageWindowsIter, OwnedSegment, ReaderConfig, from_bufread_stream_with_config};
use crate::{AnyMessage, Error, MessageType};
fn sanitize_release_code(s: &str) -> std::borrow::Cow<'_, str> {
const MAX_LEN: usize = 16;
if s.len() <= MAX_LEN && s.chars().all(|c| c.is_ascii_alphanumeric() || c == '.') {
std::borrow::Cow::Borrowed(s)
} else {
std::borrow::Cow::Owned(format!("<invalid-code:{} bytes>", s.len()))
}
}
fn resolve_pid_source(
msg_type_code: &str,
assoc_code: &str,
registry: &crate::registry::ReleaseRegistry,
) -> crate::registry::PidSource {
MessageType::from_unh_code(msg_type_code)
.and_then(|mt| {
let rel = crate::release::Release::new(assoc_code);
registry.profile(mt, &rel).ok()
})
.map(super::registry::Profile::pid_source)
.unwrap_or_default()
}
pub(crate) fn resolve_pid_source_pub(
msg_type_code: &str,
assoc_code: &str,
registry: &crate::registry::ReleaseRegistry,
) -> crate::registry::PidSource {
resolve_pid_source(msg_type_code, assoc_code, registry)
}
pub const DEFAULT_MAX_SEGMENT_BYTES: usize = 64 * 1024;
#[derive(Debug, Clone, Copy)]
pub struct ParseConfig {
pub max_segment_bytes: usize,
pub max_segments: Option<usize>,
pub max_input_bytes: Option<usize>,
pub max_messages_per_interchange: Option<usize>,
pub max_messages: Option<usize>,
pub max_segments_per_message: Option<usize>,
pub reference_date: Option<time::Date>,
}
impl Default for ParseConfig {
fn default() -> Self {
Self {
max_segment_bytes: DEFAULT_MAX_SEGMENT_BYTES,
max_segments: Some(10_000),
max_input_bytes: Some(10 * 1024 * 1024),
max_messages_per_interchange: Some(1_000),
max_segments_per_message: Some(500),
max_messages: None,
reference_date: None,
}
}
}
impl ParseConfig {
#[must_use]
pub fn with_reference_date(mut self, date: time::Date) -> Self {
self.reference_date = Some(date);
self
}
pub(crate) fn to_reader_config(self) -> ReaderConfig {
let mut cfg = ReaderConfig::default().max_segment_bytes(self.max_segment_bytes);
if let Some(n) = self.max_segments {
cfg = cfg.max_segments(n);
}
if let Some(n) = self.max_input_bytes {
cfg = cfg.max_input_bytes(n as u64);
}
if let Some(n) = self.max_messages {
cfg = cfg.max_messages(n);
}
cfg
}
}
pub fn parse_envelope_only(input: &[u8]) -> Result<crate::light_message::LightMessage, Error> {
let cfg = ParseConfig::default().to_reader_config();
let segments: Vec<OwnedSegment> = edifact_rs::from_bytes_owned_with_config(input, cfg)
.collect::<Result<_, _>>()
.map_err(Error::Parse)?;
crate::light_message::LightMessage::from_segments(
segments,
crate::registry::ReleaseRegistry::global(),
)
}
pub fn parse(input: &[u8]) -> Result<AnyMessage, Error> {
parse_with_registry(
input,
ParseConfig::default(),
crate::registry::ReleaseRegistry::global(),
)
}
pub(crate) fn parse_with_registry(
input: &[u8],
config: ParseConfig,
registry: &crate::registry::ReleaseRegistry,
) -> Result<AnyMessage, Error> {
let per_msg_limit = config.max_segments_per_message;
let cfg = config.to_reader_config();
let segments: Vec<OwnedSegment> = edifact_rs::from_bytes_owned_with_config(input, cfg)
.collect::<Result<_, _>>()
.map_err(Error::Parse)?;
if let Some(lim) = per_msg_limit {
let actual = segments.len();
if actual > lim {
return Err(Error::TooManySegmentsInMessage { limit: lim, actual });
}
}
dispatch_message(segments, registry)
}
pub fn parse_interchange(reader: impl Read) -> impl Iterator<Item = Result<AnyMessage, Error>> {
parse_interchange_with_registry(reader, ParseConfig::default())
}
pub(crate) fn parse_interchange_with_registry(
reader: impl Read,
config: ParseConfig,
) -> impl Iterator<Item = Result<AnyMessage, Error>> {
parse_interchange_impl(
reader,
config,
std::sync::Arc::clone(crate::registry::ReleaseRegistry::global_arc()),
)
}
pub(crate) fn parse_interchange_impl(
reader: impl Read,
config: ParseConfig,
registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
) -> impl Iterator<Item = Result<AnyMessage, Error>> {
let limit = config.max_messages_per_interchange;
let per_msg_limit = config.max_segments_per_message;
let cfg = config.to_reader_config();
MessageWindowsIter::new(from_bufread_stream_with_config(BufReader::new(reader), cfg))
.enumerate()
.map(move |(index, window)| {
if let Some(lim) = limit {
if index >= lim {
return Err(Error::TooManyMessages { limit: lim });
}
}
let window = window.map_err(Error::Parse)?;
if let Some(lim) = per_msg_limit {
let actual = window.segments.len();
if actual > lim {
return Err(Error::TooManySegmentsInMessage { limit: lim, actual });
}
}
dispatch_message(window.segments, ®istry)
})
}
pub(crate) fn parse_interchange_with_arc_registry(
reader: impl Read,
config: ParseConfig,
registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
) -> impl Iterator<Item = Result<AnyMessage, Error>> {
parse_interchange_impl(reader, config, registry)
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip(reader, config, registry))
)]
pub(crate) fn parse_interchange_buffered_impl(
reader: impl Read,
config: ParseConfig,
registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
) -> Result<(crate::interchange::InterchangeHeader, InterchangeIter), Error> {
let cfg = config.to_reader_config();
let segments: Vec<OwnedSegment> = from_bufread_stream_with_config(BufReader::new(reader), cfg)
.collect::<Result<_, _>>()
.map_err(Error::Parse)?;
let header = parse_interchange_header_from_segments(&segments)?;
let unz_ref = segments
.iter()
.rfind(|s| s.tag == "UNZ")
.and_then(|unz| unz.element_str(1))
.map(str::to_owned);
let declared_count = segments
.iter()
.rfind(|s| s.tag == "UNZ")
.and_then(|unz| unz.element_str(0))
.and_then(|s| s.parse::<usize>().ok())
.unwrap_or(0);
let msg_iter = MessageWindowsIter::new(segments.into_iter().map(
Ok::<_, edifact_rs::EdifactError>
as fn(OwnedSegment) -> Result<OwnedSegment, edifact_rs::EdifactError>,
));
let iter = InterchangeIter {
inner: msg_iter,
header: header.clone(),
registry,
limit: config.max_messages_per_interchange,
index: 0,
actual_count: 0,
declared_count,
unz_ref,
unz_checked: false,
done: false,
};
Ok((header, iter))
}
#[derive(Debug, Clone)]
pub struct Parser {
config: ParseConfig,
}
impl Default for Parser {
fn default() -> Self {
Self::new()
}
}
impl Parser {
#[must_use]
pub fn new() -> Self {
Self {
config: ParseConfig::default(),
}
}
#[must_use]
pub fn with_config(config: ParseConfig) -> Self {
Self { config }
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip(self, input), fields(bytes = input.len()))
)]
pub fn parse(&self, input: &[u8]) -> Result<AnyMessage, Error> {
parse_with_registry(
input,
self.config,
crate::registry::ReleaseRegistry::global(),
)
}
pub fn parse_reader(&self, reader: impl Read) -> Result<AnyMessage, Error> {
let cfg = self.config.to_reader_config();
let segments: Vec<OwnedSegment> =
from_bufread_stream_with_config(BufReader::new(reader), cfg)
.collect::<Result<_, _>>()
.map_err(Error::Parse)?;
dispatch_message(segments, crate::registry::ReleaseRegistry::global())
}
pub fn parse_envelope_only(
&self,
input: &[u8],
) -> Result<crate::light_message::LightMessage, Error> {
let cfg = self.config.to_reader_config();
let segments: Vec<OwnedSegment> = edifact_rs::from_bytes_owned_with_config(input, cfg)
.collect::<Result<_, _>>()
.map_err(Error::Parse)?;
crate::light_message::LightMessage::from_segments(
segments,
crate::registry::ReleaseRegistry::global(),
)
}
pub fn parse_interchange(
&self,
reader: impl Read,
) -> impl Iterator<Item = Result<AnyMessage, Error>> {
parse_interchange_with_registry(reader, self.config)
}
pub fn parse_interchange_buffered(
&self,
reader: impl Read,
) -> Result<(crate::interchange::InterchangeHeader, InterchangeIter), Error> {
parse_interchange_buffered_impl(
reader,
self.config,
std::sync::Arc::clone(crate::registry::ReleaseRegistry::global_arc()),
)
}
pub fn parse_interchange_full(
&self,
reader: impl Read,
) -> Result<crate::interchange::ParsedInterchange, Error> {
let reader_cfg = self.config.to_reader_config();
let segments: Vec<OwnedSegment> =
from_bufread_stream_with_config(BufReader::new(reader), reader_cfg)
.collect::<Result<_, _>>()
.map_err(Error::Parse)?;
parse_interchange_full_from_segments(segments, &self.config)
}
}
pub struct InterchangeIter {
#[expect(clippy::type_complexity)]
inner: MessageWindowsIter<
std::iter::Map<
std::vec::IntoIter<OwnedSegment>,
fn(OwnedSegment) -> Result<OwnedSegment, edifact_rs::EdifactError>,
>,
>,
header: crate::interchange::InterchangeHeader,
registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
limit: Option<usize>,
index: usize,
actual_count: usize,
declared_count: usize,
unz_ref: Option<String>,
unz_checked: bool,
done: bool,
}
impl Iterator for InterchangeIter {
type Item = Result<crate::interchange::MessageEnvelope, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
if let Some(window_result) = self.inner.next() {
let index = self.index;
self.index += 1;
self.actual_count += 1;
if let Some(lim) = self.limit {
if index >= lim {
self.done = true;
return Some(Err(Error::TooManyMessages { limit: lim }));
}
}
let result = (|| {
let window = window_result.map_err(Error::Parse)?;
let message = dispatch_message(window.segments, &self.registry)?;
Ok(crate::interchange::MessageEnvelope {
message,
header: self.header.clone(),
message_index: index,
})
})();
Some(result)
} else {
if self.unz_checked {
self.done = true;
return None;
}
self.unz_checked = true;
if let Some(ref uref) = self.unz_ref {
if !uref.is_empty() && uref.as_str() != self.header.control_ref.as_ref() {
self.done = true;
return Some(Err(Error::InterchangeRefMismatch {
unb_ref: self.header.control_ref.to_string(),
unz_ref: uref.clone(),
}));
}
}
if self.declared_count != 0 && self.declared_count != self.actual_count {
self.done = true;
return Some(Err(Error::InterchangeCountMismatch {
declared: self.declared_count,
actual: self.actual_count,
}));
}
self.done = true;
None
}
}
}
fn parse_interchange_header_from_segments(
segments: &[OwnedSegment],
) -> Result<crate::interchange::InterchangeHeader, Error> {
use crate::interchange::InterchangeHeader;
let unb = segments
.iter()
.find(|s| s.tag == "UNB")
.ok_or(Error::MissingSegment("UNB"))?;
let syntax_id = unb.component_str(0, 0).unwrap_or("UNOC").to_owned();
let syntax_version: u8 = unb
.component_str(0, 1)
.and_then(|s| s.parse().ok())
.unwrap_or(3);
let sender_id = unb.component_str(1, 0).unwrap_or("").to_owned();
let sender_qualifier = unb.component_str(1, 2).unwrap_or("").to_owned();
let receiver_id = unb.component_str(2, 0).unwrap_or("").to_owned();
let receiver_qualifier = unb.component_str(2, 2).unwrap_or("").to_owned();
let transmission_datetime = parse_unb_datetime(
unb.component_str(3, 0).unwrap_or(""),
unb.component_str(3, 1).unwrap_or(""),
);
let control_ref = unb.element_str(4).unwrap_or("").to_owned();
let test_indicator = unb.element_str(10).is_some_and(|v| v.trim() == "1");
Ok(InterchangeHeader {
sender_id: sender_id.into_boxed_str(),
sender_qualifier: sender_qualifier.into_boxed_str(),
receiver_id: receiver_id.into_boxed_str(),
receiver_qualifier: receiver_qualifier.into_boxed_str(),
transmission_datetime,
control_ref: control_ref.into_boxed_str(),
syntax_id: syntax_id.into_boxed_str(),
syntax_version,
test_indicator,
})
}
fn parse_interchange_full_from_segments(
segments: Vec<OwnedSegment>,
config: &ParseConfig,
) -> Result<crate::interchange::ParsedInterchange, Error> {
parse_interchange_full_from_segments_with_registry(
segments,
config,
std::sync::Arc::clone(crate::registry::ReleaseRegistry::global_arc()),
)
}
pub(crate) fn parse_interchange_full_with_arc_registry(
data: &[u8],
config: ParseConfig,
registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
) -> Result<crate::interchange::ParsedInterchange, Error> {
let cfg = config.to_reader_config();
let segments: Vec<OwnedSegment> = edifact_rs::from_bytes_owned_with_config(data, cfg)
.collect::<Result<_, _>>()
.map_err(Error::Parse)?;
parse_interchange_full_from_segments_with_registry(segments, &config, registry)
}
#[expect(clippy::needless_pass_by_value)]
fn parse_interchange_full_from_segments_with_registry(
segments: Vec<OwnedSegment>,
config: &ParseConfig,
registry: std::sync::Arc<crate::registry::ReleaseRegistry>,
) -> Result<crate::interchange::ParsedInterchange, Error> {
use crate::interchange::{InterchangeHeader, MessageEnvelope, ParsedInterchange};
let unb = segments
.iter()
.find(|s| s.tag == "UNB")
.ok_or(Error::MissingSegment("UNB"))?;
let syntax_id = unb.component_str(0, 0).unwrap_or("UNOC").to_owned();
let syntax_version: u8 = unb
.component_str(0, 1)
.and_then(|s| s.parse().ok())
.unwrap_or(3);
let sender_id = unb.component_str(1, 0).unwrap_or("").to_owned();
let sender_qualifier = unb.component_str(1, 2).unwrap_or("").to_owned();
let receiver_id = unb.component_str(2, 0).unwrap_or("").to_owned();
let receiver_qualifier = unb.component_str(2, 2).unwrap_or("").to_owned();
let transmission_datetime = parse_unb_datetime(
unb.component_str(3, 0).unwrap_or(""),
unb.component_str(3, 1).unwrap_or(""),
);
let control_ref = unb.element_str(4).unwrap_or("").to_owned();
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!(
"parse_interchange",
sender = %sender_id,
receiver = %receiver_id,
control_ref = %control_ref,
segment_count = segments.len(),
)
.entered();
let test_indicator = unb.element_str(10).is_some_and(|v| v.trim() == "1");
let header = InterchangeHeader {
sender_id: sender_id.into_boxed_str(),
sender_qualifier: sender_qualifier.into_boxed_str(),
receiver_id: receiver_id.into_boxed_str(),
receiver_qualifier: receiver_qualifier.into_boxed_str(),
transmission_datetime,
control_ref: control_ref.into_boxed_str(),
syntax_id: syntax_id.into_boxed_str(),
syntax_version,
test_indicator,
};
let unz_seg = segments.iter().rfind(|s| s.tag == "UNZ");
let (trailer_ref, declared_message_count) = match unz_seg {
Some(unz_seg) => {
let count: usize = unz_seg
.element_str(0)
.and_then(|s| s.parse().ok())
.unwrap_or(0);
let tref = unz_seg.element_str(1).unwrap_or("").to_owned();
(tref.into_boxed_str(), count)
}
None => ("".into(), 0),
};
let msg_iter = edifact_rs::MessageWindowsIter::new(
segments.into_iter().map(Ok::<_, edifact_rs::EdifactError>),
);
let mut messages: Vec<MessageEnvelope> = Vec::new();
for (index, window_result) in msg_iter.enumerate() {
if let Some(limit) = config.max_messages_per_interchange {
if index >= limit {
return Err(Error::TooManyMessages { limit });
}
}
let window = window_result.map_err(Error::Parse)?;
let message = dispatch_message(window.segments, ®istry)?;
messages.push(MessageEnvelope {
message,
header: header.clone(),
message_index: index,
});
}
if !trailer_ref.is_empty() && trailer_ref.as_ref() != header.control_ref.as_ref() {
return Err(Error::InterchangeRefMismatch {
unb_ref: header.control_ref.to_string(),
unz_ref: trailer_ref.to_string(),
});
}
if declared_message_count != 0 && declared_message_count != messages.len() {
return Err(Error::InterchangeCountMismatch {
declared: declared_message_count,
actual: messages.len(),
});
}
Ok(ParsedInterchange {
header,
messages,
trailer_ref,
declared_message_count,
})
}
fn parse_unb_datetime(date: &str, time: &str) -> Option<time::OffsetDateTime> {
use time::{Date, Month, OffsetDateTime, Time, UtcOffset};
let (year, month_n, day) = match date.len() {
6 => {
let yy: i32 = date[0..2].parse().ok()?;
let mm: u8 = date[2..4].parse().ok()?;
let dd: u8 = date[4..6].parse().ok()?;
(2000 + yy, mm, dd)
}
8 => {
let yyyy: i32 = date[0..4].parse().ok()?;
let mm: u8 = date[4..6].parse().ok()?;
let dd: u8 = date[6..8].parse().ok()?;
(yyyy, mm, dd)
}
_ => return None,
};
let month = Month::try_from(month_n).ok()?;
let d = Date::from_calendar_date(year, month, day).ok()?;
let (hh, mi, ss) = match time.len() {
4 => {
let hh: u8 = time[0..2].parse().ok()?;
let mi: u8 = time[2..4].parse().ok()?;
(hh, mi, 0u8)
}
6 => {
let hh: u8 = time[0..2].parse().ok()?;
let mi: u8 = time[2..4].parse().ok()?;
let ss: u8 = time[4..6].parse().ok()?;
(hh, mi, ss)
}
_ => return None,
};
let t = Time::from_hms(hh, mi, ss).ok()?;
Some(OffsetDateTime::new_utc(d, t).replace_offset(UtcOffset::UTC))
}
pub(crate) fn dispatch_message(
segments: Vec<OwnedSegment>,
registry: &crate::registry::ReleaseRegistry,
) -> Result<AnyMessage, Error> {
let (message_ref, msg_type_code, assoc_code) = {
let unh = segments
.iter()
.find(|s| s.tag == "UNH")
.ok_or(Error::MissingSegment("UNH"))?;
let message_ref = unh.element_str(0).unwrap_or_default().to_owned();
let msg_type_code = unh
.component_str(1, 0)
.ok_or(Error::MalformedSegment("UNH"))?
.to_owned();
let assoc_code = unh.component_str(1, 4).unwrap_or_default().to_owned();
(message_ref, msg_type_code, assoc_code)
};
let pruefidentifikator: Option<u32> =
match resolve_pid_source(&msg_type_code, &assoc_code, registry) {
crate::registry::PidSource::RffZ13 => segments
.iter()
.find(|s| s.tag == "RFF" && (s.element_str(0) == Some("Z13")))
.and_then(|rff| rff.component_str(0, 1))
.and_then(|s| s.parse().ok()),
crate::registry::PidSource::BgmDe1004 => segments
.iter()
.find(|s| s.tag == "BGM")
.and_then(|bgm| bgm.element_str(1))
.and_then(|s| s.parse().ok()),
};
if matches!(
crate::release::Release::new(&assoc_code).kind(),
crate::release::ReleaseKind::Opaque(_)
) {
let safe_code = sanitize_release_code(&assoc_code);
#[cfg(feature = "tracing")]
tracing::warn!(
release = %safe_code,
"unrecognised EDI@Energy release code — validate() will return ProfileNotFound"
);
#[cfg(not(feature = "tracing"))]
eprintln!(
"edi-energy: warning: unrecognised release code `{safe_code}` — \
validate() will return ProfileNotFound"
);
} else {
#[cfg(feature = "tracing")]
tracing::debug!(
message_type = %msg_type_code,
release = %assoc_code,
segment_count = segments.len(),
"parsed EDIFACT message"
);
}
dispatch_by_type(
&msg_type_code,
segments,
message_ref,
assoc_code,
pruefidentifikator,
)
}
#[allow(unused_variables)] #[allow(clippy::too_many_lines)]
fn dispatch_by_type(
msg_type_code: &str,
segments: Vec<OwnedSegment>,
message_ref: String,
assoc_code: String,
pruefidentifikator: Option<u32>,
) -> Result<AnyMessage, Error> {
match msg_type_code {
#[cfg(feature = "utilmd")]
"UTILMD" => Ok(AnyMessage::Utilmd(
crate::messages::utilmd::UtilmdMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "mscons")]
"MSCONS" => Ok(AnyMessage::Mscons(
crate::messages::mscons::MsconsMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "aperak")]
"APERAK" => Ok(AnyMessage::Aperak(
crate::messages::aperak::AperakMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "contrl")]
"CONTRL" => Ok(AnyMessage::Contrl(
crate::messages::contrl::ContrlMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "invoic")]
"INVOIC" => Ok(AnyMessage::Invoic(
crate::messages::invoic::InvoicMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "remadv")]
"REMADV" => Ok(AnyMessage::Remadv(
crate::messages::remadv::RemadvMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "orders")]
"ORDERS" => Ok(AnyMessage::Orders(
crate::messages::orders::OrdersMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "iftsta")]
"IFTSTA" => Ok(AnyMessage::Iftsta(
crate::messages::iftsta::IftstaMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "insrpt")]
"INSRPT" => Ok(AnyMessage::Insrpt(
crate::messages::insrpt::InsrptMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "reqote")]
"REQOTE" => Ok(AnyMessage::Reqote(
crate::messages::reqote::ReqoteMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "partin")]
"PARTIN" => Ok(AnyMessage::Partin(
crate::messages::partin::PartinMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "ordchg")]
"ORDCHG" => Ok(AnyMessage::Ordchg(
crate::messages::ordchg::OrdchgMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "ordrsp")]
"ORDRSP" => Ok(AnyMessage::Ordrsp(
crate::messages::ordrsp::OrdrespMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "quotes")]
"QUOTES" => Ok(AnyMessage::Quotes(
crate::messages::quotes::QuotesMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "comdis")]
"COMDIS" => Ok(AnyMessage::Comdis(
crate::messages::comdis::ComdisMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "pricat")]
"PRICAT" => Ok(AnyMessage::Pricat(
crate::messages::pricat::PricatMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
#[cfg(feature = "utilts")]
"UTILTS" => Ok(AnyMessage::Utilts(
crate::messages::utilts::UtiltsMessage::from_parts(
segments,
message_ref,
assoc_code,
pruefidentifikator,
),
)),
other => {
if let Some(mt) = MessageType::from_unh_code(other) {
if !mt.is_feature_enabled() {
return Err(Error::FeatureNotEnabled {
message_type: other.to_owned(),
feature: mt.as_str().to_lowercase(),
});
}
}
Ok(AnyMessage::Unknown {
message_type_code: other.into(),
release: crate::Release::new(&assoc_code),
message_ref: message_ref.into(),
segments,
})
}
}
}
impl MessageType {
#[must_use]
pub fn is_feature_enabled(self) -> bool {
match self {
MessageType::Utilmd => cfg!(feature = "utilmd"),
MessageType::Mscons => cfg!(feature = "mscons"),
MessageType::Aperak => cfg!(feature = "aperak"),
MessageType::Contrl => cfg!(feature = "contrl"),
MessageType::Invoic => cfg!(feature = "invoic"),
MessageType::Remadv => cfg!(feature = "remadv"),
MessageType::Orders => cfg!(feature = "orders"),
MessageType::Iftsta => cfg!(feature = "iftsta"),
MessageType::Insrpt => cfg!(feature = "insrpt"),
MessageType::Reqote => cfg!(feature = "reqote"),
MessageType::Partin => cfg!(feature = "partin"),
MessageType::Ordchg => cfg!(feature = "ordchg"),
MessageType::Ordrsp => cfg!(feature = "ordrsp"),
MessageType::Quotes => cfg!(feature = "quotes"),
MessageType::Comdis => cfg!(feature = "comdis"),
MessageType::Pricat => cfg!(feature = "pricat"),
MessageType::Utilts => cfg!(feature = "utilts"),
}
}
}