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