use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MILevel {
Green,
Yellow,
Red,
}
impl MILevel {
pub fn from_score(score: f64) -> Self {
if score >= 20.0 {
Self::Green
} else if score >= 10.0 {
Self::Yellow
} else {
Self::Red
}
}
pub fn as_str(self) -> &'static str {
match self {
Self::Green => "green",
Self::Yellow => "yellow",
Self::Red => "red",
}
}
}
impl std::fmt::Display for MILevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct MIMetrics {
pub halstead_volume: f64,
pub cyclomatic_complexity: usize,
pub loc: usize,
pub mi_score: f64,
pub level: MILevel,
}
pub fn compute_mi(volume: f64, complexity: usize, code_lines: usize) -> Option<MIMetrics> {
if code_lines == 0 || volume <= 0.0 || complexity == 0 {
return None;
}
let raw =
171.0 - 5.2 * volume.ln() - 0.23 * complexity as f64 - 16.2 * (code_lines as f64).ln();
let mi_score = f64::max(0.0, raw * 100.0 / 171.0);
Some(MIMetrics {
halstead_volume: volume,
cyclomatic_complexity: complexity,
loc: code_lines,
mi_score,
level: MILevel::from_score(mi_score),
})
}
#[cfg(test)]
#[path = "analyzer_test.rs"]
mod tests;