Skip to main content

slopguard_core/
finding.rs

1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5use crate::rule::{Category, RuleId, Severity};
6
7/// A single finding from scanning a source file.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct Finding {
10    pub rule_id: RuleId,
11    pub severity: Severity,
12    pub category: Category,
13    pub message: String,
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub note: Option<String>,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub fix: Option<String>,
18    pub file: PathBuf,
19    pub line: usize,
20    pub column: usize,
21    pub end_line: usize,
22    pub end_column: usize,
23    pub matched_text: String,
24    /// LLM confidence in `[0.0, 1.0]` for findings confirmed by an AI rule.
25    /// `None` for pure AST findings. Serialized in JSON/SARIF only; the text
26    /// output ignores it.
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub confidence: Option<f64>,
29    /// True when severity escalation raised this finding from warning to error
30    /// because its rule fired repeatedly in the same file. Always serialized so
31    /// consumers can filter on `escalated == false` without a null check.
32    #[serde(default)]
33    pub escalated: bool,
34}
35
36/// Aggregate statistics from a scan run.
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct ScanStats {
39    pub errors: usize,
40    pub warnings: usize,
41    pub total: usize,
42    pub files_scanned: usize,
43    /// Number of findings dropped because they were present in the baseline.
44    #[serde(default)]
45    pub baseline_filtered: usize,
46    /// The git ref the scan was diffed against in `--diff` mode ("HEAD" when
47    /// no `--base` was given). `None` for a normal full scan.
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    pub diff_base: Option<String>,
50    /// Number of changed files selected by `--diff`. `None` for a full scan.
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    pub files_changed: Option<usize>,
53}
54
55/// Cache statistics for a scan run.
56#[derive(Debug, Clone, Default, Serialize, Deserialize)]
57pub struct CacheStats {
58    pub cached: usize,
59    pub changed: usize,
60}
61
62/// The result of scanning one or more paths.
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct ScanResult {
65    pub findings: Vec<Finding>,
66    pub stats: ScanStats,
67    #[serde(default)]
68    pub cache_stats: Option<CacheStats>,
69}