gruff-rs 0.4.0

Rust static analyzer and quality linter for CI: dead-code, complexity, security, secrets, and architecture rules with deterministic SARIF/JSON output and baseline support.
use super::*;

#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub(crate) enum FindingScope {
    Line,
    Symbol,
    File,
    Project,
}

impl FindingScope {
    pub(crate) fn as_str(self) -> &'static str {
        match self {
            Self::Line => "line",
            Self::Symbol => "symbol",
            Self::File => "file",
            Self::Project => "project",
        }
    }
}

pub(crate) fn infer_finding_scope(
    rule_id: &str,
    symbol: Option<&str>,
    line: Option<usize>,
) -> FindingScope {
    match rule_id {
        "size.file-length" | "architecture.module-fan-out" => FindingScope::File,
        "docs.missing-readme" | "architecture.public-api-surface" | "architecture.large-module" => {
            FindingScope::Project
        }
        id if id.starts_with("dependency.") => FindingScope::Project,
        _ if symbol.is_some() => FindingScope::Symbol,
        _ if line.is_some() => FindingScope::Line,
        _ => FindingScope::Project,
    }
}

pub(crate) fn compute_stable_identity(
    rule_id: &str,
    file_path: &str,
    scope: FindingScope,
    symbol: Option<&str>,
    message: &str,
) -> String {
    let mut hasher = Sha256::new();
    hasher.update(rule_id.as_bytes());
    hasher.update(b"\0");
    hasher.update(file_path.as_bytes());
    hasher.update(b"\0");
    hasher.update(stable_identity_subject(scope, symbol, message).as_bytes());
    format!("{:x}", hasher.finalize())[..16].to_string()
}

fn stable_identity_subject<'a>(
    scope: FindingScope,
    symbol: Option<&'a str>,
    message: &'a str,
) -> &'a str {
    if let Some(symbol) = symbol {
        return symbol;
    }
    match scope {
        FindingScope::File => "file",
        FindingScope::Project => "project",
        FindingScope::Symbol => "symbol",
        FindingScope::Line => message,
    }
}