use std::collections::BTreeMap;
use automapper_validation::eval::{
ConditionEvaluator, ConditionResult, EvaluationContext, GroupScope, MapExternalProvider,
};
use automapper_validation::{
UtilmdStromConditionEvaluatorFV2504, UtilmdStromConditionEvaluatorFV2604,
};
use mig_assembly::assembler::{
AssembledGroup, AssembledGroupInstance, AssembledSegment, AssembledTree,
};
use mig_assembly::navigator::AssembledTreeNavigator;
use mig_types::segment::OwnedSegment;
use ConditionResult::{False, True};
fn seg(tag: &str, elements: &[&[&str]]) -> AssembledSegment {
AssembledSegment {
tag: tag.to_string(),
elements: elements
.iter()
.map(|e| e.iter().map(|c| c.to_string()).collect())
.collect(),
mig_number: None,
segment_number: None,
}
}
fn instance(
segments: Vec<AssembledSegment>,
child_groups: Vec<AssembledGroup>,
) -> AssembledGroupInstance {
AssembledGroupInstance {
segments,
child_groups,
entry_mig_number: None,
variant_mig_numbers: vec![],
skipped_segments: vec![],
skipped_positions: Vec::new(),
}
}
fn group(id: &str, repetitions: Vec<AssembledGroupInstance>) -> AssembledGroup {
AssembledGroup {
group_id: id.to_string(),
repetitions,
}
}
fn sg8(
seq: &str,
zeitraum: &str,
mut segments: Vec<AssembledSegment>,
sg10s: Vec<Vec<AssembledSegment>>,
) -> AssembledGroupInstance {
segments.insert(0, seg("SEQ", &[&[seq], &[zeitraum]]));
let children = if sg10s.is_empty() {
vec![]
} else {
vec![group(
"SG10",
sg10s.into_iter().map(|s| instance(s, vec![])).collect(),
)]
};
instance(segments, children)
}
fn rff(qualifier: &str, value: &str) -> AssembledSegment {
seg("RFF", &[&[qualifier, value]])
}
fn cci(klassentyp: &str, code: &str) -> AssembledSegment {
seg("CCI", &[&[klassentyp], &[], &[code]])
}
fn cav(code: &str) -> AssembledSegment {
seg("CAV", &[&[code]])
}
const Z01_MALO1: usize = 0;
const PROFIL_MALO1: usize = 4;
const PROFIL_MALO2: usize = 5;
const Z58_T1: usize = 6;
const Z58_T1_SAME: usize = 7;
const Z58_T2: usize = 8;
const Z20_DEV1: usize = 9;
const Z20_DEV2: usize = 11;
const Z98_PAUSCHAL: usize = 12;
const Z98_T2: usize = 14;
fn tree() -> AssembledTree {
let loc = |q: &str, id: &str, zeitraum: &str| seg("LOC", &[&[q], &[id, "", "", zeitraum]]);
let sg5s = vec![
instance(vec![loc("Z16", "MALO1", "")], vec![]),
instance(vec![loc("Z16", "MALO2", "")], vec![]),
instance(vec![loc("Z16", "MALO3", "")], vec![]),
instance(vec![loc("Z22", "RUHE1", "T1")], vec![]),
];
let sg6s = vec![
instance(vec![rff("Z50", "MALO1")], vec![]),
instance(vec![rff("Z50", "MALO2")], vec![]),
instance(vec![rff("Z50", "MALO3")], vec![]),
instance(
vec![
seg("RFF", &[&["Z49", "", "T1"]]),
seg("DTM", &[&["Z25", "202301010000?+00", "303"]]),
],
vec![],
),
instance(
vec![
seg("RFF", &[&["Z47", "", "T2"]]),
seg("DTM", &[&["Z25", "202201010000?+00", "303"]]),
],
vec![],
),
];
let pia = |obis: &str| seg("PIA", &[&["5"], &[obis, "SRW"]]);
let sg8s = vec![
sg8(
"Z01",
"T1",
vec![rff("Z18", "MALO1")],
vec![
vec![cci("", "ZA6"), cav("E14")],
vec![cci("Z30", "Z06")],
vec![cci("", "Z83"), cav("Z53")],
vec![cci("", "ZA6"), cav("Z36")],
],
),
sg8(
"Z01",
"T2",
vec![rff("Z18", "MALO2")],
vec![vec![cci("", "ZA6"), cav("E02")], vec![cci("6", "ZA9")]],
),
sg8(
"Z02",
"T1",
vec![rff("Z18", "MALO1"), pia("1-1:1.8.0")],
vec![],
),
sg8(
"Z02",
"T2",
vec![rff("Z18", "MALO2"), pia("1-1:1.29.0")],
vec![],
),
sg8("Z21", "T1", vec![rff("Z18", "MALO1")], vec![]),
sg8("Z21", "T2", vec![rff("Z18", "MALO2")], vec![]),
sg8(
"Z58",
"T1",
vec![seg("RFF", &[&["Z33", "OBJ1", "1"]])],
vec![],
),
sg8(
"Z58",
"T1",
vec![seg("RFF", &[&["Z33", "OBJ1", "1"]])],
vec![],
),
sg8(
"Z58",
"T2",
vec![seg("RFF", &[&["Z33", "OBJ2", "1"]])],
vec![],
),
sg8("Z20", "T1", vec![rff("MG", "DEV1")], vec![]),
sg8("Z20", "T1", vec![rff("MG", "DEV1")], vec![]),
sg8("Z20", "T1", vec![rff("MG", "DEV2")], vec![]),
sg8("Z98", "T1", vec![rff("Z18", "MALO1")], vec![]),
sg8("Z78", "T1", vec![rff("Z31", "9992 00000 001 8")], vec![]),
sg8("Z98", "T2", vec![rff("Z18", "MALO2")], vec![]),
];
AssembledTree {
segments: vec![],
groups: vec![group(
"SG4",
vec![instance(
vec![seg("IDE", &[&["24"], &["TX1"]])],
vec![group("SG5", sg5s), group("SG6", sg6s), group("SG8", sg8s)],
)],
)],
post_group_start: 0,
inter_group_segments: BTreeMap::new(),
}
}
fn flat(tree: &AssembledTree) -> Vec<OwnedSegment> {
fn walk(groups: &[AssembledGroup], out: &mut Vec<OwnedSegment>) {
for group in groups {
for instance in &group.repetitions {
for s in &instance.segments {
out.push(OwnedSegment {
id: s.tag.clone(),
elements: s.elements.clone(),
segment_number: out.len() as u32,
});
}
walk(&instance.child_groups, out);
}
}
}
let mut out = Vec::new();
walk(&tree.groups, &mut out);
out
}
fn evaluate(
evaluator: &dyn ConditionEvaluator,
group: &str,
index: usize,
condition: u32,
) -> ConditionResult {
let tree = tree();
let navigator = AssembledTreeNavigator::new(&tree);
let external = MapExternalProvider::new(Default::default());
let segments = flat(&tree);
let ctx = EvaluationContext::with_navigator("55043", &external, &segments, &navigator);
let path = [("SG4", 0), (group, index)];
evaluator.evaluate(condition, &ctx.with_scope(GroupScope { path: &path }))
}
const FV2504: &[(u32, &str, usize, ConditionResult)] = &[
(117, "SG8", PROFIL_MALO1, True),
(117, "SG8", PROFIL_MALO2, False),
(197, "SG8", PROFIL_MALO1, False),
(197, "SG8", PROFIL_MALO2, True),
(223, "SG8", PROFIL_MALO1, True),
(223, "SG8", PROFIL_MALO2, False),
(86, "SG6", 0, True),
(86, "SG6", 2, False),
(408, "SG8", PROFIL_MALO1, True),
(408, "SG8", PROFIL_MALO2, False),
(409, "SG8", PROFIL_MALO1, False),
(409, "SG8", PROFIL_MALO2, True),
(410, "SG8", PROFIL_MALO1, False),
(410, "SG8", PROFIL_MALO2, True),
(411, "SG8", PROFIL_MALO1, True),
(411, "SG8", PROFIL_MALO2, False),
(175, "SG8", Z58_T1, True),
(175, "SG8", Z58_T1_SAME, True),
(175, "SG8", Z58_T2, False),
(196, "SG8", Z58_T1, True),
(196, "SG8", Z58_T2, False),
(229, "SG8", Z58_T1, True),
(229, "SG8", Z58_T2, False),
(266, "SG8", Z58_T1, True),
(266, "SG8", Z58_T2, False),
(332, "SG8", Z58_T1, True),
(332, "SG8", Z58_T2, False),
(306, "SG8", Z58_T1, True),
(306, "SG8", Z58_T2, False),
(307, "SG8", Z58_T1, False),
(307, "SG8", Z58_T2, True),
(327, "SG8", Z58_T1, True),
(327, "SG8", Z58_T2, False),
(124, "SG8", Z20_DEV1, True),
(124, "SG8", Z20_DEV2, False),
(51, "SG8", Z98_PAUSCHAL, False),
(51, "SG8", Z98_T2, True),
];
const FV2604: &[(u32, &str, usize, ConditionResult)] = &[
(160, "SG8", Z58_T1, True),
(160, "SG8", Z58_T2, False),
(271, "SG6", 0, True),
(271, "SG6", 1, False),
];
#[test]
fn each_instance_is_compared_with_the_others_of_its_sg4() {
let _ = Z01_MALO1;
let fv2504 = UtilmdStromConditionEvaluatorFV2504::default();
let fv2604 = UtilmdStromConditionEvaluatorFV2604::default();
let mut wrong = Vec::new();
for (evaluator, cases) in [
(&fv2504 as &dyn ConditionEvaluator, FV2504),
(&fv2604 as &dyn ConditionEvaluator, FV2604),
] {
for &(condition, group, index, expected) in cases {
let got = evaluate(evaluator, group, index, condition);
if got != expected {
wrong.push(format!(
"[{condition}] from {group} {index}: {got:?}, expected {expected:?}"
));
}
}
}
assert!(wrong.is_empty(), "{wrong:#?}");
}
#[test]
fn condition_404_reads_the_msb_of_this_sg8s_marktlokation() {
const RECIPIENT: &str = "9900000000001";
let msb = |mp_id: &str| {
vec![
seg("CCI", &[&[""], &[], &["ZB3"]]),
seg("CAV", &[&["Z91", mp_id]]),
]
};
let loc = |id: &str| seg("LOC", &[&["Z16"], &[id]]);
let tree = AssembledTree {
segments: vec![seg("NAD", &[&["MR"], &[RECIPIENT, "", "293"]])],
groups: vec![group(
"SG4",
vec![instance(
vec![seg("IDE", &[&["24"], &["TX1"]])],
vec![
group(
"SG5",
vec![
instance(vec![loc("MALO1")], vec![]),
instance(vec![loc("MALO2")], vec![]),
],
),
group(
"SG8",
vec![
sg8(
"Z01",
"",
vec![rff("Z18", "MALO1")],
vec![msb("9900000000002")],
),
sg8("Z01", "", vec![rff("Z18", "MALO2")], vec![msb(RECIPIENT)]),
sg8("Z18", "", vec![rff("Z19", "MELO1")], vec![msb(RECIPIENT)]),
sg8("Z02", "", vec![rff("Z18", "MALO1")], vec![]),
sg8("Z02", "", vec![rff("Z18", "MALO2")], vec![]),
],
),
],
)],
)],
post_group_start: 0,
inter_group_segments: BTreeMap::new(),
};
let navigator = AssembledTreeNavigator::new(&tree);
let external = MapExternalProvider::new(Default::default());
let mut segments = vec![OwnedSegment {
id: "NAD".into(),
elements: vec![
vec!["MR".into()],
vec![RECIPIENT.into(), "".into(), "293".into()],
],
segment_number: 0,
}];
segments.extend(flat(&tree));
let ctx = EvaluationContext::with_navigator("55043", &external, &segments, &navigator);
let evaluator = UtilmdStromConditionEvaluatorFV2504::default();
let at = |i: usize| {
let path = [("SG4", 0), ("SG8", i)];
evaluator.evaluate(404, &ctx.with_scope(GroupScope { path: &path }))
};
assert_eq!(at(3), True, "MaLo 1's MSB is not the recipient");
assert_eq!(at(4), False, "MaLo 2's MSB is the recipient");
}