1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4
5use crate::{SourceFile, SourceModel};
6
7#[derive(
9 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema, Default,
10)]
11#[serde(rename_all = "lowercase")]
12pub enum Severity {
13 #[default]
14 Hint,
15 Warning,
16 Error,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)]
21#[serde(rename_all = "snake_case")]
22pub enum SmellCategory {
23 #[default]
24 Bloaters,
25 OoAbusers,
26 ChangePreventers,
27 Dispensables,
28 Couplers,
29 Security,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
34pub struct Location {
35 pub path: PathBuf,
36 pub start_line: usize,
37 #[serde(default, skip_serializing_if = "crate::is_zero_usize")]
39 pub start_col: usize,
40 pub end_line: usize,
41 #[serde(default, skip_serializing_if = "crate::is_zero_usize")]
43 pub end_col: usize,
44 pub name: Option<String>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
49pub struct Finding {
50 pub smell_name: String,
51 pub category: SmellCategory,
52 pub severity: Severity,
53 pub location: Location,
54 pub message: String,
55 pub suggested_refactorings: Vec<String>,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub actual_value: Option<f64>,
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub threshold: Option<f64>,
62 #[serde(skip_serializing_if = "Option::is_none")]
66 pub risk_score: Option<f64>,
67}
68
69pub struct AnalysisContext<'a> {
71 pub file: &'a SourceFile,
72 pub model: &'a SourceModel,
73}
74
75pub trait Plugin: Send + Sync {
77 fn name(&self) -> &str;
79
80 fn version(&self) -> &str {
82 env!("CARGO_PKG_VERSION")
83 }
84
85 fn description(&self) -> &str {
87 ""
88 }
89
90 fn authors(&self) -> Vec<String> {
92 vec![env!("CARGO_PKG_AUTHORS").to_string()]
93 }
94
95 fn smells(&self) -> Vec<String> {
99 Vec::new()
100 }
101
102 fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding>;
104}
105
106pub fn func_location(path: &std::path::Path, f: &crate::FunctionInfo) -> Location {
108 Location {
109 path: path.into(),
110 start_line: f.start_line,
111 start_col: f.name_col,
112 end_line: f.start_line,
113 end_col: f.name_end_col,
114 name: Some(f.name.clone()),
115 }
116}
117
118pub fn class_location(path: &std::path::Path, c: &crate::ClassInfo) -> Location {
120 Location {
121 path: path.into(),
122 start_line: c.start_line,
123 start_col: c.name_col,
124 end_line: c.start_line,
125 end_col: c.name_end_col,
126 name: Some(c.name.clone()),
127 }
128}