acme_disk_use/
lib.rs

1//! Disk usage analyzer with intelligent caching
2//!
3//! This library provides fast disk usage calculation with caching support,
4//! designed for applications that work with mostly immutable files.
5
6mod cache;
7mod disk_use;
8mod error;
9mod scanner;
10pub mod tui;
11
12// Re-export public API
13pub use disk_use::DiskUse;
14pub use error::DiskUseError;
15pub use scanner::DirStat;
16
17use std::{env, path::PathBuf};
18
19/// Format bytes into string with optional human-readable scaling
20///
21/// # Arguments
22/// * `bytes` - The size in bytes to format
23/// * `human_readable` - If true, formats with appropriate scale (B, KB, MB, GB, TB)
24///   If false, returns raw bytes with "bytes" suffix
25///
26/// # Examples
27/// ```
28/// use acme_disk_use::format_size;
29///
30/// assert_eq!(format_size(1024, true), "1.00 KB");
31/// assert_eq!(format_size(1024, false), "1024 bytes");
32/// ```
33pub fn format_size(bytes: u64, human_readable: bool) -> String {
34    if !human_readable {
35        return format!("{} bytes", bytes);
36    }
37
38    const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
39    const THRESHOLD: f64 = 1024.0;
40
41    if bytes == 0 {
42        return "0 B".to_string();
43    }
44
45    let mut size = bytes as f64;
46    let mut unit_index = 0;
47
48    while size >= THRESHOLD && unit_index < UNITS.len() - 1 {
49        size /= THRESHOLD;
50        unit_index += 1;
51    }
52
53    if unit_index == 0 {
54        format!("{} {}", bytes, UNITS[unit_index])
55    } else {
56        format!("{:.2} {}", size, UNITS[unit_index])
57    }
58}
59
60/// Get default cache directory path
61///
62/// Checks the `ACME_DISK_USE_CACHE` environment variable first,
63/// then falls back to `~/.cache/acme-disk-use/cache.bin` on Unix systems.
64pub fn get_default_cache_path() -> PathBuf {
65    if let Ok(cache_dir) = env::var("ACME_DISK_USE_CACHE") {
66        let mut cache_dir = PathBuf::from(cache_dir);
67        cache_dir.push("cache.bin");
68        return cache_dir;
69    }
70
71    // Use XDG cache directory on Unix systems
72    if let Ok(home) = env::var("HOME") {
73        let mut cache_dir = PathBuf::from(home);
74        cache_dir.push(".cache");
75        cache_dir.push("acme-disk-use");
76        cache_dir.push("cache.bin");
77        cache_dir
78    } else {
79        // Fallback for systems without HOME
80        let mut cache_dir = PathBuf::from("./.cache/acme-disk-use");
81        std::fs::create_dir_all(&cache_dir).ok(); // Create the directory if it doesn't exist
82        cache_dir.push("cache.bin");
83        cache_dir
84    }
85}
86
87/// Logger module for file-based logging
88pub mod logger;
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_format_size_human_readable() {
96        // This test verifies the `format_size` function with `human_readable = true`.
97        // It checks various byte sizes to ensure they are correctly formatted with
98        // the appropriate units (B, KB, MB, GB, TB) and decimal precision.
99        assert_eq!(format_size(0, true), "0 B");
100        assert_eq!(format_size(512, true), "512 B");
101        assert_eq!(format_size(1024, true), "1.00 KB");
102        assert_eq!(format_size(1536, true), "1.50 KB");
103        assert_eq!(format_size(1024 * 1024, true), "1.00 MB");
104        assert_eq!(format_size(1024 * 1024 * 1024, true), "1.00 GB");
105        assert_eq!(format_size(1024_u64.pow(4), true), "1.00 TB");
106
107        // Test non-human-readable format
108        // This verifies that when `human_readable = false`, the function returns
109        // the raw byte count followed by "bytes".
110        assert_eq!(format_size(0, false), "0 bytes");
111        assert_eq!(format_size(1024, false), "1024 bytes");
112        assert_eq!(format_size(1234567, false), "1234567 bytes");
113    }
114
115    #[test]
116    fn test_default_cache_path() {
117        // This test verifies that `get_default_cache_path` returns a valid path
118        // that ends with "cache.bin". It checks the default behavior for cache
119        // file location resolution.
120        let default_path = get_default_cache_path();
121
122        // Should end with cache.bin
123        assert!(default_path.to_string_lossy().ends_with("cache.bin"));
124
125        // Should be different from just "cache.bin" unless we're in fallback mode
126        // This test is environment-dependent, so we just check it's a valid path
127        assert!(default_path.is_absolute() || default_path.ends_with("cache.bin"));
128    }
129}