use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use crate::knowledge::KnowledgeNature;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum FindingKind {
Canonical,
Heuristic,
Style,
Conflict,
Naming,
FileStructure,
ImportOrganization,
Testing,
ErrorHandling,
Export,
DependencyWrapper,
#[default]
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum AnchorKind {
#[default]
CallSite,
Declaration,
ImportLine,
FileLevel,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ConventionFinding {
pub file_path: PathBuf,
pub detector_name: String,
pub nature: KnowledgeNature,
#[serde(default)]
pub kind: FindingKind,
pub description: String,
pub evidence: Vec<CodeEvidence>,
pub follows_convention: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CodeEvidence {
pub file: PathBuf,
pub line: usize,
pub end_line: usize,
pub snippet: String,
#[serde(default)]
pub snippet_start_line: usize,
#[serde(default)]
pub anchor: AnchorKind,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct DetectorResults {
pub file_path: PathBuf,
pub findings: Vec<ConventionFinding>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn snippet_start_line_backward_compat_deserialization() {
let json = r#"{
"file": "src/main.rs",
"line": 10,
"end_line": 12,
"snippet": "fn main() {}"
}"#;
let evidence: CodeEvidence = serde_json::from_str(json).unwrap();
assert_eq!(evidence.snippet_start_line, 0);
assert_eq!(evidence.line, 10);
}
}