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