koala-core 1.0.4

Shared types, invariant evaluator, and primitives for the koala framework.
Documentation
use crate::invariant::rules::util::{list_files_with_ext, rel};
use crate::invariant::{Category, Context, Invariant, Outcome};
use std::fs;

/// Files that mention the literal `unsafe` keyword as documentation/detection
/// data, not as compiled code. Skipping them avoids the rule flagging itself
/// or the health collector that mirrors its detection pattern.
const SKIP_FILES: &[&str] = &[
    "crates/koala-core/src/invariant/rules/no_unsafe.rs",
    "crates/koala-health/src/collect.rs",
    "crates/koala-health/tests/snapshot.rs",
];

pub struct NoUnsafe;

impl Invariant for NoUnsafe {
    fn id(&self) -> &'static str {
        "arch.no-unsafe"
    }
    fn category(&self) -> Category {
        Category::Arch
    }
    fn intent(&self) -> &'static str {
        "No `unsafe` blocks, fns, impls, or traits in workspace Rust source."
    }
    fn adr(&self) -> Option<&'static str> {
        Some("ADR-0013")
    }

    fn evaluate(&self, ctx: &Context) -> Outcome {
        let mut hits = Vec::new();
        for path in list_files_with_ext(ctx.root(), "rs") {
            let r = rel(&path, ctx.root());
            if SKIP_FILES.iter().any(|s| r == *s) {
                continue;
            }
            let Ok(content) = fs::read_to_string(&path) else {
                continue;
            };
            for (i, line) in content.lines().enumerate() {
                let trimmed = line.trim_start();
                if trimmed.starts_with("//") || trimmed.starts_with("/*") {
                    continue;
                }
                let is_hit = trimmed.starts_with("unsafe ")
                    || trimmed.starts_with("unsafe{")
                    || trimmed.contains(" unsafe ")
                    || trimmed.contains(" unsafe{");
                if is_hit {
                    hits.push(format!("{}:{}", r, i + 1));
                }
            }
        }
        if hits.is_empty() {
            Outcome::pass()
        } else {
            Outcome::fail_repro(
                format!(
                    "found {} unsafe occurrence(s):\n  {}",
                    hits.len(),
                    hits.join("\n  ")
                ),
                "rg -nP '(^|\\s)unsafe\\s*[\\{a-z]' --type rust",
            )
        }
    }
}