1pub mod ast;
2pub mod cache;
3pub mod lazy;
4pub mod metrics;
5pub mod monadic;
6
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::path::PathBuf;
11
12#[derive(Clone, Debug, Serialize, Deserialize)]
13pub struct AnalysisResults {
14 pub project_path: PathBuf,
15 pub timestamp: DateTime<Utc>,
16 pub complexity: ComplexityReport,
17 pub technical_debt: TechnicalDebtReport,
18 pub dependencies: DependencyReport,
19 pub duplications: Vec<DuplicationBlock>,
20}
21
22#[derive(Clone, Debug, Serialize, Deserialize)]
23pub struct ComplexityReport {
24 pub metrics: Vec<FunctionMetrics>,
25 pub summary: ComplexitySummary,
26}
27
28#[derive(Clone, Debug, Serialize, Deserialize)]
29pub struct ComplexitySummary {
30 pub total_functions: usize,
31 pub average_complexity: f64,
32 pub max_complexity: u32,
33 pub high_complexity_count: usize,
34}
35
36#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
37pub struct FunctionMetrics {
38 pub name: String,
39 pub file: PathBuf,
40 pub line: usize,
41 pub cyclomatic: u32,
42 pub cognitive: u32,
43 pub nesting: u32,
44 pub length: usize,
45 pub is_test: bool,
46 pub visibility: Option<String>, pub is_trait_method: bool, }
49
50impl FunctionMetrics {
51 pub fn new(name: String, file: PathBuf, line: usize) -> Self {
52 Self {
53 name,
54 file,
55 line,
56 cyclomatic: 1,
57 cognitive: 0,
58 nesting: 0,
59 length: 0,
60 is_test: false,
61 visibility: None,
62 is_trait_method: false,
63 }
64 }
65
66 pub fn is_complex(&self, threshold: u32) -> bool {
67 self.cyclomatic > threshold || self.cognitive > threshold
68 }
69}
70
71#[derive(Clone, Debug, Serialize, Deserialize)]
72pub struct TechnicalDebtReport {
73 pub items: Vec<DebtItem>,
74 pub by_type: HashMap<DebtType, Vec<DebtItem>>,
75 pub priorities: Vec<Priority>,
76 pub duplications: Vec<DuplicationBlock>,
77}
78
79#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
80pub struct DebtItem {
81 pub id: String,
82 pub debt_type: DebtType,
83 pub priority: Priority,
84 pub file: PathBuf,
85 pub line: usize,
86 pub message: String,
87 pub context: Option<String>,
88}
89
90#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Copy)]
91pub enum DebtType {
92 Todo,
93 Fixme,
94 CodeSmell,
95 Duplication,
96 Complexity,
97 Dependency,
98 TestComplexity,
100 TestTodo,
101 TestDuplication,
102}
103
104impl std::fmt::Display for DebtType {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 static DISPLAY_STRINGS: &[(DebtType, &str)] = &[
107 (DebtType::Todo, "TODO"),
108 (DebtType::Fixme, "FIXME"),
109 (DebtType::CodeSmell, "Code Smell"),
110 (DebtType::Duplication, "Duplication"),
111 (DebtType::Complexity, "Complexity"),
112 (DebtType::Dependency, "Dependency"),
113 (DebtType::TestComplexity, "Test Complexity"),
114 (DebtType::TestTodo, "Test TODO"),
115 (DebtType::TestDuplication, "Test Duplication"),
116 ];
117
118 let display_str = DISPLAY_STRINGS
119 .iter()
120 .find(|(dt, _)| dt == self)
121 .map(|(_, s)| *s)
122 .unwrap_or("Unknown");
123
124 write!(f, "{display_str}")
125 }
126}
127
128#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Copy, Ord, PartialOrd)]
129pub enum Priority {
130 Low,
131 Medium,
132 High,
133 Critical,
134}
135
136impl std::fmt::Display for Priority {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 static DISPLAY_STRINGS: &[(Priority, &str)] = &[
139 (Priority::Low, "Low"),
140 (Priority::Medium, "Medium"),
141 (Priority::High, "High"),
142 (Priority::Critical, "Critical"),
143 ];
144
145 let display_str = DISPLAY_STRINGS
146 .iter()
147 .find(|(p, _)| p == self)
148 .map(|(_, s)| *s)
149 .unwrap_or("Unknown");
150
151 write!(f, "{display_str}")
152 }
153}
154
155#[derive(Clone, Debug, Serialize, Deserialize)]
156pub struct DependencyReport {
157 pub modules: Vec<ModuleDependency>,
158 pub circular: Vec<CircularDependency>,
159}
160
161#[derive(Clone, Debug, Serialize, Deserialize)]
162pub struct ModuleDependency {
163 pub module: String,
164 pub dependencies: Vec<String>,
165 pub dependents: Vec<String>,
166}
167
168#[derive(Clone, Debug, Serialize, Deserialize)]
169pub struct CircularDependency {
170 pub cycle: Vec<String>,
171}
172
173#[derive(Clone, Debug, Serialize, Deserialize)]
174pub struct DuplicationBlock {
175 pub hash: String,
176 pub lines: usize,
177 pub locations: Vec<DuplicationLocation>,
178}
179
180#[derive(Clone, Debug, Serialize, Deserialize)]
181pub struct DuplicationLocation {
182 pub file: PathBuf,
183 pub start_line: usize,
184 pub end_line: usize,
185}
186
187#[derive(Clone, Debug, Serialize, Deserialize)]
188pub struct FileMetrics {
189 pub path: PathBuf,
190 pub language: Language,
191 pub complexity: ComplexityMetrics,
192 pub debt_items: Vec<DebtItem>,
193 pub dependencies: Vec<Dependency>,
194 pub duplications: Vec<DuplicationBlock>,
195}
196
197#[derive(Clone, Debug, Serialize, Deserialize, Default)]
198pub struct ComplexityMetrics {
199 pub functions: Vec<FunctionMetrics>,
200 pub cyclomatic_complexity: u32,
201 pub cognitive_complexity: u32,
202}
203
204impl ComplexityMetrics {
205 pub fn from_function(func: &FunctionMetrics) -> Self {
206 Self {
207 functions: vec![func.clone()],
208 cyclomatic_complexity: func.cyclomatic,
209 cognitive_complexity: func.cognitive,
210 }
211 }
212}
213
214#[derive(Clone, Debug, Serialize, Deserialize)]
215pub struct Dependency {
216 pub name: String,
217 pub kind: DependencyKind,
218}
219
220#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
221pub enum DependencyKind {
222 Import,
223 Module,
224 Package,
225}
226
227#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Copy)]
228pub enum Language {
229 Rust,
230 Python,
231 JavaScript,
232 TypeScript,
233 Unknown,
234}
235
236impl Language {
237 pub fn from_extension(ext: &str) -> Self {
238 static EXTENSION_MAP: &[(&[&str], Language)] = &[
239 (&["rs"], Language::Rust),
240 (&["py"], Language::Python),
241 (&["js", "jsx", "mjs", "cjs"], Language::JavaScript),
242 (&["ts", "tsx", "mts", "cts"], Language::TypeScript),
243 ];
244
245 EXTENSION_MAP
246 .iter()
247 .find(|(exts, _)| exts.contains(&ext))
248 .map(|(_, lang)| *lang)
249 .unwrap_or(Language::Unknown)
250 }
251}
252
253impl std::fmt::Display for Language {
254 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255 static DISPLAY_STRINGS: &[(Language, &str)] = &[
256 (Language::Rust, "Rust"),
257 (Language::Python, "Python"),
258 (Language::JavaScript, "JavaScript"),
259 (Language::TypeScript, "TypeScript"),
260 (Language::Unknown, "Unknown"),
261 ];
262
263 let display_str = DISPLAY_STRINGS
264 .iter()
265 .find(|(l, _)| l == self)
266 .map(|(_, s)| *s)
267 .unwrap_or("Unknown");
268
269 write!(f, "{display_str}")
270 }
271}