pub(crate) mod analyzer;
pub(crate) mod report;
use std::cmp::Reverse;
use std::error::Error;
use std::path::Path;
use crate::loc::counter::LineKind;
use crate::loc::language::LanguageSpec;
use crate::report_helpers;
use crate::util::read_and_classify;
use crate::walk::WalkConfig;
use analyzer::compute_mi;
use report::{FileMIMetrics, print_json, print_report, print_short, print_terse};
pub(crate) struct Inputs {
pub volume: f64,
pub complexity: usize,
pub code_lines: usize,
pub comment_lines: usize,
}
pub(crate) fn inputs(
path: &Path,
spec: &LanguageSpec,
exclude_tests: bool,
) -> Result<Option<Inputs>, Box<dyn Error>> {
let Some((lines, kinds)) = read_and_classify(path, spec, exclude_tests)? else {
return Ok(None);
};
let count = |kind| kinds.iter().filter(|k| **k == kind).count();
let volume = crate::hal::analyze_content(&lines, &kinds, spec).map(|h| h.volume);
let complexity =
crate::cycom::analyze_content(&lines, &kinds, spec).map(|c| c.total_complexity);
Ok(volume.zip(complexity).map(|(volume, complexity)| Inputs {
volume,
complexity,
code_lines: count(LineKind::Code),
comment_lines: count(LineKind::Comment),
}))
}
fn analyze_file(
path: &Path,
spec: &LanguageSpec,
exclude_tests: bool,
) -> Result<Option<FileMIMetrics>, Box<dyn Error>> {
let Some(of) = inputs(path, spec, exclude_tests)? else {
return Ok(None);
};
let metrics = compute_mi(of.volume, of.complexity, of.code_lines);
Ok(metrics.map(|metrics| FileMIMetrics {
path: path.to_path_buf(),
language: spec.name.to_string(),
metrics,
}))
}
pub fn run(
cfg: &WalkConfig<'_>,
output: crate::cli::OutputMode,
top: usize,
sort_by: &str,
) -> Result<(), Box<dyn Error>> {
let mut results = cfg.collect_analysis(analyze_file);
match sort_by {
"volume" => results.sort_by(|a, b| {
b.metrics
.halstead_volume
.total_cmp(&a.metrics.halstead_volume)
}),
"complexity" => {
results.sort_by_key(|r| Reverse(r.metrics.cyclomatic_complexity));
}
"loc" => results.sort_by_key(|r| Reverse(r.metrics.loc)),
_ => results.sort_by(|a, b| a.metrics.mi_score.total_cmp(&b.metrics.mi_score)),
}
report_helpers::output_results(
&mut results,
top,
output,
print_json,
print_report,
print_short,
print_terse,
)
}
#[cfg(test)]
#[path = "mod_test.rs"]
mod tests;