rusdu 0.1.2

Rust rewrite of ncdu — NCurses Disk Usage analyzer
pub mod filter;
pub mod parallel;
pub mod platform;
pub mod walker;

use crate::tree::TreeArena;
use std::path::Path;

#[derive(Debug, Clone)]
pub struct ScanOptions {
    pub one_file_system: bool,
    pub exclude_patterns: Vec<String>,
    pub exclude_from: Option<std::path::PathBuf>,
    pub exclude_caches: bool,
    pub exclude_kernfs: bool,
    pub follow_symlinks: bool,
    pub threads: usize,
    pub extended: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProgressMode {
    Silent,
    Line,
    Fullscreen,
}

pub fn scan_directory(
    path: &Path,
    opts: ScanOptions,
    progress_mode: ProgressMode,
) -> anyhow::Result<TreeArena> {
    if opts.threads > 1 {
        parallel::scan_parallel(path, opts, progress_mode)
    } else {
        walker::scan_single_threaded(path, opts, progress_mode)
    }
}

#[derive(Default)]
pub struct ScanStats {
    pub items_scanned: u64,
    pub size_scanned: i64,
    pub last_update: Option<std::time::Instant>,
}

pub fn update_progress(current_path: &Path, stats: &mut ScanStats, mode: ProgressMode) {
    if mode == ProgressMode::Silent {
        return;
    }

    let now = std::time::Instant::now();
    if let Some(last) = stats.last_update {
        if now.duration_since(last).as_millis() < 100 {
            return;
        }
    }
    stats.last_update = Some(now);

    let path_str = current_path.to_string_lossy();
    let truncated_path = if path_str.len() > 50 {
        format!("...{}", &path_str[path_str.len() - 47..])
    } else {
        path_str.into_owned()
    };

    if mode == ProgressMode::Line {
        let size_str = crate::format::format_size(stats.size_scanned, false);
        eprint!(
            "\rScanning: {} items [size: {}] | Current: {}",
            stats.items_scanned, size_str, truncated_path
        );
        use std::io::Write;
        let _ = std::io::stderr().flush();
    } else if mode == ProgressMode::Fullscreen {
        use crossterm::{cursor, terminal, QueueableCommand};
        use std::io::Write;
        let mut stderr = std::io::stderr();
        let _ = stderr.queue(cursor::Hide);
        let _ = stderr.queue(terminal::Clear(terminal::ClearType::All));
        let _ = stderr.queue(cursor::MoveTo(0, 0));
        let size_str = crate::format::format_size(stats.size_scanned, false);
        let _ = writeln!(
            stderr,
            "rusdu {} ~ Scanning files...\n\n   Items scanned: {}\n   Total size   : {}\n   Scanning     : {}\n\nPress 'q' to abort.",
            env!("CARGO_PKG_VERSION"),
            stats.items_scanned,
            size_str,
            truncated_path
        );
        let _ = stderr.flush();
    }
}