mod scoring;
use super::functions;
#[derive(Debug, Clone, PartialEq)]
pub struct CognitiveComplexity {
pub total: usize,
pub max: usize,
pub avg: f64,
pub function_count: usize,
pub high_complexity_functions: Vec<HighCognitiveFunction>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct HighCognitiveFunction {
pub name: String,
pub line: usize,
pub complexity: usize,
}
impl Default for CognitiveComplexity {
fn default() -> Self {
Self {
total: 0,
max: 0,
avg: 0.0,
function_count: 0,
high_complexity_functions: Vec::new(),
}
}
}
const HIGH_COGNITIVE_THRESHOLD: usize = 15;
pub fn estimate_cognitive_complexity(content: &str, language: &str) -> CognitiveComplexity {
let lang = language.to_lowercase();
let lines: Vec<&str> = content.lines().collect();
if lines.is_empty() {
return CognitiveComplexity::default();
}
let spans = functions::function_spans_for_cognitive_language(&lines, &lang);
if spans.is_empty() {
return CognitiveComplexity::default();
}
let mut complexities: Vec<(String, usize, usize)> = Vec::new();
for span in &spans {
let func_name = functions::extract_function_name(&lines, span.start_line, &lang);
let func_lines: Vec<&str> = lines[span.start_line..=span.end_line].to_vec();
let cc = scoring::calculate_cognitive_complexity(&func_lines, &lang);
complexities.push((func_name, span.start_line + 1, cc)); }
let total: usize = complexities.iter().map(|(_, _, cc)| cc).sum();
let max = complexities.iter().map(|(_, _, cc)| *cc).max().unwrap_or(0);
let function_count = complexities.len();
let avg = if function_count > 0 {
total as f64 / function_count as f64
} else {
0.0
};
let high_complexity_functions: Vec<HighCognitiveFunction> = complexities
.iter()
.filter(|(_, _, cc)| *cc > HIGH_COGNITIVE_THRESHOLD)
.map(|(name, line, cc)| HighCognitiveFunction {
name: name.clone(),
line: *line,
complexity: *cc,
})
.collect();
CognitiveComplexity {
total,
max,
avg,
function_count,
high_complexity_functions,
}
}