use std::collections::{BTreeMap, BTreeSet};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConditionExpr {
Ref(u32),
And(Vec<ConditionExpr>),
Or(Vec<ConditionExpr>),
Xor(Box<ConditionExpr>, Box<ConditionExpr>),
Not(Box<ConditionExpr>),
Package { id: u32, min: u32, max: u32 },
}
impl ConditionExpr {
pub fn condition_ids(&self) -> BTreeSet<u32> {
let mut ids = BTreeSet::new();
self.collect_ids(&mut ids);
ids
}
fn collect_ids(&self, ids: &mut BTreeSet<u32>) {
match self {
ConditionExpr::Ref(id) => {
ids.insert(*id);
}
ConditionExpr::And(exprs) | ConditionExpr::Or(exprs) => {
for expr in exprs {
expr.collect_ids(ids);
}
}
ConditionExpr::Xor(left, right) => {
left.collect_ids(ids);
right.collect_ids(ids);
}
ConditionExpr::Not(inner) => {
inner.collect_ids(ids);
}
ConditionExpr::Package { .. } => {
}
}
}
}
impl std::fmt::Display for ConditionExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConditionExpr::Ref(id) => write!(f, "[{id}]"),
ConditionExpr::And(exprs) => {
let parts: Vec<String> = exprs.iter().map(|e| format!("{e}")).collect();
write!(f, "({})", parts.join(" ∧ "))
}
ConditionExpr::Or(exprs) => {
let parts: Vec<String> = exprs.iter().map(|e| format!("{e}")).collect();
write!(f, "({})", parts.join(" ∨ "))
}
ConditionExpr::Xor(left, right) => write!(f, "({left} ⊻ {right})"),
ConditionExpr::Not(inner) => write!(f, "NOT {inner}"),
ConditionExpr::Package { id, min, max } => write!(f, "[{id}P{min}..{max}]"),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AhbCodeRule {
pub value: String,
pub description: String,
pub ahb_status: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AhbFieldRule {
pub segment_path: String,
pub name: String,
pub ahb_status: String,
pub codes: Vec<AhbCodeRule>,
pub parent_group_ahb_status: Option<String>,
pub segment_ahb_status: Option<String>,
pub element_index: Option<usize>,
pub component_index: Option<usize>,
pub mig_number: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AhbWorkflow {
pub pruefidentifikator: String,
pub description: String,
pub communication_direction: Option<String>,
pub fields: Vec<AhbFieldRule>,
pub ub_definitions: BTreeMap<String, ConditionExpr>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ref_condition_ids() {
let expr = ConditionExpr::Ref(931);
assert_eq!(expr.condition_ids(), [931].into());
}
#[test]
fn test_nested_condition_ids() {
let expr = ConditionExpr::Xor(
Box::new(ConditionExpr::Or(vec![
ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
ConditionExpr::And(vec![ConditionExpr::Ref(3), ConditionExpr::Ref(4)]),
])),
Box::new(ConditionExpr::Ref(5)),
);
assert_eq!(expr.condition_ids(), [1, 2, 3, 4, 5].into());
}
#[test]
fn test_display_complex() {
let expr = ConditionExpr::Xor(
Box::new(ConditionExpr::And(vec![
ConditionExpr::Ref(102),
ConditionExpr::Ref(2006),
])),
Box::new(ConditionExpr::And(vec![
ConditionExpr::Ref(103),
ConditionExpr::Ref(2005),
])),
);
assert_eq!(format!("{expr}"), "(([102] ∧ [2006]) ⊻ ([103] ∧ [2005]))");
}
#[test]
fn test_package_condition_ids() {
let expr = ConditionExpr::Package {
id: 4,
min: 0,
max: 1,
};
assert!(expr.condition_ids().is_empty());
}
#[test]
fn ahb_workflow_json_roundtrip() {
let wf = AhbWorkflow {
pruefidentifikator: "55001".into(),
description: "Anmeldung MaLo".into(),
communication_direction: Some("NB an LF".into()),
fields: vec![AhbFieldRule {
segment_path: "SG2/NAD/3035".into(),
name: "Partnerrolle".into(),
ahb_status: "Muss [182] ∧ [152]".into(),
codes: vec![AhbCodeRule {
value: "MS".into(),
description: "Messstellenbetreiber".into(),
ahb_status: "X".into(),
}],
mig_number: Some("0042".into()),
..Default::default()
}],
ub_definitions: BTreeMap::from([(
"UB1".to_string(),
ConditionExpr::Xor(
Box::new(ConditionExpr::Ref(931)),
Box::new(ConditionExpr::Ref(932)),
),
)]),
};
let json = serde_json::to_string(&wf).unwrap();
let back: AhbWorkflow = serde_json::from_str(&json).unwrap();
assert_eq!(back.pruefidentifikator, "55001");
assert_eq!(back.fields.len(), 1);
assert_eq!(back.fields[0].codes[0].value, "MS");
assert_eq!(back.fields[0].ahb_status, "Muss [182] ∧ [152]");
assert_eq!(
back.ub_definitions["UB1"].condition_ids(),
[931, 932].into()
);
}
}