use std::sync::Arc;
use crate::{AnyMessage, Error, ParseConfig, generated, registry::ReleaseRegistry};
#[cfg(any(
feature = "utilmd",
feature = "mscons",
feature = "aperak",
feature = "contrl",
feature = "invoic",
feature = "remadv",
feature = "orders",
feature = "iftsta",
feature = "insrpt",
feature = "reqote",
feature = "partin",
feature = "ordchg",
feature = "ordrsp",
feature = "quotes",
feature = "comdis",
feature = "pricat",
feature = "utilts",
))]
use crate::{EdiEnergyReport, Release};
#[derive(Clone)]
pub struct Platform {
registry: Arc<ReleaseRegistry>,
}
impl std::fmt::Debug for Platform {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Platform")
.field("profiles", &self.registry.all_profiles().len())
.finish()
}
}
impl Platform {
#[must_use]
pub fn new(registry: ReleaseRegistry) -> Self {
Self {
registry: Arc::new(registry),
}
}
#[must_use]
pub fn with_all_profiles() -> Self {
let mut profiles: Vec<&'static dyn crate::registry::Profile> = Vec::new();
generated::register_profiles(&mut profiles);
Self::new(ReleaseRegistry::new(profiles))
}
#[must_use]
pub fn with_transition_grace_days(self, days: i64) -> Self {
let registry = Arc::try_unwrap(self.registry)
.unwrap_or_else(|arc| (*arc).clone())
.with_transition_grace_days(days);
Self {
registry: Arc::new(registry),
}
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(
skip(self, input),
fields(
bytes = input.len(),
// message_type and release are recorded after parsing via Span::current().record(...)
message_type = tracing::field::Empty,
release = tracing::field::Empty,
pruefidentifikator = tracing::field::Empty,
)
)
)]
pub fn parse(&self, input: &[u8]) -> Result<AnyMessage, Error> {
let msg = crate::parse::parse_with_registry(input, ParseConfig::default(), &self.registry)?;
#[cfg(feature = "tracing")]
{
let span = tracing::Span::current();
if let Some(mt) = msg.try_message_type() {
span.record("message_type", mt.as_str());
}
if let Ok(release) = crate::EdiEnergyMessage::detect_release(&msg) {
span.record("release", release.as_str());
}
if let Ok(pid) = crate::EdiEnergyMessage::detect_pruefidentifikator(&msg) {
span.record("pruefidentifikator", pid.as_u32());
}
}
Ok(msg)
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(
skip(self, input),
fields(
bytes = input.len(),
message_type = tracing::field::Empty,
release = tracing::field::Empty,
pruefidentifikator = tracing::field::Empty,
)
)
)]
pub fn parse_with_config(
&self,
input: &[u8],
config: ParseConfig,
) -> Result<AnyMessage, Error> {
let msg = crate::parse::parse_with_registry(input, config, &self.registry)?;
#[cfg(feature = "tracing")]
{
let span = tracing::Span::current();
if let Some(mt) = msg.try_message_type() {
span.record("message_type", mt.as_str());
}
if let Ok(release) = crate::EdiEnergyMessage::detect_release(&msg) {
span.record("release", release.as_str());
}
if let Ok(pid) = crate::EdiEnergyMessage::detect_pruefidentifikator(&msg) {
span.record("pruefidentifikator", pid.as_u32());
}
}
Ok(msg)
}
pub fn parse_interchange(
&self,
reader: impl std::io::Read,
) -> impl Iterator<Item = Result<AnyMessage, Error>> {
self.parse_interchange_with_config(reader, ParseConfig::default())
}
pub fn parse_interchange_with_config(
&self,
reader: impl std::io::Read,
config: ParseConfig,
) -> impl Iterator<Item = Result<AnyMessage, Error>> {
crate::parse::parse_interchange_with_arc_registry(
reader,
config,
Arc::clone(&self.registry),
)
}
pub fn parse_interchange_full(
&self,
data: &[u8],
) -> Result<crate::interchange::ParsedInterchange, Error> {
crate::parse::parse_interchange_full_with_arc_registry(
data,
ParseConfig::default(),
Arc::clone(&self.registry),
)
}
#[cfg(any(
feature = "utilmd",
feature = "mscons",
feature = "aperak",
feature = "contrl",
feature = "invoic",
feature = "remadv",
feature = "orders",
feature = "iftsta",
feature = "insrpt",
feature = "reqote",
feature = "partin",
feature = "ordchg",
feature = "ordrsp",
feature = "quotes",
feature = "comdis",
feature = "pricat",
feature = "utilts",
))]
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip(self, message), fields(message_type = ?message.try_message_type()))
)]
pub fn validate(&self, message: &AnyMessage) -> Result<EdiEnergyReport, Error> {
let core = message
.message_core()
.ok_or_else(|| Error::UnknownMessageType {
raw_code: crate::error::sanitize_code(
message.try_message_type().map_or("Unknown", |t| t.as_str()),
),
})?;
let release = core.detect_release()?;
core.validate_against_with_semantic_and_registry(release, None, &self.registry)
}
#[cfg(any(
feature = "utilmd",
feature = "mscons",
feature = "aperak",
feature = "contrl",
feature = "invoic",
feature = "remadv",
feature = "orders",
feature = "iftsta",
feature = "insrpt",
feature = "reqote",
feature = "partin",
feature = "ordchg",
feature = "ordrsp",
feature = "quotes",
feature = "comdis",
feature = "pricat",
feature = "utilts",
))]
#[cfg_attr(
feature = "tracing",
tracing::instrument(
skip(self, message),
fields(message_type = ?message.try_message_type(), release = %release.as_str())
)
)]
pub fn validate_against(
&self,
message: &AnyMessage,
release: &Release,
) -> Result<EdiEnergyReport, Error> {
let core = message
.message_core()
.ok_or_else(|| Error::UnknownMessageType {
raw_code: crate::error::sanitize_code(
message.try_message_type().map_or("Unknown", |t| t.as_str()),
),
})?;
core.validate_against_with_semantic_and_registry(release, None, &self.registry)
}
#[must_use]
pub fn registry(&self) -> &ReleaseRegistry {
&self.registry
}
#[must_use]
pub fn registry_arc(&self) -> Arc<ReleaseRegistry> {
Arc::clone(&self.registry)
}
#[must_use]
pub fn process_context(&self, date: time::Date) -> crate::registry::ProcessContext {
crate::registry::ProcessContext::for_date_with_registry(date, Arc::clone(&self.registry))
}
#[must_use]
pub fn current_context(&self) -> crate::registry::ProcessContext {
let today = time::OffsetDateTime::now_utc().date();
self.process_context(today)
}
#[must_use]
pub fn is_wire_code_acceptable_on(
&self,
envelope: &crate::interchange::MessageEnvelope,
date: time::Date,
) -> bool {
envelope.is_wire_code_acceptable_on(date, &self.registry)
}
pub fn warm_up(&self) {
for profile in self.registry.all_profiles() {
let _ = profile.mig_rule_pack();
let _ = profile.ahb_rule_pack(None);
}
}
}