Skip to main content

automapper_validation/
pipeline.rs

1//! Full raw-EDIFACT validation pipeline, shared by the v2 API route
2//! (`automapper-api`'s `run_validation`) and the `edifact-mapper` library entry
3//! point (`Mapper::validate_edifact`), so both produce byte-identical findings.
4
5use mig_assembly::assembler::{Assembler, AssemblerConfig};
6use mig_assembly::navigator::AssembledTreeNavigator;
7use mig_types::schema::mig::MigSchema;
8use mig_types::segment::OwnedSegment;
9
10use crate::eval::{ConditionEvaluator, ExternalConditionProvider};
11use crate::{
12    build_validated_tree, validate_unt_segment_count, AhbWorkflow, EdifactValidator, IssueKind,
13    Severity, ValidationIssue, ValidationLevel, ValidationReport,
14};
15
16/// Validate one message's assembled segments against its AHB workflow.
17///
18/// Callers resolve `filtered_mig`, `workflow`, and `evaluator` from wherever they
19/// hold the data (the API from its `MigServiceRegistry`, `edifact-mapper` from the
20/// data bundle) and pass the already-collected `all_segments` (message body plus
21/// envelope for MIGs that cover it). Assembly config, validated-tree construction,
22/// UNT-count and structure diagnostics all live here so the two entry points can
23/// never drift.
24pub fn validate_edifact_message<E: ConditionEvaluator>(
25    all_segments: &[OwnedSegment],
26    filtered_mig: &MigSchema,
27    workflow: &AhbWorkflow,
28    evaluator: E,
29    external: &dyn ExternalConditionProvider,
30    level: ValidationLevel,
31) -> ValidationReport {
32    // `skip_unknown_segments` lets the assembler advance past AHB-foreign segments
33    // (surfaced once as diagnostics below) instead of stalling; `strict_code_matching`
34    // disambiguates merged sibling slots. Mirrors the v2 route's config exactly.
35    let assembler = Assembler::with_config(
36        filtered_mig,
37        AssemblerConfig {
38            strict_code_matching: true,
39            skip_unknown_segments: true,
40            ..Default::default()
41        },
42    );
43    let (tree, structure_diagnostics) = assembler.assemble_with_diagnostics(all_segments);
44
45    let validated_tree = build_validated_tree(workflow, &tree);
46    let navigator = AssembledTreeNavigator::new(&tree);
47    let validator = EdifactValidator::new(evaluator);
48    let mut report = validator.validate_tree(
49        &validated_tree,
50        all_segments,
51        external,
52        level,
53        Some(&navigator),
54    );
55
56    if let Some(issue) = validate_unt_segment_count(all_segments) {
57        report.add_issue(issue);
58    }
59
60    for diag in structure_diagnostics {
61        let kind = IssueKind::StructureDiagnostic {
62            kind: diag.kind,
63            segment_id: diag.segment_id.clone(),
64            position: diag.position,
65            detail: diag.message,
66        };
67        report.add_issue(ValidationIssue::new(Severity::Warning, kind));
68    }
69
70    report
71}