smart-tree 8.0.1

Smart Tree - An intelligent, AI-friendly directory visualization tool
Documentation
use super::Formatter;
use crate::scanner::{FileNode, TreeStats};
use anyhow::Result;
use chrono::{DateTime, Local};
use humansize::{format_size, BINARY};
use std::io::Write;
use std::path::Path;

pub struct StatsFormatter;

impl Default for StatsFormatter {
    fn default() -> Self {
        Self::new()
    }
}

impl StatsFormatter {
    pub fn new() -> Self {
        Self
    }
}

impl Formatter for StatsFormatter {
    fn format(
        &self,
        writer: &mut dyn Write,
        _nodes: &[FileNode],
        stats: &TreeStats,
        root_path: &Path,
    ) -> Result<()> {
        writeln!(writer, "{}", "=".repeat(60))?;
        writeln!(writer, "Directory Statistics for: {}", root_path.display())?;
        writeln!(writer, "{}", "=".repeat(60))?;
        writeln!(
            writer,
            "Total Files: {} ({:x} hex)",
            stats.total_files, stats.total_files
        )?;
        writeln!(
            writer,
            "Total Directories: {} ({:x} hex)",
            stats.total_dirs, stats.total_dirs
        )?;
        writeln!(
            writer,
            "Total Size: {} bytes ({:x} hex) ({})",
            stats.total_size,
            stats.total_size,
            format_size(stats.total_size, BINARY)
        )?;
        writeln!(writer)?;

        // File types by count
        if !stats.file_types.is_empty() {
            writeln!(writer, "File Types (by count):")?;
            let mut types: Vec<_> = stats.file_types.iter().collect();
            types.sort_by(|a, b| b.1.cmp(a.1));

            for (ext, count) in types.iter().take(20) {
                writeln!(writer, "  .{}: {}", ext, count)?;
            }
            writeln!(writer)?;
        }

        // Largest files
        if !stats.largest_files.is_empty() {
            writeln!(writer, "Largest Files:")?;
            for (size, path) in stats.largest_files.iter().take(10) {
                let rel_path = path.strip_prefix(root_path).unwrap_or(path);
                writeln!(
                    writer,
                    "  {:>12} bytes ({:>10x} hex) {:>8}  {}",
                    size,
                    size,
                    format_size(*size, BINARY),
                    rel_path.display()
                )?;
            }
            writeln!(writer)?;
        }

        // Newest files
        if !stats.newest_files.is_empty() {
            writeln!(writer, "Newest Files:")?;
            for (mtime, path) in stats.newest_files.iter().take(5) {
                let datetime = DateTime::<Local>::from(*mtime);
                let rel_path = path.strip_prefix(root_path).unwrap_or(path);
                writeln!(
                    writer,
                    "  {}  {}",
                    datetime.format("%Y-%m-%d %H:%M"),
                    rel_path.display()
                )?;
            }
            writeln!(writer)?;
        }

        // Oldest files
        if !stats.oldest_files.is_empty() {
            writeln!(writer, "Oldest Files:")?;
            for (mtime, path) in stats.oldest_files.iter().take(5) {
                let datetime = DateTime::<Local>::from(*mtime);
                let rel_path = path.strip_prefix(root_path).unwrap_or(path);
                writeln!(
                    writer,
                    "  {}  {}",
                    datetime.format("%Y-%m-%d %H:%M"),
                    rel_path.display()
                )?;
            }
        }

        Ok(())
    }
}