automapper-validation 0.14.0

AHB condition expression parsing, evaluation, and EDIFACT validation
Documentation
// <auto-generated>
// Generated by automapper-generator generate-conditions
// AHB: xml-migs-and-ahbs/FV2504/CONTRL_AHB_2_4a_Fehlerkorrektur_20250331.xml
// Generated: 2026-03-12T08:44:40Z
// </auto-generated>

// LLM-produced bodies aren't idiomatic Rust — silence stylistic lints at
// file scope so `cargo clippy -D warnings` stays green. Correctness,
// suspicious, and perf categories stay on so real bugs still surface.
#![allow(clippy::style, clippy::complexity)]

#[allow(unused_imports)]
use crate::eval::format_validators::*;
use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};

/// Generated condition evaluator for CONTRL FV2504.
pub struct ContrlConditionEvaluatorFV2504 {
    // External condition IDs that require runtime context.
    external_conditions: std::collections::HashSet<u32>,
}

impl Default for ContrlConditionEvaluatorFV2504 {
    fn default() -> Self {
        let mut external_conditions = std::collections::HashSet::new();
        external_conditions.insert(1);
        Self {
            external_conditions,
        }
    }
}

impl ConditionEvaluator for ContrlConditionEvaluatorFV2504 {
    fn message_type(&self) -> &str {
        "CONTRL"
    }

    fn format_version(&self) -> &str {
        "FV2504"
    }

    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
        match condition {
            1 => self.evaluate_1(ctx),
            2 => self.evaluate_2(ctx),
            3 => self.evaluate_3(ctx),
            5 => self.evaluate_5(ctx),
            6 => self.evaluate_6(ctx),
            8 => self.evaluate_8(ctx),
            9 => self.evaluate_9(ctx),
            _ => ConditionResult::Unknown,
        }
    }

    fn is_external(&self, condition: u32) -> bool {
        self.external_conditions.contains(&condition)
    }
    fn is_known(&self, condition: u32) -> bool {
        matches!(condition, 1 | 2 | 3 | 5 | 6 | 8 | 9)
    }
}

impl ContrlConditionEvaluatorFV2504 {
    /// [1] Wenn Angabe möglich.
    /// EXTERNAL: Requires context from outside the message.
    // REVIEW: "Wenn Angabe möglich" (when specification is possible) is a business-context condition — whether a particular piece of data can be provided depends on external factors (e.g., whether the information is known or available to the sender), not on the content of the EDIFACT message itself. (medium confidence)
    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
        ctx.external.evaluate("angabe_moeglich")
    }

    /// [2] Wenn Syntaxfehler in UNH vorhanden.
    fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
        let ucm_segments = ctx.find_segments("UCM");
        ConditionResult::from(ucm_segments.iter().any(|s| {
            let has_unh = s
                .elements
                .get(4)
                .and_then(|e| e.first())
                .is_some_and(|v| v == "UNH");
            let has_error = s
                .elements
                .get(3)
                .and_then(|e| e.first())
                .is_some_and(|v| !v.is_empty());
            has_unh && has_error
        }))
    }

    /// [3] Wenn Syntaxfehler in UNT vorhanden.
    fn evaluate_3(&self, ctx: &EvaluationContext) -> ConditionResult {
        let ucm_segments = ctx.find_segments("UCM");
        ConditionResult::from(ucm_segments.iter().any(|s| {
            let has_unt = s
                .elements
                .get(4)
                .and_then(|e| e.first())
                .is_some_and(|v| v == "UNT");
            let has_error = s
                .elements
                .get(3)
                .and_then(|e| e.first())
                .is_some_and(|v| !v.is_empty());
            has_unt && has_error
        }))
    }

    /// [5] Wenn Fehler auf Segment(gruppen)ebene vorhanden.
    // REVIEW: Segment-group level errors in UCM are identified by DE0085 (syntax error code, elements[3]) being present without S011/DE0098 (element position, elements[5][0]) — meaning the error is at segment level, not pinpointed to a specific data element. (medium confidence)
    fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
        let ucm_segments = ctx.find_segments("UCM");
        ConditionResult::from(ucm_segments.iter().any(|s| {
            let has_error = s
                .elements
                .get(3)
                .and_then(|e| e.first())
                .is_some_and(|v| !v.is_empty());
            // Segment-level error: DE0085 present but S011 (DE0098) absent — no element-level detail
            let has_element_detail = s
                .elements
                .get(5)
                .and_then(|e| e.first())
                .is_some_and(|v| !v.is_empty());
            has_error && !has_element_detail
        }))
    }

    /// [6] Wenn Fehler auf Datenelement-, Gruppendatenelement- oder Datengruppenebene vorhanden.
    // REVIEW: Data element / group data element level errors are indicated by S011 being populated. DE0098 (elements[5][0]) holds the segment position of the faulty element. If present, the error is at element level. (medium confidence)
    fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
        let ucm_segments = ctx.find_segments("UCM");
        ConditionResult::from(ucm_segments.iter().any(|s| {
            // Element-level error: S011 DE0098 (elements[5][0]) is present
            s.elements
                .get(5)
                .and_then(|e| e.first())
                .is_some_and(|v| !v.is_empty())
        }))
    }

    /// [8] Wenn SG1 UCM DE0013 vorhanden.
    fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
        let ucm_segments = ctx.find_segments("UCM");
        ConditionResult::from(ucm_segments.iter().any(|s| {
            s.elements
                .get(4)
                .and_then(|e| e.first())
                .is_some_and(|v| !v.is_empty())
        }))
    }

    /// [9] Wenn SG1 UCM DE0013 nicht vorhanden.
    fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
        let ucm_segments = ctx.find_segments("UCM");
        if ucm_segments.is_empty() {
            return ConditionResult::Unknown;
        }
        ConditionResult::from(ucm_segments.iter().all(|s| {
            !s.elements
                .get(4)
                .and_then(|e| e.first())
                .is_some_and(|v| !v.is_empty())
        }))
    }
}