use crate::core::xml_scan::{element_inner, element_text};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BusinessHeader {
pub from: Option<String>,
pub to: Option<String>,
pub biz_msg_idr: Option<String>,
pub msg_def_idr: Option<String>,
pub cre_dt: Option<String>,
}
impl BusinessHeader {
pub fn to_app_hdr_xml(&self) -> String {
self.to_app_hdr_xml_ns("urn:iso:std:iso:20022:tech:xsd:head.001.001.02")
}
pub fn to_app_hdr_xml_ns(&self, namespace: &str) -> String {
fn party(tag: &str, bic: &Option<String>) -> String {
match bic {
Some(b) => format!(
"<{tag}><FIId><FinInstnId><BICFI>{}</BICFI></FinInstnId></FIId></{tag}>",
xml_escape(b)
),
None => String::new(),
}
}
fn elem(tag: &str, v: &Option<String>) -> String {
match v {
Some(x) => format!("<{tag}>{}</{tag}>", xml_escape(x)),
None => String::new(),
}
}
format!(
"<AppHdr xmlns=\"{ns}\">{fr}{to}{bmi}{mdi}{cd}</AppHdr>",
ns = namespace,
fr = party("Fr", &self.from),
to = party("To", &self.to),
bmi = elem("BizMsgIdr", &self.biz_msg_idr),
mdi = elem("MsgDefIdr", &self.msg_def_idr),
cd = elem("CreDt", &self.cre_dt),
)
}
}
fn xml_escape(s: &str) -> String {
s.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
}
pub fn parse_business_header(xml: &str) -> Option<BusinessHeader> {
let scope = element_inner(xml, "AppHdr").unwrap_or_else(|| xml.to_string());
let from = element_inner(&scope, "Fr").and_then(|s| first_bic(&s));
let to = element_inner(&scope, "To").and_then(|s| first_bic(&s));
let biz_msg_idr = element_text(&scope, "BizMsgIdr");
let msg_def_idr = element_text(&scope, "MsgDefIdr");
let cre_dt = element_text(&scope, "CreDt");
if from.is_none()
&& to.is_none()
&& biz_msg_idr.is_none()
&& msg_def_idr.is_none()
&& cre_dt.is_none()
{
return None;
}
Some(BusinessHeader {
from,
to,
biz_msg_idr,
msg_def_idr,
cre_dt,
})
}
fn first_bic(fragment: &str) -> Option<String> {
for tag in ["BICFI", "AnyBIC", "BIC", "Id"] {
if let Some(v) = element_text(fragment, tag) {
if !v.is_empty() {
return Some(v);
}
}
}
None
}