1use super::Formatter;
2use crate::scanner::{FileNode, TreeStats};
3use anyhow::Result;
4use chrono::{DateTime, Local};
5use humansize::{format_size, BINARY};
6use std::io::Write;
7use std::path::Path;
8
9pub struct StatsFormatter;
10
11impl Default for StatsFormatter {
12 fn default() -> Self {
13 Self::new()
14 }
15}
16
17impl StatsFormatter {
18 pub fn new() -> Self {
19 Self
20 }
21}
22
23impl Formatter for StatsFormatter {
24 fn format(
25 &self,
26 writer: &mut dyn Write,
27 _nodes: &[FileNode],
28 stats: &TreeStats,
29 root_path: &Path,
30 ) -> Result<()> {
31 writeln!(writer, "{}", "=".repeat(60))?;
32 writeln!(writer, "Directory Statistics for: {}", root_path.display())?;
33 writeln!(writer, "{}", "=".repeat(60))?;
34 writeln!(
35 writer,
36 "Total Files: {} ({:x} hex)",
37 stats.total_files, stats.total_files
38 )?;
39 writeln!(
40 writer,
41 "Total Directories: {} ({:x} hex)",
42 stats.total_dirs, stats.total_dirs
43 )?;
44 writeln!(
45 writer,
46 "Total Size: {} bytes ({:x} hex) ({})",
47 stats.total_size,
48 stats.total_size,
49 format_size(stats.total_size, BINARY)
50 )?;
51 writeln!(writer)?;
52
53 if !stats.file_types.is_empty() {
55 writeln!(writer, "File Types (by count):")?;
56 let mut types: Vec<_> = stats.file_types.iter().collect();
57 types.sort_by(|a, b| b.1.cmp(a.1));
58
59 for (ext, count) in types.iter().take(20) {
60 writeln!(writer, " .{}: {}", ext, count)?;
61 }
62 writeln!(writer)?;
63 }
64
65 if !stats.largest_files.is_empty() {
67 writeln!(writer, "Largest Files:")?;
68 for (size, path) in stats.largest_files.iter().take(10) {
69 let rel_path = path.strip_prefix(root_path).unwrap_or(path);
70 writeln!(
71 writer,
72 " {:>12} bytes ({:>10x} hex) {:>8} {}",
73 size,
74 size,
75 format_size(*size, BINARY),
76 rel_path.display()
77 )?;
78 }
79 writeln!(writer)?;
80 }
81
82 if !stats.newest_files.is_empty() {
84 writeln!(writer, "Newest Files:")?;
85 for (mtime, path) in stats.newest_files.iter().take(5) {
86 let datetime = DateTime::<Local>::from(*mtime);
87 let rel_path = path.strip_prefix(root_path).unwrap_or(path);
88 writeln!(
89 writer,
90 " {} {}",
91 datetime.format("%Y-%m-%d %H:%M"),
92 rel_path.display()
93 )?;
94 }
95 writeln!(writer)?;
96 }
97
98 if !stats.oldest_files.is_empty() {
100 writeln!(writer, "Oldest Files:")?;
101 for (mtime, path) in stats.oldest_files.iter().take(5) {
102 let datetime = DateTime::<Local>::from(*mtime);
103 let rel_path = path.strip_prefix(root_path).unwrap_or(path);
104 writeln!(
105 writer,
106 " {} {}",
107 datetime.format("%Y-%m-%d %H:%M"),
108 rel_path.display()
109 )?;
110 }
111 }
112
113 Ok(())
114 }
115}