vtcode_core/code/code_quality/metrics/
complexity.rs

1use std::fs;
2use std::path::Path;
3use walkdir::WalkDir;
4
5/// Complexity analysis results
6#[derive(Debug, Clone)]
7pub struct ComplexityResult {
8    pub cyclomatic_complexity: f64,
9    pub cognitive_complexity: f64,
10    pub lines_of_code: usize,
11    pub maintainability_index: f64,
12}
13
14/// Complexity analyzer for code quality metrics
15pub struct ComplexityAnalyzer;
16
17impl ComplexityAnalyzer {
18    pub fn new() -> Self {
19        Self
20    }
21
22    /// Analyze complexity of a source file
23    pub fn analyze_file(&self, _file_path: &Path, source: &str) -> ComplexityResult {
24        // Calculate actual complexity metrics
25        let lines_of_code = source.lines().count();
26
27        // Calculate cyclomatic complexity (simplified)
28        let cyclomatic_complexity = self.calculate_cyclomatic_complexity(source);
29
30        // Calculate cognitive complexity (simplified)
31        let cognitive_complexity = self.calculate_cognitive_complexity(source);
32
33        // Calculate maintainability index
34        let maintainability_index =
35            self.calculate_maintainability_index(cyclomatic_complexity, lines_of_code, source);
36
37        ComplexityResult {
38            cyclomatic_complexity,
39            cognitive_complexity,
40            lines_of_code,
41            maintainability_index,
42        }
43    }
44
45    /// Analyze complexity of a directory
46    pub fn analyze_directory(&self, dir_path: &Path) -> Vec<ComplexityResult> {
47        let mut results = Vec::new();
48
49        // Walk the directory to analyze all source files
50        for entry in WalkDir::new(dir_path)
51            .follow_links(true)
52            .into_iter()
53            .filter_map(|e| e.ok())
54        {
55            if entry.file_type().is_file() {
56                if let Some(ext) = entry.path().extension() {
57                    // Check if this is a source code file
58                    let source_extensions = ["rs", "js", "ts", "py", "java", "cpp", "c", "go"];
59                    if source_extensions.contains(&ext.to_str().unwrap_or("")) {
60                        if let Ok(content) = fs::read_to_string(entry.path()) {
61                            let result = self.analyze_file(entry.path(), &content);
62                            results.push(result);
63                        }
64                    }
65                }
66            }
67        }
68
69        results
70    }
71
72    /// Calculate cyclomatic complexity (simplified implementation)
73    fn calculate_cyclomatic_complexity(&self, source: &str) -> f64 {
74        let mut complexity = 1.0; // Base complexity
75
76        // Count decision points
77        for line in source.lines() {
78            let trimmed = line.trim();
79            if trimmed.starts_with("if ")
80                || trimmed.starts_with("while ")
81                || trimmed.starts_with("for ")
82                || trimmed.starts_with("match ")
83                || trimmed.contains(" && ")
84                || trimmed.contains(" || ")
85                || trimmed.starts_with("case ")
86                || trimmed.starts_with("catch ")
87            {
88                complexity += 1.0;
89            }
90        }
91
92        complexity
93    }
94
95    /// Calculate cognitive complexity (simplified implementation)
96    fn calculate_cognitive_complexity(&self, source: &str) -> f64 {
97        let mut complexity = 0.0;
98
99        // Count nesting levels and complex structures
100        let mut nesting_level: i32 = 0;
101        for line in source.lines() {
102            let trimmed = line.trim();
103
104            // Increase nesting level for control structures
105            if trimmed.starts_with("if ")
106                || trimmed.starts_with("while ")
107                || trimmed.starts_with("for ")
108                || trimmed.starts_with("match ")
109            {
110                nesting_level += 1;
111                complexity += nesting_level as f64;
112            }
113
114            // Decrease nesting level for closing braces/keywords
115            if trimmed == "}" || trimmed == "end" {
116                nesting_level = nesting_level.saturating_sub(1);
117            }
118        }
119
120        complexity
121    }
122
123    /// Calculate maintainability index
124    fn calculate_maintainability_index(
125        &self,
126        cyclomatic_complexity: f64,
127        lines_of_code: usize,
128        source: &str,
129    ) -> f64 {
130        // Count Halstead volume approximation (simplified)
131        let halstead_volume = source.chars().count() as f64;
132
133        // Maintainability index formula (simplified)
134        let mi = 171.0
135            - 5.2 * halstead_volume.ln()
136            - 0.23 * cyclomatic_complexity
137            - 16.2 * (lines_of_code as f64).ln();
138
139        // Normalize to 0-100 scale
140        mi.max(0.0).min(100.0)
141    }
142}