pub mod analyzer;
mod report;
use std::collections::HashMap;
use std::error::Error;
use std::path::PathBuf;
use crate::git::GitRepo;
use crate::util::parse_since;
use crate::walk::{self, WalkConfig};
use analyzer::{FileChurn, classify};
use report::{print_json, print_report};
pub fn run(
cfg: &WalkConfig<'_>,
json: bool,
top: usize,
sort_by: &str,
since: Option<&str>,
) -> Result<(), Box<dyn Error>> {
let git = GitRepo::open(cfg.path)
.map_err(|e| format!("not a git repository (or any parent): {e}"))?;
let since_ts = since.map(parse_since).transpose()?;
let freqs = git.file_frequencies(since_ts)?;
if freqs.is_empty() {
if since.is_some() {
eprintln!("No commits found in the specified time range.");
} else {
eprintln!("No commits found in the repository.");
}
return Ok(());
}
let freq_map: HashMap<PathBuf, _> = freqs.into_iter().map(|f| (f.path.clone(), f)).collect();
let (walk_root, walk_prefix) = git.walk_prefix(cfg.path)?;
let mut files: Vec<FileChurn> = walk::source_files(&walk_root, cfg.exclude_tests(), cfg.filter)
.into_iter()
.filter_map(|(file_path, spec)| {
let rel = GitRepo::to_git_path(&walk_root, &walk_prefix, &file_path);
freq_map.get(&rel).map(|freq| {
classify(
rel,
spec.name,
freq.commits,
freq.first_commit,
freq.last_commit,
)
})
})
.collect();
match sort_by {
"rate" => files.sort_by(|a, b| b.rate.partial_cmp(&a.rate).unwrap()),
"file" => files.sort_by(|a, b| a.path.cmp(&b.path)),
_ => files.sort_by(|a, b| b.commits.cmp(&a.commits)),
}
files.truncate(top);
if json {
print_json(&files);
} else {
print_report(&files);
}
Ok(())
}
#[cfg(test)]
#[path = "mod_test.rs"]
mod tests;