mod analyzer;
mod report;
mod rules;
use std::cmp::Reverse;
use std::error::Error;
use std::path::{Path, PathBuf};
use crate::cli::OutputMode;
use crate::cycom::markers::markers_for;
use crate::loc::language::{LanguageSpec, detect};
use crate::util::read_and_classify;
use crate::walk::WalkConfig;
use analyzer::detect_smells;
use report::{
FileSmellMetrics, print_codeclimate, print_github, print_json, print_report, print_short,
print_terse,
};
fn analyze_file(
path: &Path,
spec: &LanguageSpec,
max_lines: usize,
max_params: usize,
) -> Result<Option<FileSmellMetrics>, Box<dyn Error>> {
let markers = match markers_for(spec.name) {
Some(m) => m,
None => return Ok(None),
};
let (lines, kinds) = match read_and_classify(path, spec)? {
Some(v) => v,
None => return Ok(None),
};
let smells = match detect_smells(&lines, &kinds, markers, max_lines, max_params) {
Some(s) => s,
None => return Ok(None),
};
let total = smells.smells.len();
Ok(Some(FileSmellMetrics {
path: path.to_path_buf(),
language: spec.name.to_string(),
smells,
total,
}))
}
pub fn run_on_files(
paths: &[PathBuf],
output: OutputMode,
top: usize,
max_lines: usize,
max_params: usize,
) -> Result<(), Box<dyn Error>> {
let mut results: Vec<FileSmellMetrics> = Vec::new();
for path in paths {
let spec = match detect(path) {
Some(s) => s,
None => continue,
};
match analyze_file(path, spec, max_lines, max_params) {
Ok(Some(m)) => results.push(m),
Ok(None) => {}
Err(e) => eprintln!("warning: {}: {e}", path.display()),
}
}
if results.is_empty() {
if output == OutputMode::Json {
return report::print_json(&[]);
}
println!("No recognized source files in the provided list.");
return Ok(());
}
results.sort_by_key(|r| Reverse(r.total));
results.truncate(top);
dispatch_output(output, &results)
}
pub fn run(
cfg: &WalkConfig<'_>,
output: OutputMode,
top: usize,
max_lines: usize,
max_params: usize,
) -> Result<(), Box<dyn Error>> {
let mut results =
cfg.collect_analysis(|path, spec| analyze_file(path, spec, max_lines, max_params));
results.sort_by_key(|r| Reverse(r.total));
results.truncate(top);
dispatch_output(output, &results)
}
fn dispatch_output(output: OutputMode, results: &[FileSmellMetrics]) -> Result<(), Box<dyn Error>> {
match output {
OutputMode::Json => print_json(results)?,
OutputMode::Github => print_github(results),
OutputMode::Codeclimate => print_codeclimate(results)?,
OutputMode::Short => print_short(results),
OutputMode::Terse => print_terse(results),
OutputMode::Table => print_report(results),
}
Ok(())
}
#[cfg(test)]
#[path = "mod_test.rs"]
mod tests;