weavatrix-rust 2.16.4

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, n8n, Dify, Agent catalogs, Mermaid, and Web3 ABI
Documentation
use crate::engine::RepositoryState;
use std::collections::BTreeSet;
use std::fs;
use std::path::Path;
use weavatrix_graph::NodeKind;

/// Whether an import names a library target produced by this repository.
///
/// Cargo binary and example targets legitimately import their sibling library
/// by package name. That is a project edge, not a missing external dependency.
pub(super) fn contains(state: &RepositoryState, ecosystem: &str, package: &str) -> bool {
    ecosystem == "cargo"
        && cargo_names(state).contains(&package.trim().to_ascii_lowercase().replace('-', "_"))
}

fn cargo_names(state: &RepositoryState) -> BTreeSet<String> {
    state
        .graph()
        .nodes()
        .iter()
        .filter(|node| {
            node.kind == NodeKind::File
                && Path::new(&node.label)
                    .file_name()
                    .is_some_and(|name| name.eq_ignore_ascii_case("Cargo.toml"))
        })
        .filter_map(|node| fs::read_to_string(state.root().join(&node.label)).ok())
        .filter_map(|manifest| cargo_package_name(&manifest))
        .map(|name| name.to_ascii_lowercase().replace('-', "_"))
        .collect()
}

fn cargo_package_name(manifest: &str) -> Option<String> {
    let mut package_section = false;
    for raw in manifest.lines() {
        let line = raw.split('#').next().unwrap_or_default().trim();
        if line.starts_with('[') && line.ends_with(']') {
            package_section = line.trim_matches(['[', ']']) == "package";
            continue;
        }
        if !package_section {
            continue;
        }
        let Some((key, value)) = line.split_once('=') else {
            continue;
        };
        if key.trim() == "name" {
            return Some(value.trim().trim_matches(['"', '\'']).to_owned());
        }
    }
    None
}

#[cfg(test)]
mod tests {
    use super::cargo_package_name;

    #[test]
    fn cargo_package_name_reads_the_package_table() {
        assert_eq!(
            cargo_package_name("[package]\nname = \"weavatrix-rust\"\n"),
            Some("weavatrix-rust".into())
        );
        assert_eq!(
            cargo_package_name("[workspace]\n[package]\n# name = \"no\"\nname = 'inner'\n"),
            Some("inner".into())
        );
        assert_eq!(cargo_package_name("[lib]\nname = \"x\"\n"), None);
        assert_eq!(cargo_package_name("[package]\nversion = \"1\"\n"), None);
    }
}