Skip to main content

st/formatters/
stats.rs

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        // File types by count
54        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        // Largest files
66        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        // Newest files
83        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        // Oldest files
99        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}