use std::marker::PhantomData;
use edifact_rs::Writer;
use crate::AgencyCode;
use crate::{Error, Pruefidentifikator, Release};
use super::{Set, Unset, bytes_to_segments, dtm_today, format_dtm137};
macro_rules! emit_seg {
($writer:expr, $tag:expr, $($elem:expr),+ $(,)?) => {{
let elements: &[&str] = &[$($elem),+];
$writer.write_raw($tag, elements).map_err(|e| Error::Parse(e.into()))?;
}};
}
#[derive(Debug, Clone)]
struct AperakBuilderInner {
release: Release,
sender_id: Option<String>,
receiver_id: Option<String>,
sender_agency: AgencyCode,
receiver_agency: AgencyCode,
message_ref: String,
document_code: String,
document_id: Option<String>,
acw_ref: Option<String>,
error_code: Option<String>,
error_text: Option<String>,
document_date: Option<String>,
}
#[derive(Debug, Clone)]
#[must_use = "Builder must be consumed via .build() or .serialize()"]
pub struct AperakBuilder<S = Unset, R = Unset> {
_ph: PhantomData<fn() -> (S, R)>,
inner: AperakBuilderInner,
}
impl AperakBuilder<Unset, Unset> {
pub fn new(release: Release) -> Self {
Self {
_ph: PhantomData,
inner: AperakBuilderInner {
release,
sender_id: None,
receiver_id: None,
sender_agency: AgencyCode::Bdew,
receiver_agency: AgencyCode::Bdew,
message_ref: "1".to_owned(),
document_code: "1000".to_owned(),
document_id: None,
acw_ref: None,
error_code: None,
error_text: None,
document_date: None,
},
}
}
}
impl<S, R> AperakBuilder<S, R> {
fn transition<S2, R2>(self) -> AperakBuilder<S2, R2> {
AperakBuilder {
_ph: PhantomData,
inner: self.inner,
}
}
pub fn sender(mut self, id: impl Into<String>) -> AperakBuilder<Set, R> {
self.inner.sender_id = Some(id.into());
self.transition()
}
pub fn receiver(mut self, id: impl Into<String>) -> AperakBuilder<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.document_id = Some(pid.as_u32().to_string());
self
}
pub fn acw_ref(mut self, reference: impl Into<String>) -> Self {
self.inner.acw_ref = Some(reference.into());
self
}
pub fn error_code(mut self, code: impl Into<String>) -> Self {
self.inner.error_code = Some(code.into());
self
}
pub fn error_text(mut self, text: impl Into<String>) -> Self {
self.inner.error_text = Some(text.into());
self
}
pub fn message_ref(mut self, reference: impl Into<String>) -> Self {
self.inner.message_ref = reference.into();
self
}
pub fn document_date(mut self, date: impl Into<String>) -> Self {
self.inner.document_date = Some(date.into());
self
}
fn to_bytes(&self) -> Result<Vec<u8>, Error> {
let unh_type = format!("APERAK:D:07B:UN:{}", self.inner.release.as_str());
let dtm_val = self
.inner
.document_date
.as_deref()
.map_or_else(dtm_today, format_dtm137);
let mut buf = Vec::new();
let mut w = Writer::new(&mut buf);
let doc_id = self.inner.document_id.as_deref().unwrap_or("");
emit_seg!(w, "UNH", &self.inner.message_ref, &unh_type);
emit_seg!(w, "BGM", &self.inner.document_code, doc_id, "9");
emit_seg!(w, "DTM", &dtm_val);
if let Some(id) = &self.inner.sender_id {
emit_seg!(
w,
"NAD",
"MS",
&self.inner.sender_agency.format_nad_c082(id)
);
}
if let Some(id) = &self.inner.receiver_id {
emit_seg!(
w,
"NAD",
"MR",
&self.inner.receiver_agency.format_nad_c082(id)
);
}
if let Some(r) = &self.inner.acw_ref {
emit_seg!(w, "RFF", &format!("ACW:{r}"));
}
if let Some(code) = &self.inner.error_code {
emit_seg!(w, "ERC", code);
}
if let Some(text) = &self.inner.error_text {
emit_seg!(w, "FTX", "AAI", "", "", text);
}
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 AperakBuilder<Set, Set> {
pub fn build(self) -> Result<crate::messages::aperak::AperakMessage, Error> {
let pid = self
.inner
.document_id
.as_deref()
.and_then(|s| s.parse::<u32>().ok());
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::aperak::AperakMessage::from_parts(
segments,
message_ref.as_str(),
assoc_code.as_str(),
pid,
))
}
}