use std::collections::HashMap;
use std::io::{self, Write};
use super::filter::Filter;
use super::format::format_size;
use super::OutputConfig;
use crate::category::Category;
use crate::entry::Entry;
struct CategoryStats {
size: u64,
count: u64,
}
pub fn write(entry: &Entry, config: &OutputConfig, out: &mut impl Write) -> io::Result<()> {
let mut stats: HashMap<Category, CategoryStats> = HashMap::new();
collect_stats(entry, config.filter, &mut stats);
let total: u64 = stats.values().map(|s| s.size).sum();
writeln!(out, "Total: {}", format_size(total))?;
writeln!(out)?;
writeln!(out, "Category Summary:")?;
let mut sorted: Vec<_> = stats.into_iter().collect();
sorted.sort_by(|a, b| {
b.1.size
.cmp(&a.1.size)
.then_with(|| a.0.label().cmp(b.0.label()))
});
for (category, stat) in &sorted {
let pct = (stat.size * 100).checked_div(total).unwrap_or(0);
writeln!(
out,
" {:<8} {:>10} {:>3}% {} items",
category.label(),
format_size(stat.size),
pct,
stat.count,
)?;
}
Ok(())
}
fn collect_stats(entry: &Entry, filter: &Filter, stats: &mut HashMap<Category, CategoryStats>) {
let matches = filter.is_empty() || filter.matches(entry);
if !entry.is_dir() {
if matches {
let stat = stats
.entry(entry.category)
.or_insert(CategoryStats { size: 0, count: 0 });
stat.size += entry.size;
stat.count += 1;
}
return;
}
if entry.category != Category::Other && matches {
let stat = stats
.entry(entry.category)
.or_insert(CategoryStats { size: 0, count: 0 });
stat.size += entry.size;
stat.count += 1;
return;
}
if let Some(children) = entry.children() {
for child in children {
collect_stats(child, filter, stats);
}
}
}