smelt_validator/rules/
complexity.rs1use super::ValidationRule;
4use crate::config::ComplexityConfig;
5use crate::validator::{ValidationSeverity, Violation};
6use smelt_core::{IntentRecord, SemanticDelta};
7
8pub struct ComplexityChecker {
10 config: ComplexityConfig,
11}
12
13impl ComplexityChecker {
14 pub fn new(config: ComplexityConfig) -> Self {
16 Self { config }
17 }
18}
19
20impl ValidationRule for ComplexityChecker {
21 fn name(&self) -> &'static str {
22 "complexity"
23 }
24
25 fn validate(&self, delta: &SemanticDelta, _intent: Option<&IntentRecord>) -> Vec<Violation> {
26 let mut violations = Vec::new();
27
28 let complexity_delta = delta.impact_summary.complexity_delta;
30 if complexity_delta > self.config.max_complexity_increase {
31 violations.push(Violation {
32 rule: "complexity-increase".to_string(),
33 severity: if self.config.complexity_error {
34 ValidationSeverity::Error
35 } else {
36 ValidationSeverity::Warning
37 },
38 message: format!(
39 "Complexity increased by {} (threshold: {})",
40 complexity_delta, self.config.max_complexity_increase
41 ),
42 location: None,
43 suggestion: Some(
44 "Consider refactoring to reduce complexity or splitting into smaller functions"
45 .to_string(),
46 ),
47 });
48 }
49
50 for change in &delta.changes {
54 if let smelt_core::SemanticChange::BodyModified {
55 name,
56 file,
57 complexity_delta: func_delta,
58 } = change
59 {
60 if *func_delta > self.config.max_complexity_increase {
61 violations.push(Violation {
62 rule: "function-complexity".to_string(),
63 severity: if self.config.complexity_error {
64 ValidationSeverity::Error
65 } else {
66 ValidationSeverity::Warning
67 },
68 message: format!(
69 "Function '{}' complexity increased by {}",
70 name, func_delta
71 ),
72 location: Some(file.clone()),
73 suggestion: Some(
74 "Extract helper functions or simplify control flow".to_string(),
75 ),
76 });
77 }
78 }
79 }
80
81 violations
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use chrono::Utc;
89 use smelt_core::{ImpactSummary, SemanticChange};
90 use uuid::Uuid;
91
92 fn make_delta_with_complexity(complexity_delta: i32) -> SemanticDelta {
93 SemanticDelta {
94 id: Uuid::new_v4(),
95 intent_id: Uuid::new_v4(),
96 timestamp: Utc::now(),
97 from_snapshot: Uuid::new_v4(),
98 to_snapshot: Uuid::new_v4(),
99 changes: Vec::new(),
100 impact_summary: ImpactSummary {
101 complexity_delta,
102 ..Default::default()
103 },
104 }
105 }
106
107 #[test]
108 fn test_complexity_under_threshold() {
109 let checker = ComplexityChecker::new(ComplexityConfig::default());
110
111 let delta = make_delta_with_complexity(5);
112 let violations = checker.validate(&delta, None);
113 assert!(violations.is_empty());
114 }
115
116 #[test]
117 fn test_complexity_over_threshold() {
118 let checker = ComplexityChecker::new(ComplexityConfig::default());
119
120 let delta = make_delta_with_complexity(15);
121 let violations = checker.validate(&delta, None);
122 assert_eq!(violations.len(), 1);
123 assert_eq!(violations[0].rule, "complexity-increase");
124 }
125
126 #[test]
127 fn test_function_complexity_change() {
128 let checker = ComplexityChecker::new(ComplexityConfig::default());
129
130 let delta = SemanticDelta {
131 id: Uuid::new_v4(),
132 intent_id: Uuid::new_v4(),
133 timestamp: Utc::now(),
134 from_snapshot: Uuid::new_v4(),
135 to_snapshot: Uuid::new_v4(),
136 changes: vec![SemanticChange::BodyModified {
137 name: "complex_function".to_string(),
138 file: "lib.rs".to_string(),
139 complexity_delta: 15,
140 }],
141 impact_summary: ImpactSummary::default(),
142 };
143
144 let violations = checker.validate(&delta, None);
145 assert_eq!(violations.len(), 1);
146 assert_eq!(violations[0].rule, "function-complexity");
147 }
148}