use serde::Serialize;
use crate::loc::counter::LineKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ComplexityLevel {
Low,
Moderate,
High,
VeryHigh,
}
impl ComplexityLevel {
pub fn from_stddev(stddev: f64) -> Self {
if stddev < 1.0 {
Self::Low
} else if stddev < 1.5 {
Self::Moderate
} else if stddev < 2.0 {
Self::High
} else {
Self::VeryHigh
}
}
pub fn as_str(self) -> &'static str {
match self {
Self::Low => "Low",
Self::Moderate => "Moderate",
Self::High => "High",
Self::VeryHigh => "Very High",
}
}
}
#[derive(Debug, Clone)]
pub struct IndentMetrics {
pub code_lines: usize,
pub stddev: f64,
pub max_depth: usize,
pub total_indent: usize,
pub complexity: ComplexityLevel,
}
pub fn indent_depth(line: &str, tab_width: usize) -> usize {
let mut spaces = 0;
for ch in line.chars() {
match ch {
' ' => spaces += 1,
'\t' => spaces += tab_width,
_ => break,
}
}
spaces / tab_width
}
pub fn analyze(lines: &[String], kinds: &[LineKind], tab_width: usize) -> Option<IndentMetrics> {
let depths: Vec<usize> = lines
.iter()
.zip(kinds)
.filter(|(_, k)| **k == LineKind::Code)
.map(|(line, _)| indent_depth(line, tab_width))
.collect();
if depths.is_empty() {
return None;
}
let max_depth = *depths.iter().max().unwrap();
let total_indent: usize = depths.iter().sum();
let stddev = calculate_stddev(&depths);
let complexity = ComplexityLevel::from_stddev(stddev);
Some(IndentMetrics {
code_lines: depths.len(),
stddev,
max_depth,
total_indent,
complexity,
})
}
fn calculate_stddev(values: &[usize]) -> f64 {
let n = values.len() as f64;
if n <= 1.0 {
return 0.0;
}
let mean = values.iter().sum::<usize>() as f64 / n;
let variance = values
.iter()
.map(|&v| (v as f64 - mean).powi(2))
.sum::<f64>()
/ (n - 1.0);
variance.sqrt()
}
#[cfg(test)]
#[path = "analyzer_test.rs"]
mod tests;