cartulary 0.3.0-alpha.1

The knowledge layer of your project — decisions, issues, docs, all in one place.
Documentation
//! Enforce that an issue's id suffix matches its directory's leading
//! prefix.

use crate::domain::model::check::{
    extract_id_prefix_from_path, id_prefix_matches, CheckViolationKind, Severity,
};
use crate::domain::usecases::check::{CheckViolation, IssueCheckCtx, IssueFinding, IssueRule};

pub struct IdMatchesDirectoryRule;

pub const RULE_ID: &str = "issue/id-matches-directory";

impl IssueRule for IdMatchesDirectoryRule {
    fn id(&self) -> &'static str {
        RULE_ID
    }

    fn find(&self, ctx: &IssueCheckCtx<'_>) -> anyhow::Result<Vec<IssueFinding>> {
        let mut out = Vec::new();
        for (path, issue) in ctx.issues {
            let parent = path.parent().unwrap_or(path);
            let Some(dir_prefix) = extract_id_prefix_from_path(parent) else {
                continue;
            };
            let id_suffix = issue.id.suffix();
            if id_prefix_matches(&dir_prefix, id_suffix) {
                continue;
            }
            let kind = CheckViolationKind::IdSlugMismatch {
                id_suffix: id_suffix.to_string(),
                dir_prefix: dir_prefix.clone(),
            };
            out.push(CheckViolation {
                rule_id: RULE_ID,
                path: path.clone(),
                severity: Severity::Error,
                kind,
            });
        }
        Ok(out.into_iter().map(IssueFinding::report).collect())
    }
}