use std::marker::PhantomData;
use edifact_rs::Writer;
use crate::AgencyCode;
use crate::{Error, ObjectType, Pruefidentifikator, Release};
use super::{Set, Unset, bytes_to_segments, today_ccyymmdd};
#[derive(Debug, Clone, Default)]
struct UtilmdTransactionSpec {
ide_qualifier: String,
ide_id: String,
status_code: Option<String>,
process_dates: Vec<(String, String)>,
location: Option<(String, String)>,
references: Vec<(String, String)>,
free_texts: Vec<(String, String)>,
agr: Option<(String, String)>,
customer_nad: Option<(String, String)>,
}
#[derive(Debug, Clone)]
struct UtilmdBuilderInner {
release: Release,
pruefidentifikator: Option<Pruefidentifikator>,
sender_id: Option<String>,
receiver_id: Option<String>,
sender_agency: AgencyCode,
receiver_agency: AgencyCode,
message_ref: String,
document_code: String,
document_date: Option<String>,
rff_entries: Vec<(String, String)>,
transactions: Vec<UtilmdTransactionSpec>,
}
#[derive(Debug, Clone)]
#[must_use = "Builder must be consumed via .build() or .serialize()"]
pub struct UtilmdBuilder<S = Unset, R = Unset> {
_ph: PhantomData<fn() -> (S, R)>,
inner: UtilmdBuilderInner,
}
impl UtilmdBuilder<Unset, Unset> {
pub fn new(release: Release) -> Self {
Self {
_ph: PhantomData,
inner: UtilmdBuilderInner {
release,
pruefidentifikator: None,
sender_id: None,
receiver_id: None,
sender_agency: AgencyCode::Bdew,
receiver_agency: AgencyCode::Bdew,
message_ref: "1".to_owned(),
document_code: "E01".to_owned(),
document_date: None,
rff_entries: Vec::new(),
transactions: Vec::new(),
},
}
}
}
impl<S, R> UtilmdBuilder<S, R> {
fn transition<S2, R2>(self) -> UtilmdBuilder<S2, R2> {
UtilmdBuilder {
_ph: PhantomData,
inner: self.inner,
}
}
pub fn sender(mut self, id: impl Into<String>) -> UtilmdBuilder<Set, R> {
self.inner.sender_id = Some(id.into());
self.transition()
}
pub fn receiver(mut self, id: impl Into<String>) -> UtilmdBuilder<S, Set> {
self.inner.receiver_id = Some(id.into());
self.transition()
}
pub fn sender_agency(mut self, agency: crate::AgencyCode) -> Self {
self.inner.sender_agency = agency;
self
}
pub fn receiver_agency(mut self, agency: crate::AgencyCode) -> Self {
self.inner.receiver_agency = agency;
self
}
pub fn pruefidentifikator(mut self, pid: Pruefidentifikator) -> Self {
self.inner.pruefidentifikator = Some(pid);
self
}
pub fn message_ref(mut self, reference: impl Into<String>) -> Self {
self.inner.message_ref = reference.into();
self
}
pub fn document_code(mut self, code: impl Into<String>) -> Self {
self.inner.document_code = code.into();
self
}
pub fn document_date(mut self, date: impl Into<String>) -> Self {
self.inner.document_date = Some(date.into());
self
}
pub fn rff(mut self, qualifier: impl Into<String>, reference: impl Into<String>) -> Self {
self.inner
.rff_entries
.push((qualifier.into(), reference.into()));
self
}
pub fn transaction(
self,
object_type: ObjectType,
ide_id: impl Into<String>,
) -> UtilmdTransactionBuilder<S, R> {
self.transaction_with_qualifier(object_type.qualifier_code(), ide_id)
}
pub fn transaction_with_qualifier(
self,
ide_qualifier: impl Into<String>,
ide_id: impl Into<String>,
) -> UtilmdTransactionBuilder<S, R> {
UtilmdTransactionBuilder {
parent: self,
spec: UtilmdTransactionSpec {
ide_qualifier: ide_qualifier.into(),
ide_id: ide_id.into(),
..Default::default()
},
}
}
}
impl<S, R> UtilmdBuilder<S, R> {
fn to_bytes(&self) -> Result<Vec<u8>, Error> {
let pid_str = self
.inner
.pruefidentifikator
.map(|p| format!("{:05}", p.as_u32()))
.unwrap_or_default();
let dtm_val = self
.inner
.document_date
.as_deref()
.map_or_else(today_ccyymmdd, str::to_owned);
let mut buf = Vec::new();
let mut w = Writer::new(&mut buf);
emit_comp!(
w,
"UNH",
[&self.inner.message_ref],
["UTILMD", "D", "11A", "UN", self.inner.release.as_str()]
);
emit_seg!(w, "BGM", &self.inner.document_code, &pid_str, "9");
emit_comp!(w, "DTM", ["137", &dtm_val, "102"]);
for (qualifier, reference) in &self.inner.rff_entries {
emit_comp!(w, "RFF", [qualifier, reference]);
}
if let Some(id) = &self.inner.sender_id {
emit_comp!(
w,
"NAD",
["MS"],
[id, "", self.inner.sender_agency.as_str()]
);
}
if let Some(id) = &self.inner.receiver_id {
emit_comp!(
w,
"NAD",
["MR"],
[id, "", self.inner.receiver_agency.as_str()]
);
}
for tx in &self.inner.transactions {
emit_seg!(w, "IDE", &tx.ide_qualifier, &tx.ide_id);
if let Some(status) = &tx.status_code {
emit_seg!(w, "STS", status);
}
for (qualifier, date_val) in &tx.process_dates {
emit_comp!(w, "DTM", [qualifier, date_val, "102"]);
}
if let Some((loc_q, loc_id)) = &tx.location {
emit_comp!(w, "LOC", [loc_q], [loc_id, "", "293"]);
}
for (rff_q, rff_ref) in &tx.references {
emit_comp!(w, "RFF", [rff_q, rff_ref]);
}
for (ftx_q, ftx_text) in &tx.free_texts {
emit_comp!(w, "FTX", [ftx_q], [""], [""], [ftx_text]);
}
if let Some((svc_req, resp_type)) = &tx.agr {
emit_comp!(w, "AGR", [svc_req, resp_type]);
}
if let Some((nad_q, nad_id)) = &tx.customer_nad {
emit_comp!(w, "NAD", [nad_q], [nad_id, "", "293"]);
}
}
w.finish_unt(&self.inner.message_ref)
.map_err(Error::Parse)?;
Ok(buf)
}
pub fn serialize(self) -> Result<Vec<u8>, Error> {
self.to_bytes()
}
}
impl UtilmdBuilder<Set, Set> {
pub fn build(self) -> Result<crate::messages::utilmd::UtilmdMessage, Error> {
let pid = self
.inner
.pruefidentifikator
.map(super::super::pruefidentifikator::Pruefidentifikator::as_u32);
let message_ref = self.inner.message_ref.clone();
let assoc_code = self.inner.release.as_str().to_owned();
let segments = bytes_to_segments(&self.to_bytes()?)?;
Ok(crate::messages::utilmd::UtilmdMessage::from_parts(
segments,
message_ref.as_str(),
assoc_code.as_str(),
pid,
))
}
}
#[derive(Debug)]
#[must_use = "Sub-builder must be finalized with .done()"]
pub struct UtilmdTransactionBuilder<S = Unset, R = Unset> {
parent: UtilmdBuilder<S, R>,
spec: UtilmdTransactionSpec,
}
impl<S, R> UtilmdTransactionBuilder<S, R> {
pub fn status(mut self, code: impl Into<String>) -> Self {
self.spec.status_code = Some(code.into());
self
}
pub fn process_date(mut self, qualifier: impl Into<String>, date: impl Into<String>) -> Self {
self.spec
.process_dates
.push((qualifier.into(), date.into()));
self
}
pub fn location(mut self, qualifier: impl Into<String>, id: impl Into<String>) -> Self {
self.spec.location = Some((qualifier.into(), id.into()));
self
}
pub fn reference(mut self, qualifier: impl Into<String>, ref_id: impl Into<String>) -> Self {
self.spec.references.push((qualifier.into(), ref_id.into()));
self
}
pub fn customer(mut self, party_qualifier: impl Into<String>, id: impl Into<String>) -> Self {
self.spec.customer_nad = Some((party_qualifier.into(), id.into()));
self
}
pub fn free_text(mut self, text_function: impl Into<String>, text: impl Into<String>) -> Self {
self.spec
.free_texts
.push((text_function.into(), text.into()));
self
}
pub fn agr(
mut self,
service_requirement: impl Into<String>,
response_type: impl Into<String>,
) -> Self {
self.spec.agr = Some((service_requirement.into(), response_type.into()));
self
}
pub fn done(mut self) -> UtilmdBuilder<S, R> {
self.parent.inner.transactions.push(self.spec);
self.parent
}
}