1pub mod context;
2pub mod correlation;
3pub mod delegation;
4pub mod evidence;
5pub mod evidence_calculator;
6pub mod insights;
7pub mod lcov;
8pub mod priority;
9pub mod roi;
10pub mod strategy;
11pub mod thresholds;
12
13use crate::core::ComplexityMetrics;
14use im::Vector;
15use serde::{Deserialize, Serialize};
16use std::path::PathBuf;
17
18#[derive(Clone, Debug, Serialize, Deserialize)]
19pub struct FunctionRisk {
20 pub file: PathBuf,
21 pub function_name: String,
22 pub line_range: (usize, usize),
23 pub cyclomatic_complexity: u32,
24 pub cognitive_complexity: u32,
25 pub coverage_percentage: Option<f64>,
26 pub risk_score: f64,
27 pub test_effort: TestEffort,
28 pub risk_category: RiskCategory,
29 pub is_test_function: bool,
30}
31
32#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
33pub enum RiskCategory {
34 Critical, High, Medium, Low, WellTested, }
40
41#[derive(Clone, Debug, Serialize, Deserialize)]
42pub struct TestEffort {
43 pub estimated_difficulty: Difficulty,
44 pub cognitive_load: u32,
45 pub branch_count: u32,
46 pub recommended_test_cases: u32,
47}
48
49#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
50pub enum Difficulty {
51 Trivial, Simple, Moderate, Complex, VeryComplex, }
57
58#[derive(Clone, Debug, Serialize, Deserialize)]
59pub struct RiskInsight {
60 pub top_risks: Vector<FunctionRisk>,
61 pub risk_reduction_opportunities: Vector<TestingRecommendation>,
62 pub codebase_risk_score: f64,
63 pub complexity_coverage_correlation: Option<f64>,
64 pub risk_distribution: RiskDistribution,
65}
66
67#[derive(Clone, Debug, Serialize, Deserialize)]
68pub struct TestingRecommendation {
69 pub function: String,
70 pub file: PathBuf,
71 pub line: usize,
72 pub current_risk: f64,
73 pub potential_risk_reduction: f64,
74 pub test_effort_estimate: TestEffort,
75 pub rationale: String,
76 pub roi: Option<f64>,
77 pub dependencies: Vec<String>,
78 pub dependents: Vec<String>,
79}
80
81#[derive(Clone, Debug, Serialize, Deserialize)]
82pub struct RiskDistribution {
83 pub critical_count: usize,
84 pub high_count: usize,
85 pub medium_count: usize,
86 pub low_count: usize,
87 pub well_tested_count: usize,
88 pub total_functions: usize,
89}
90
91use self::context::{AnalysisTarget, ContextAggregator, ContextualRisk};
92use self::strategy::{EnhancedRiskStrategy, RiskCalculator, RiskContext};
93
94pub struct RiskAnalyzer {
95 strategy: Box<dyn RiskCalculator>,
96 debt_score: Option<f64>,
97 debt_threshold: Option<f64>,
98 context_aggregator: Option<ContextAggregator>,
99}
100
101impl Clone for RiskAnalyzer {
102 fn clone(&self) -> Self {
103 Self {
104 strategy: self.strategy.box_clone(),
105 debt_score: self.debt_score,
106 debt_threshold: self.debt_threshold,
107 context_aggregator: None, }
109 }
110}
111
112impl Default for RiskAnalyzer {
113 fn default() -> Self {
114 Self {
115 strategy: Box::new(EnhancedRiskStrategy::default()),
116 debt_score: None,
117 debt_threshold: None,
118 context_aggregator: None,
119 }
120 }
121}
122
123impl RiskAnalyzer {
124 pub fn with_debt_context(mut self, debt_score: f64, debt_threshold: f64) -> Self {
125 self.debt_score = Some(debt_score);
126 self.debt_threshold = Some(debt_threshold);
127 self
128 }
129
130 pub fn with_context_aggregator(mut self, aggregator: ContextAggregator) -> Self {
131 self.context_aggregator = Some(aggregator);
132 self
133 }
134
135 pub fn analyze_function(
136 &self,
137 file: PathBuf,
138 function_name: String,
139 line_range: (usize, usize),
140 complexity: &ComplexityMetrics,
141 coverage: Option<f64>,
142 is_test: bool,
143 ) -> FunctionRisk {
144 let context = RiskContext {
145 file,
146 function_name,
147 line_range,
148 complexity: complexity.clone(),
149 coverage,
150 debt_score: self.debt_score,
151 debt_threshold: self.debt_threshold,
152 is_test,
153 is_recognized_pattern: false,
154 pattern_type: None,
155 pattern_confidence: 0.0,
156 };
157
158 self.strategy.calculate(&context)
159 }
160
161 #[allow(clippy::too_many_arguments)]
162 pub fn analyze_function_with_context(
163 &mut self,
164 file: PathBuf,
165 function_name: String,
166 line_range: (usize, usize),
167 complexity: &ComplexityMetrics,
168 coverage: Option<f64>,
169 is_test: bool,
170 root_path: PathBuf,
171 ) -> (FunctionRisk, Option<ContextualRisk>) {
172 let base_risk = self.analyze_function(
173 file.clone(),
174 function_name.clone(),
175 line_range,
176 complexity,
177 coverage,
178 is_test,
179 );
180
181 let contextual_risk = if let Some(ref mut aggregator) = self.context_aggregator {
182 let target = AnalysisTarget {
183 root_path,
184 file_path: file,
185 function_name,
186 line_range,
187 };
188
189 let context_map = aggregator.analyze(&target);
190 Some(ContextualRisk::new(base_risk.risk_score, &context_map))
191 } else {
192 None
193 };
194
195 (base_risk, contextual_risk)
196 }
197
198 pub fn calculate_risk_score(
199 &self,
200 cyclomatic: u32,
201 cognitive: u32,
202 coverage: Option<f64>,
203 ) -> f64 {
204 let context = RiskContext {
205 file: PathBuf::new(),
206 function_name: String::new(),
207 line_range: (0, 0),
208 complexity: ComplexityMetrics {
209 functions: vec![],
210 cyclomatic_complexity: cyclomatic,
211 cognitive_complexity: cognitive,
212 },
213 coverage,
214 debt_score: self.debt_score,
215 debt_threshold: self.debt_threshold,
216 is_test: false,
217 is_recognized_pattern: false,
218 pattern_type: None,
219 pattern_confidence: 0.0,
220 };
221
222 self.strategy.calculate_risk_score(&context)
223 }
224
225 pub fn calculate_risk_reduction(
226 &self,
227 current_risk: f64,
228 complexity: u32,
229 target_coverage: f64,
230 ) -> f64 {
231 self.strategy
232 .calculate_risk_reduction(current_risk, complexity, target_coverage)
233 }
234}