cordance-advise 0.1.1

Cordance advisory engine. Deterministic doctrine checks against project state.
Documentation
//! R-naming-1 — verify workspace crate directory layout.

use camino::Utf8PathBuf;
use cordance_core::advise::{AdviseFinding, Severity};
use cordance_core::pack::CordancePack;

use super::AdviseRule;

pub struct RNaming1;

impl AdviseRule for RNaming1 {
    fn id(&self) -> &'static str {
        "R-naming-1"
    }

    fn doctrine_anchor(&self) -> &'static str {
        "doctrine/principles/naming-and-repo-layout.md"
    }

    fn check(&self, pack: &CordancePack) -> Vec<AdviseFinding> {
        // Find all sources that look like crate manifests: crates/*/Cargo.toml
        let crate_manifests: Vec<_> = pack
            .sources
            .iter()
            .filter(|r| {
                let p = r.path.as_str();
                p.starts_with("crates/") && p.ends_with("Cargo.toml")
            })
            .collect();

        if crate_manifests.is_empty() {
            return vec![];
        }

        // Each crate path should be exactly 3 segments: crates/{name}/Cargo.toml.
        // More segments means nested crates.
        let has_nested = crate_manifests
            .iter()
            .any(|r| r.path.as_str().split('/').count() > 3);

        if !has_nested {
            return vec![];
        }

        vec![AdviseFinding {
            id: self.id().into(),
            severity: Severity::Info,
            summary:
                "Workspace has nested crate layout — verify crate names match directory names."
                    .into(),
            doctrine_anchor: Utf8PathBuf::from(self.doctrine_anchor()),
            project_paths: vec!["crates/".into()],
            remediation: "Each crate directory should be named identically to its package name in \
                 Cargo.toml."
                .into(),
        }]
    }
}