scribe_analysis/
analyzer.rs1use scribe_core::Result;
6use crate::ast::AstNode;
7
8#[derive(Debug, Clone)]
9pub struct AnalysisResult {
10 pub complexity: f64,
11 pub maintainability: f64,
12 pub issues: Vec<String>,
13}
14
15impl AnalysisResult {
16 pub fn new() -> Self {
17 Self {
18 complexity: 0.0,
19 maintainability: 1.0,
20 issues: Vec::new(),
21 }
22 }
23}
24
25impl Default for AnalysisResult {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31pub struct CodeAnalyzer;
32
33impl CodeAnalyzer {
34 pub fn new() -> Self {
35 Self
36 }
37
38 pub async fn analyze(&self, ast: &AstNode) -> Result<AnalysisResult> {
39 let mut result = AnalysisResult::new();
40
41 result.complexity = self.calculate_complexity(ast);
43
44 result.maintainability = self.calculate_maintainability(ast, result.complexity);
46
47 result.issues = self.detect_issues(ast);
49
50 Ok(result)
51 }
52
53 fn calculate_complexity(&self, ast: &AstNode) -> f64 {
54 self.complexity_recursive(ast, 1.0)
55 }
56
57 fn complexity_recursive(&self, node: &AstNode, base_complexity: f64) -> f64 {
58 let mut complexity = base_complexity;
59
60 match node.node_type.as_str() {
62 "if" | "while" | "for" | "match" | "switch" => {
63 complexity += 1.0; }
65 "function" | "method" => {
66 complexity += 0.5; }
68 _ => {
69 }
71 }
72
73 for child in &node.children {
75 complexity += self.complexity_recursive(child, 0.0);
76 }
77
78 complexity
79 }
80
81 fn calculate_maintainability(&self, ast: &AstNode, complexity: f64) -> f64 {
82 let size_factor = self.count_nodes(ast) as f64;
83 let nesting_factor = self.max_nesting_depth(ast) as f64;
84
85 let base_maintainability = 100.0;
88 let complexity_penalty = complexity * 5.0;
89 let size_penalty = (size_factor / 50.0) * 10.0;
90 let nesting_penalty = nesting_factor * 15.0;
91
92 (base_maintainability - complexity_penalty - size_penalty - nesting_penalty)
93 .max(0.0) / 100.0
94 }
95
96 fn count_nodes(&self, ast: &AstNode) -> usize {
97 let mut count = 1; for child in &ast.children {
101 count += self.count_nodes(child);
102 }
103
104 count
105 }
106
107 fn max_nesting_depth(&self, ast: &AstNode) -> usize {
108 self.nesting_recursive(ast, 0)
109 }
110
111 fn nesting_recursive(&self, ast: &AstNode, current_depth: usize) -> usize {
112 let mut max_depth = current_depth;
113
114 let next_depth = match ast.node_type.as_str() {
116 "if" | "while" | "for" | "match" | "switch" | "function" | "method" => {
117 current_depth + 1
118 }
119 _ => current_depth
120 };
121
122 max_depth = max_depth.max(next_depth);
123
124 for child in &ast.children {
126 max_depth = max_depth.max(self.nesting_recursive(child, next_depth));
127 }
128
129 max_depth
130 }
131
132 fn detect_issues(&self, ast: &AstNode) -> Vec<String> {
133 let mut issues = Vec::new();
134
135 let complexity = self.calculate_complexity(ast);
136 if complexity > 10.0 {
137 issues.push(format!("High cyclomatic complexity: {:.1}", complexity));
138 }
139
140 let nesting = self.max_nesting_depth(ast);
141 if nesting > 4 {
142 issues.push(format!("Deep nesting detected: {} levels", nesting));
143 }
144
145 let size = self.count_nodes(ast);
146 if size > 100 {
147 issues.push(format!("Large function detected: {} nodes", size));
148 }
149
150 issues
151 }
152}
153
154impl Default for CodeAnalyzer {
155 fn default() -> Self {
156 Self::new()
157 }
158}