mod scoring;
use super::functions;
#[derive(Debug, Clone, PartialEq)]
pub struct CyclomaticComplexity {
pub total_cc: usize,
pub max_cc: usize,
pub avg_cc: f64,
pub high_complexity_functions: Vec<HighComplexityFunction>,
pub function_count: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub struct HighComplexityFunction {
pub name: String,
pub line: usize,
pub complexity: usize,
}
impl Default for CyclomaticComplexity {
fn default() -> Self {
Self {
total_cc: 0,
max_cc: 0,
avg_cc: 0.0,
high_complexity_functions: Vec::new(),
function_count: 0,
}
}
}
const HIGH_COMPLEXITY_THRESHOLD: usize = 10;
pub fn estimate_cyclomatic_complexity(content: &str, language: &str) -> CyclomaticComplexity {
let lang = language.to_lowercase();
let lines: Vec<&str> = content.lines().collect();
if lines.is_empty() {
return CyclomaticComplexity::default();
}
let spans = functions::function_spans_for_language(&lines, &lang);
if spans.is_empty() {
return CyclomaticComplexity::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_cyclomatic_complexity(&func_lines, &lang);
complexities.push((func_name, span.start_line + 1, cc)); }
let total_cc: usize = complexities.iter().map(|(_, _, cc)| cc).sum();
let max_cc = complexities.iter().map(|(_, _, cc)| *cc).max().unwrap_or(0);
let function_count = complexities.len();
let avg_cc = if function_count > 0 {
total_cc as f64 / function_count as f64
} else {
0.0
};
let high_complexity_functions: Vec<HighComplexityFunction> = complexities
.iter()
.filter(|(_, _, cc)| *cc > HIGH_COMPLEXITY_THRESHOLD)
.map(|(name, line, cc)| HighComplexityFunction {
name: name.clone(),
line: *line,
complexity: *cc,
})
.collect();
CyclomaticComplexity {
total_cc,
max_cc,
avg_cc,
high_complexity_functions,
function_count,
}
}