use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MILevel {
Good,
Moderate,
Difficult,
}
impl MILevel {
pub fn from_score(score: f64) -> Self {
if score >= 85.0 {
Self::Good
} else if score >= 65.0 {
Self::Moderate
} else {
Self::Difficult
}
}
pub fn as_str(self) -> &'static str {
match self {
Self::Good => "good",
Self::Moderate => "moderate",
Self::Difficult => "difficult",
}
}
}
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 comment_lines: usize,
pub comment_percent: f64,
pub mi_woc: f64,
pub mi_cw: f64,
pub mi_score: f64,
pub level: MILevel,
}
pub fn compute_mi(
volume: f64,
complexity: usize,
code_lines: usize,
comment_lines: usize,
) -> Option<MIMetrics> {
if code_lines == 0 || volume <= 0.0 || complexity == 0 {
return None;
}
let total_lines = code_lines + comment_lines;
let comment_percent = comment_lines as f64 / total_lines as f64 * 100.0;
let mi_woc =
171.0 - 5.2 * volume.ln() - 0.23 * complexity as f64 - 16.2 * (code_lines as f64).ln();
let mi_cw = 50.0 * (2.46 * comment_percent.to_radians()).sqrt().sin();
let mi_score = mi_woc + mi_cw;
Some(MIMetrics {
halstead_volume: volume,
cyclomatic_complexity: complexity,
loc: code_lines,
comment_lines,
comment_percent,
mi_woc,
mi_cw,
mi_score,
level: MILevel::from_score(mi_score),
})
}
#[cfg(test)]
#[path = "analyzer_test.rs"]
mod tests;