use edifact_rs::OwnedSegment;
use crate::{
DvgwMessageType,
message::{MessageCore, find_all_segments, find_segment, impl_dvgw_message},
};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct NomintQuantity {
pub location_code: String,
pub location_qualifier: String,
pub quantity: String,
pub quantity_qualifier: String,
pub unit: Option<String>,
pub gas_day_start: Option<String>,
pub period_end: Option<String>,
}
impl NomintQuantity {
#[must_use]
pub fn quantity_f64(&self) -> Option<f64> {
if self.quantity.is_empty() {
None
} else {
self.quantity.parse().ok()
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct NomintMessage {
pub(crate) core: MessageCore,
pub nomination_ref: Option<String>,
pub balance_group_code: Option<String>,
pub reference_date: Option<String>,
pub nomination_deadline: Option<String>,
pub quantities: Vec<NomintQuantity>,
}
impl_dvgw_message!(NomintMessage);
impl NomintMessage {
pub(crate) fn from_segments(segments: Vec<OwnedSegment>) -> Self {
let core = MessageCore::from_segments(segments, DvgwMessageType::Nomint);
let nomination_ref = find_segment(&core.segments, "BGM")
.and_then(|s| s.component_str(1, 0))
.map(str::to_owned);
let balance_group_code = extract_balance_group(&core.segments);
let reference_date = extract_dtm(&core.segments, "137");
let nomination_deadline = extract_dtm(&core.segments, "461");
let quantities = extract_nomint_quantities(&core.segments);
Self {
core,
nomination_ref,
balance_group_code,
reference_date,
nomination_deadline,
quantities,
}
}
}
fn extract_dtm(segs: &[OwnedSegment], qualifier: &str) -> Option<String> {
find_all_segments(segs, "DTM")
.find(|s| s.component_str(0, 0) == Some(qualifier))
.and_then(|s| s.component_str(0, 1))
.map(str::to_owned)
}
fn extract_balance_group(segs: &[OwnedSegment]) -> Option<String> {
for qualifier in &["Z01", "Z08", "ZSO"] {
if let Some(val) = segs
.iter()
.find(|s| s.tag == "NAD" && s.element_str(0) == Some(qualifier))
.and_then(|s| s.component_str(1, 0))
{
return Some(val.to_owned());
}
}
None
}
fn extract_nomint_quantities(segs: &[OwnedSegment]) -> Vec<NomintQuantity> {
let mut result = Vec::new();
let mut i = 0;
while i < segs.len() {
let seg = &segs[i];
if seg.tag == "LOC" {
let location_qualifier = seg.element_str(0).unwrap_or("").to_owned();
let location_code = seg.component_str(1, 0).unwrap_or("").to_owned();
let mut quantity = String::new();
let mut quantity_qualifier = String::new();
let mut unit = None;
let mut gas_day_start = None;
let mut period_end = None;
let mut j = i + 1;
while j < segs.len() && segs[j].tag != "LOC" {
match segs[j].tag.as_str() {
"QTY" => {
segs[j]
.component_str(0, 0)
.unwrap_or("")
.clone_into(&mut quantity_qualifier);
segs[j]
.component_str(0, 1)
.unwrap_or("")
.clone_into(&mut quantity);
unit = segs[j].component_str(0, 2).map(str::to_owned);
}
"DTM" => {
let q = segs[j].component_str(0, 0).unwrap_or("");
let v = segs[j].component_str(0, 1).map(str::to_owned);
match q {
"318" | "137" => gas_day_start = v,
"164" => period_end = v,
_ => {}
}
}
_ => {}
}
j += 1;
}
if !location_code.is_empty() && !quantity.is_empty() {
result.push(NomintQuantity {
location_code,
location_qualifier,
quantity,
quantity_qualifier,
unit,
gas_day_start,
period_end,
});
}
i = j;
} else {
i += 1;
}
}
result
}