Skip to main content

cha_core/
baseline.rs

1use crate::Finding;
2use serde::{Deserialize, Serialize};
3use std::collections::HashSet;
4use std::path::Path;
5
6/// A baseline is a set of finding fingerprints representing known/accepted issues.
7#[derive(Debug, Serialize, Deserialize, Default)]
8pub struct Baseline {
9    pub fingerprints: HashSet<String>,
10}
11
12impl Baseline {
13    /// Build a baseline from a list of findings.
14    pub fn from_findings(findings: &[Finding], project_root: &Path) -> Self {
15        let fingerprints = findings
16            .iter()
17            .map(|f| fingerprint(f, project_root))
18            .collect();
19        Self { fingerprints }
20    }
21
22    /// Load from a JSON file.
23    pub fn load(path: &Path) -> Option<Self> {
24        let content = std::fs::read_to_string(path).ok()?;
25        serde_json::from_str(&content).ok()
26    }
27
28    /// Save to a JSON file.
29    pub fn save(&self, path: &Path) -> std::io::Result<()> {
30        if let Some(dir) = path.parent() {
31            std::fs::create_dir_all(dir)?;
32        }
33        let json = serde_json::to_string_pretty(self).unwrap_or_default();
34        std::fs::write(path, json)
35    }
36
37    /// Filter out findings that are already in the baseline.
38    pub fn filter_new(&self, findings: Vec<Finding>, project_root: &Path) -> Vec<Finding> {
39        findings
40            .into_iter()
41            .filter(|f| !self.fingerprints.contains(&fingerprint(f, project_root)))
42            .collect()
43    }
44}
45
46/// Stable fingerprint: smell_name + relative_path + symbol_name.
47/// Deliberately excludes line numbers so that minor edits don't invalidate the baseline.
48fn fingerprint(f: &Finding, project_root: &Path) -> String {
49    let rel = f
50        .location
51        .path
52        .strip_prefix(project_root)
53        .unwrap_or(&f.location.path)
54        .to_string_lossy();
55    let symbol = f.location.name.as_deref().unwrap_or("");
56    format!("{}:{}:{}", f.smell_name, rel, symbol)
57}