astmap-core 0.0.2

Core domain types and logic for astmap
Documentation
//! Architecture layer enforcement test.
//! Parses `use crate::...` statements and verifies dependency direction.
//!
//! Layer rules:
//!   Layer 0: error      — depends on nothing (foundation)
//!   Layer 1: model      — depends on error (validation errors)
//!   Layer 2: port       — depends on model, error
//!   Layer 3: scanner    — depends on model, error, port
//!   Layer 3: diff       — depends on model, error, port
//!   Layer 3: export     — depends on model, error, port
//!   No sibling deps: scanner/diff/export do not import from each other.

#[path = "../../../tests/arch.rs"]
mod arch;

use std::collections::HashMap;
use std::path::Path;

fn allowed_deps() -> HashMap<&'static str, Vec<&'static str>> {
    let mut m = HashMap::new();
    m.insert("error", vec![]);
    m.insert("model", vec!["error"]);
    m.insert("port", vec!["model", "error"]);
    m.insert("scanner", vec!["model", "error", "port"]);
    m.insert("diff", vec!["model", "error", "port"]);
    m.insert("export", vec!["model", "error", "port"]);
    m
}

#[test]
fn enforce_layer_dependencies() {
    let src_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
    let allowed = allowed_deps();

    for (module, allowed_deps) in &allowed {
        let violations = arch::find_violations(&src_dir, module, |line| {
            let dep = arch::parse_crate_dep(line)?;
            if !allowed_deps.contains(&dep.as_str()) {
                Some(format!("imports crate::{dep} (allowed: {allowed_deps:?})"))
            } else {
                None
            }
        });
        assert!(
            violations.is_empty(),
            "LAYER VIOLATION in {module}:\n{}",
            violations.join("\n"),
        );
    }
}