use edifact_rs::Segment;
use crate::{Pruefidentifikator, registry::PidSource};
fn plausible(raw: &str) -> Option<u32> {
raw.parse::<u32>()
.ok()
.filter(|v| (Pruefidentifikator::MIN..=Pruefidentifikator::MAX).contains(v))
}
pub(crate) trait PidSegment {
fn tag_str(&self) -> &str;
fn element(&self, index: usize) -> Option<&str>;
fn component(&self, element: usize, component: usize) -> Option<&str>;
}
impl PidSegment for Segment<'_> {
fn tag_str(&self) -> &str {
&self.tag
}
fn element(&self, index: usize) -> Option<&str> {
self.element_str(index)
}
fn component(&self, element: usize, component: usize) -> Option<&str> {
self.component_str(element, component)
}
}
fn from_bgm<S: PidSegment>(segments: &[S]) -> Option<u32> {
segments
.iter()
.find(|s| s.tag_str() == "BGM")
.and_then(|bgm| bgm.element(1))
.and_then(plausible)
}
fn from_rff_z13<S: PidSegment>(segments: &[S]) -> Option<u32> {
segments
.iter()
.filter(|s| s.tag_str() == "RFF" && s.element(0) == Some("Z13"))
.find_map(|rff| rff.component(0, 1).and_then(plausible))
}
pub(crate) fn detect<S: PidSegment>(segments: &[S], source: PidSource) -> Option<u32> {
match source {
PidSource::RffZ13 => from_rff_z13(segments).or_else(|| from_bgm(segments)),
PidSource::BgmDe1004 => from_bgm(segments).or_else(|| from_rff_z13(segments)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use edifact_rs::{Element, OwnedSegment};
fn seg(tag: &str, elements: &[&[&str]]) -> OwnedSegment {
Segment::new(
tag,
elements.iter().map(|comps| Element::of(comps)).collect(),
)
.into_owned()
}
#[test]
fn a_numeric_document_number_does_not_beat_the_real_pid() {
let segments = vec![
seg("BGM", &[&["E01"], &["7"]]),
seg("RFF", &[&["Z13", "13002"]]),
];
assert_eq!(detect(&segments, PidSource::RffZ13), Some(13002));
assert_eq!(detect(&segments, PidSource::BgmDe1004), Some(13002));
}
#[test]
fn either_location_is_found_from_either_declared_source() {
let in_bgm = vec![seg("BGM", &[&["E01"], &["55001"]])];
let in_rff = vec![seg("RFF", &[&["Z13", "55001"]])];
for source in [PidSource::BgmDe1004, PidSource::RffZ13] {
assert_eq!(detect(&in_bgm, source), Some(55001));
assert_eq!(detect(&in_rff, source), Some(55001));
}
}
#[test]
fn the_declared_source_wins_a_genuine_conflict() {
let segments = vec![
seg("BGM", &[&["E01"], &["11001"]]),
seg("RFF", &[&["Z13", "13002"]]),
];
assert_eq!(detect(&segments, PidSource::BgmDe1004), Some(11001));
assert_eq!(detect(&segments, PidSource::RffZ13), Some(13002));
}
#[test]
fn other_rff_qualifiers_are_ignored() {
let segments = vec![seg("RFF", &[&["ACW", "55001"]])];
assert_eq!(detect(&segments, PidSource::RffZ13), None);
}
#[test]
fn out_of_range_and_non_numeric_values_are_rejected() {
for raw in ["9999", "100000", "", "ABCDE", "-55001"] {
let segments = vec![seg("BGM", &[&["E01"], &[raw]])];
assert_eq!(detect(&segments, PidSource::BgmDe1004), None, "{raw:?}");
}
}
}