sim-lib-skill 0.1.0-rc.1

SIM workspace package for sim lib skill.
Documentation
use std::{fs, path::Path};

#[test]
fn source_tree_has_no_parallel_skill_or_mcp_execution_families() {
    let root = workspace_root();
    let banned = [
        concat!("Skill", "Tool"),
        concat!("Adapter", "Tool"),
        concat!("Gateway", "Skill", "Registry"),
        concat!("Mcp", "Tool", "Registry"),
        concat!("Mcp", "Tool", "Executor"),
        concat!("Mcp", "Resource", "Registry"),
        concat!("Mcp", "Prompt", "Registry"),
        concat!("Mcp", "Eval", "Loop"),
        concat!("Mcp", "Progress", "Buffer"),
        concat!("Mcp", "Stream", "Queue"),
        concat!("Mcp", "Progress", "Queue"),
        concat!("Mcp", "Http", "Stack"),
        concat!("Mcp", "Http", "Server"),
        concat!("Mcp", "Sse", "Server"),
        concat!("Mcp", "Web", "Socket", "Server"),
        concat!("Skill", "Http", "Registry"),
        concat!("Http", "Skill", "Registry"),
        concat!("Skill", "Http", "Executor"),
        concat!("Skill", "Process", "Registry"),
        concat!("Process", "Skill", "Registry"),
        concat!("Skill", "Process", "Executor"),
        concat!("Process", "Skill", "Executor"),
        concat!("Skill", "Cache", "Registry"),
        concat!("Skill", "Cassette", "Registry"),
        concat!("Skill", "Audit", "Registry"),
        concat!("Skill", "Cache", "Executor"),
        concat!("Skill", "Cassette", "Executor"),
        concat!("Mcp", "Skill", "Callable"),
    ];
    let mut hits = Vec::new();

    for source_root in ["crates", "src"] {
        let path = root.join(source_root);
        if path.exists() {
            scan_for_banned_names(&path, &banned, &mut hits);
        }
    }

    assert!(
        hits.is_empty(),
        "parallel skill/MCP execution family names found:\n{}",
        hits.join("\n")
    );
}

fn workspace_root() -> &'static Path {
    Path::new(env!("CARGO_MANIFEST_DIR"))
        .parent()
        .and_then(Path::parent)
        .expect("sim-lib-skill should live under crates/")
}

fn scan_for_banned_names(path: &Path, banned: &[&str], hits: &mut Vec<String>) {
    let metadata = fs::metadata(path)
        .unwrap_or_else(|err| panic!("failed to read metadata for {}: {err}", path.display()));
    if metadata.is_dir() {
        for entry in fs::read_dir(path)
            .unwrap_or_else(|err| panic!("failed to read {}: {err}", path.display()))
        {
            let entry = entry
                .unwrap_or_else(|err| panic!("failed to read entry in {}: {err}", path.display()));
            scan_for_banned_names(&entry.path(), banned, hits);
        }
        return;
    }

    let Ok(contents) = fs::read_to_string(path) else {
        return;
    };
    for (line_index, line) in contents.lines().enumerate() {
        for name in banned {
            if line.contains(name) {
                hits.push(format!("{}:{}:{name}", path.display(), line_index + 1));
            }
        }
    }
}