debtmap/core/
mod.rs

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", "pub(crate)", or None for private
47    pub is_trait_method: bool,      // Whether this is a trait method implementation
48}
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    // Test-specific debt types
99    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}