edifact-parser 0.1.60

Streaming EDIFACT tokenizer and SAX-style parser — standalone, no BO4E dependency
Documentation
//! Asserts every (message_type, version) tuple in the supported FV MIG XMLs
//! is present in `VERSION_TABLE`. Skipped if `xml-migs-and-ahbs/` submodule
//! is not initialized.

use std::fs;
use std::path::Path;

use edifact_parser::{detect_format_version, DetectError};

const SUPPORTED_FVS: &[&str] = &["FV2504", "FV2510", "FV2604", "FV2610"];

#[test]
fn version_table_covers_mig_xml() {
    let xml_root = Path::new("../../xml-migs-and-ahbs");
    if !xml_root.exists() {
        eprintln!("SKIP: xml-migs-and-ahbs submodule not present");
        return;
    }

    let mut missing = Vec::new();
    for fv in SUPPORTED_FVS {
        let fv_dir = xml_root.join(fv);
        if !fv_dir.exists() {
            continue;
        }
        for entry in fs::read_dir(&fv_dir).unwrap() {
            let entry = entry.unwrap();
            let name = entry.file_name().to_string_lossy().to_string();
            if !name.ends_with(".xml") || !name.contains("MIG") {
                continue;
            }
            let Some(msg_type) = name.split('_').next() else {
                continue;
            };
            let xml = fs::read_to_string(entry.path()).unwrap();
            let Some(version) = extract_versionsnummer(&xml) else {
                missing.push(format!("{fv}/{name}: no Versionsnummer attribute"));
                continue;
            };
            let synthetic = format!(
                "UNB+UNOC:3+s+r+250505:0826+R'\
                 UNH+R+{msg_type}:D:XXA:UN:{version}'\
                 UNT+1+R'UNZ+1+R'"
            );
            match detect_format_version(&synthetic) {
                Ok(result) => {
                    // Either the picked FV matches, or this FV is mentioned in the
                    // ambiguity note (i.e., a row for this FV exists, but a newer FV won
                    // the newest-first sort).
                    let covered = result.format_version == *fv
                        || result.note.as_deref().is_some_and(|n| n.contains(fv));
                    if !covered {
                        missing.push(format!("{fv}/{msg_type}/{version}"));
                    }
                }
                Err(DetectError::UnknownVersion { .. }) => {
                    missing.push(format!("{fv}/{msg_type}/{version}"));
                }
                Err(DetectError::UnsupportedMessageType { .. }) => {}
                Err(other) => panic!("unexpected error for {msg_type} {version}: {other}"),
            }
        }
    }

    assert!(
        missing.is_empty(),
        "VERSION_TABLE missing entries (add to format_detection.rs):\n  {}",
        missing.join("\n  ")
    );
}

fn extract_versionsnummer(xml: &str) -> Option<String> {
    let needle = "Versionsnummer=\"";
    let start = xml.find(needle)? + needle.len();
    let rest = &xml[start..];
    let end = rest.find('"')?;
    Some(rest[..end].to_string())
}