automapper-validation 0.3.0

AHB condition expression parsing, evaluation, and EDIFACT validation
Documentation
use automapper_validation::eval::{EvaluationContext, GroupScope, MapExternalProvider};
use mig_types::navigator::GroupNavigator;
use mig_types::segment::OwnedSegment;

#[test]
fn context_defaults_scope_to_none() {
    let external = MapExternalProvider::new(Default::default());
    let segments = vec![];
    let ctx = EvaluationContext::new("55218", &external, &segments);
    assert!(ctx.scope.is_none());
}

#[test]
fn with_scope_sets_scope() {
    let external = MapExternalProvider::new(Default::default());
    let segments = vec![];
    let ctx = EvaluationContext::new("55218", &external, &segments);
    let path = ["SG4", "SG8"];
    let scope = GroupScope {
        group_path: &path,
        instance_index: 2,
    };
    let scoped = ctx.with_scope(scope);
    let s = scoped.scope.expect("scope set");
    assert_eq!(s.group_path, &["SG4", "SG8"]);
    assert_eq!(s.instance_index, 2);
}

struct StubNavigator {
    per_instance: Vec<Vec<OwnedSegment>>,
}

impl GroupNavigator for StubNavigator {
    fn find_segments_in_group(&self, tag: &str, _: &[&str], idx: usize) -> Vec<OwnedSegment> {
        self.per_instance
            .get(idx)
            .cloned()
            .unwrap_or_default()
            .into_iter()
            .filter(|s| s.id == tag)
            .collect()
    }
    fn find_segments_with_qualifier_in_group(
        &self,
        tag: &str,
        elem: usize,
        qual: &str,
        _: &[&str],
        idx: usize,
    ) -> Vec<OwnedSegment> {
        self.per_instance
            .get(idx)
            .cloned()
            .unwrap_or_default()
            .into_iter()
            .filter(|s| {
                s.id == tag
                    && s.elements
                        .get(elem)
                        .and_then(|e| e.first())
                        .is_some_and(|v| v == qual)
            })
            .collect()
    }
    fn group_instance_count(&self, _: &[&str]) -> usize {
        self.per_instance.len()
    }
}

fn pia(code: &str) -> OwnedSegment {
    OwnedSegment {
        id: "PIA".to_string(),
        segment_number: 0,
        elements: vec![vec!["Z02".to_string()], vec![code.to_string()]],
    }
}

#[test]
fn scoped_find_segments_returns_segments_in_instance_only() {
    let nav = StubNavigator {
        per_instance: vec![
            vec![pia("1-02-0-001")],
            vec![pia("1-08-4-004")],
            vec![pia("1-10-1")],
        ],
    };
    let external = MapExternalProvider::new(Default::default());
    let segments = vec![];
    let ctx = EvaluationContext::with_navigator("55218", &external, &segments, &nav);
    let path = ["SG4", "SG8"];
    let ctx1 = ctx.with_scope(GroupScope {
        group_path: &path,
        instance_index: 1,
    });
    let found = ctx1.scoped_find_segments("PIA");
    assert_eq!(found.len(), 1);
    assert_eq!(found[0].elements[1][0], "1-08-4-004");
}

#[test]
fn scoped_find_segments_falls_back_to_message_wide_when_scope_is_none() {
    let external = MapExternalProvider::new(Default::default());
    let segments = vec![pia("1-02-0-001"), pia("1-10-1")];
    let ctx = EvaluationContext::new("55218", &external, &segments);
    let found = ctx.scoped_find_segments("PIA");
    assert_eq!(found.len(), 2);
}

#[test]
fn scoped_find_segments_with_qualifier_narrows_by_scope() {
    let nav = StubNavigator {
        per_instance: vec![
            vec![pia("1-02-0-001"), pia("1-08-4-004")],
            vec![pia("1-10-1")],
        ],
    };
    let external = MapExternalProvider::new(Default::default());
    let segments = vec![];
    let ctx = EvaluationContext::with_navigator("55218", &external, &segments, &nav);
    let path = ["SG4", "SG8"];
    // Scope to instance 0, look for PIA with Z02 qualifier on element 0.
    let ctx0 = ctx.with_scope(GroupScope {
        group_path: &path,
        instance_index: 0,
    });
    let found = ctx0.scoped_find_segments_with_qualifier("PIA", 0, "Z02");
    assert_eq!(found.len(), 2, "both PIAs in instance 0 are Z02");
}

#[test]
fn scoped_find_segments_with_qualifier_falls_back_to_message_wide() {
    let external = MapExternalProvider::new(Default::default());
    let segments = vec![pia("1-02-0-001"), pia("1-10-1")];
    let ctx = EvaluationContext::new("55218", &external, &segments);
    let found = ctx.scoped_find_segments_with_qualifier("PIA", 0, "Z02");
    assert_eq!(found.len(), 2);
}