use serde::Serialize;
use super::analyzer::FileCoupling;
use crate::report_helpers;
pub fn print_report(pairs: &[FileCoupling], total: usize) {
if pairs.is_empty() {
println!("No coupled file pairs found.");
return;
}
let max_a_len = report_helpers::max_path_width(pairs.iter().map(|p| p.file_a.as_path()), 6);
let max_b_len = report_helpers::max_path_width(pairs.iter().map(|p| p.file_b.as_path()), 6);
let header_width = max_a_len + max_b_len + 35;
let separator = report_helpers::separator(header_width.max(78));
println!("Temporal Coupling — Files That Change Together");
println!("{separator}");
println!(
" {:<aw$} {:<bw$} {:>6} {:>8} {:>8}",
"File A",
"File B",
"Shared",
"Strength",
"Level",
aw = max_a_len,
bw = max_b_len,
);
println!("{separator}");
for p in pairs {
println!(
" {:<aw$} {:<bw$} {:>6} {:>8.2} {:>8}",
p.file_a.display(),
p.file_b.display(),
p.shared_commits,
p.strength,
p.level.label(),
aw = max_a_len,
bw = max_b_len,
);
}
println!("{separator}");
if total > pairs.len() {
println!();
println!(
"{total} coupled pairs found ({shown} shown).",
shown = pairs.len()
);
}
}
#[derive(Serialize)]
struct JsonEntry {
file_a: String,
file_b: String,
shared_commits: usize,
commits_a: usize,
commits_b: usize,
strength: f64,
level: String,
}
pub fn print_json(pairs: &[FileCoupling]) -> Result<(), Box<dyn std::error::Error>> {
let entries: Vec<JsonEntry> = pairs
.iter()
.map(|p| JsonEntry {
file_a: p.file_a.display().to_string(),
file_b: p.file_b.display().to_string(),
shared_commits: p.shared_commits,
commits_a: p.commits_a,
commits_b: p.commits_b,
strength: (p.strength * 100.0).round() / 100.0,
level: p.level.label().to_string(),
})
.collect();
report_helpers::print_json_stdout(&entries)
}
#[cfg(test)]
#[path = "report_test.rs"]
mod tests;