Skip to main content

edifact_mapper/
evaluator_factory.rs

1//! Factory for creating condition evaluators by variant name and format version.
2
3use automapper_validation::{
4    ConditionEvaluator, ConditionExprEvaluator, ConditionResult, EvaluationContext,
5};
6use mig_bo4e::pid_requirements::PidRequirements;
7use mig_bo4e::pid_validation::PidValidationError;
8use serde_json::Value;
9
10/// Run condition-aware validation using a boxed evaluator.
11///
12/// With `scoped` (a navigator over the reverse-mapped tree and the instance
13/// each entity element becomes), every element is evaluated in its own group
14/// instance, so "in dieser SG8" means its SG8.
15///
16/// Internally dispatches to a concrete generic call to satisfy `Sized` bounds.
17pub fn validate_with_boxed_evaluator(
18    evaluator: &dyn ConditionEvaluator,
19    json: &Value,
20    requirements: &PidRequirements,
21    pid: &str,
22    segments: &[mig_types::segment::OwnedSegment],
23    scoped: Option<(
24        &dyn mig_types::navigator::GroupNavigator,
25        &std::collections::HashMap<
26            crate::element_scopes::ElementKey,
27            crate::element_scopes::InstancePath,
28        >,
29        &std::collections::HashMap<
30            crate::element_scopes::NestedKey,
31            crate::element_scopes::InstancePath,
32        >,
33    )>,
34) -> Vec<PidValidationError> {
35    // We need a Sized type to pass to ConditionExprEvaluator.
36    // Use a newtype wrapper around &dyn ConditionEvaluator.
37    struct EvalRef<'a>(&'a dyn ConditionEvaluator);
38
39    impl ConditionEvaluator for EvalRef<'_> {
40        fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
41            self.0.evaluate(condition, ctx)
42        }
43
44        fn is_external(&self, condition: u32) -> bool {
45            self.0.is_external(condition)
46        }
47
48        fn is_known(&self, condition: u32) -> bool {
49            self.0.is_known(condition)
50        }
51
52        fn message_type(&self) -> &str {
53            self.0.message_type()
54        }
55
56        fn format_version(&self) -> &str {
57            self.0.format_version()
58        }
59    }
60
61    let wrapper = EvalRef(evaluator);
62    let external = automapper_validation::MapExternalProvider::new(Default::default());
63    let expr_eval = ConditionExprEvaluator::new(&wrapper);
64    match scoped {
65        Some((navigator, scopes, nested)) => {
66            let ctx = EvaluationContext::with_navigator(pid, &external, segments, navigator);
67            crate::conditional_validation::validate_with_conditions_in_scopes(
68                json,
69                requirements,
70                &expr_eval,
71                &ctx,
72                scopes,
73                nested,
74            )
75        }
76        None => {
77            let ctx = EvaluationContext::new(pid, &external, segments);
78            crate::conditional_validation::validate_with_conditions(
79                json,
80                requirements,
81                &expr_eval,
82                &ctx,
83            )
84        }
85    }
86}
87
88/// Create a condition evaluator for the given variant and format version.
89///
90/// Returns `None` if no evaluator exists for the combination. See
91/// [`automapper_validation::evaluator_for`].
92pub fn create_evaluator(variant: &str, fv: &str) -> Option<Box<dyn ConditionEvaluator>> {
93    automapper_validation::evaluator_for(variant, fv)
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn test_known_evaluators_exist() {
102        assert!(create_evaluator("UTILMD_Strom", "FV2504").is_some());
103        assert!(create_evaluator("MSCONS", "FV2504").is_some());
104        assert!(create_evaluator("ORDERS", "FV2510").is_some());
105        assert!(create_evaluator("UTILMD_Gas", "FV2604").is_some());
106    }
107
108    #[test]
109    fn test_unknown_evaluator_returns_none() {
110        assert!(create_evaluator("NONEXISTENT", "FV2504").is_none());
111        assert!(create_evaluator("UTILMD_Strom", "FV9999").is_none());
112    }
113}