use super::{AnalysisContext, ExtractablePattern, MatchedPattern};
pub struct ConfidenceScorer;
impl Default for ConfidenceScorer {
fn default() -> Self {
Self::new()
}
}
impl ConfidenceScorer {
pub fn new() -> Self {
Self
}
pub fn score_pattern(pattern: &MatchedPattern, context: &AnalysisContext) -> f32 {
let base_score = Self::calculate_base_score(&pattern.pattern);
let complexity_factor = Self::calculate_complexity_factor(context.complexity_before);
let side_effect_penalty = if context.has_side_effects { 0.7 } else { 1.0 };
let dependency_factor = Self::calculate_dependency_factor(&context.data_dependencies);
(base_score * complexity_factor * side_effect_penalty * dependency_factor).min(1.0)
}
fn calculate_base_score(pattern: &ExtractablePattern) -> f32 {
match pattern {
ExtractablePattern::AccumulationLoop {
filter, transform, ..
} => {
let base = 0.9;
let filter_penalty = if filter.is_some() { 0.95 } else { 1.0 };
let transform_penalty = if transform.is_some() { 0.95 } else { 1.0 };
base * filter_penalty * transform_penalty
}
ExtractablePattern::GuardChainSequence { checks, .. } => {
let base = 0.95;
let length_factor = match checks.len() {
1..=3 => 1.0,
4..=6 => 0.95,
7..=10 => 0.9,
_ => 0.85,
};
base * length_factor
}
ExtractablePattern::TransformationPipeline { stages, .. } => {
let base = 0.85;
let stage_factor = (1.0 - (stages.len() as f32 * 0.02)).max(0.7);
base * stage_factor
}
ExtractablePattern::SimilarBranches {
branch_specific, ..
} => {
let base = 0.75;
let branch_factor = (1.0 - (branch_specific.len() as f32 * 0.05)).max(0.5);
base * branch_factor
}
ExtractablePattern::NestedExtraction { inner_patterns, .. } => {
let base = 0.7;
let inner_avg = if inner_patterns.is_empty() {
1.0
} else {
inner_patterns
.iter()
.map(|p| Self::calculate_base_score(p))
.sum::<f32>()
/ inner_patterns.len() as f32
};
base * inner_avg
}
}
}
fn calculate_complexity_factor(cyclomatic: u32) -> f32 {
match cyclomatic {
0..=5 => 1.0, 6..=10 => 0.95, 11..=15 => 0.9, 16..=20 => 0.85, _ => 0.8, }
}
fn calculate_dependency_factor(dependencies: &[String]) -> f32 {
match dependencies.len() {
0 => 1.0, 1..=2 => 0.95, 3..=4 => 0.9, 5..=7 => 0.85, _ => 0.8, }
}
}
pub struct ConfidenceFactors {
pub has_clear_boundaries: bool,
pub no_external_state: bool,
pub pure_computation: bool,
pub single_responsibility: bool,
pub testable_in_isolation: bool,
}
impl ConfidenceFactors {
pub fn calculate_confidence(&self) -> f32 {
let mut score = 0.0;
let mut factors = 0.0;
if self.has_clear_boundaries {
score += 0.25;
factors += 0.25;
}
if self.no_external_state {
score += 0.25;
factors += 0.25;
}
if self.pure_computation {
score += 0.2;
factors += 0.2;
}
if self.single_responsibility {
score += 0.15;
factors += 0.15;
}
if self.testable_in_isolation {
score += 0.15;
factors += 0.15;
}
if factors > 0.0 {
score / factors
} else {
0.5 }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_confidence_scoring() {
let _scorer = ConfidenceScorer::new();
let pattern = ExtractablePattern::AccumulationLoop {
iterator_binding: "item".to_string(),
accumulator: "sum".to_string(),
operation: super::super::AccumulationOp::Sum,
filter: None,
transform: None,
start_line: 10,
end_line: 20,
};
let matched = MatchedPattern {
pattern,
confidence: 0.0, context: AnalysisContext {
function_name: "test_func".to_string(),
file_path: "test.rs".to_string(),
language: "rust".to_string(),
complexity_before: 5,
has_side_effects: false,
data_dependencies: vec![],
},
};
let score = ConfidenceScorer::score_pattern(&matched, &matched.context);
assert!(score > 0.8); }
#[test]
fn test_confidence_factors() {
let factors = ConfidenceFactors {
has_clear_boundaries: true,
no_external_state: true,
pure_computation: true,
single_responsibility: true,
testable_in_isolation: true,
};
let confidence = factors.calculate_confidence();
assert_eq!(confidence, 1.0); }
}