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        assert_eq!(format_size(0, true), "0 B");
94        assert_eq!(format_size(512, true), "512 B");
95        assert_eq!(format_size(1024, true), "1.00 KB");
96        assert_eq!(format_size(1536, true), "1.50 KB");
97        assert_eq!(format_size(1024 * 1024, true), "1.00 MB");
98        assert_eq!(format_size(1024 * 1024 * 1024, true), "1.00 GB");
99        assert_eq!(format_size(1024_u64.pow(4), true), "1.00 TB");
100
101        // Test non-human-readable format
102        assert_eq!(format_size(0, false), "0 bytes");
103        assert_eq!(format_size(1024, false), "1024 bytes");
104        assert_eq!(format_size(1234567, false), "1234567 bytes");
105    }
106
107    #[test]
108    fn test_default_cache_path() {
109        let default_path = get_default_cache_path();
110
111        // Should end with cache.bin
112        assert!(default_path.to_string_lossy().ends_with("cache.bin"));
113
114        // Should be different from just "cache.bin" unless we're in fallback mode
115        // This test is environment-dependent, so we just check it's a valid path
116        assert!(default_path.is_absolute() || default_path.ends_with("cache.bin"));
117    }
118}