Skip to main content

diskard_core/
size.rs

1use std::path::Path;
2
3/// Format bytes into a human-readable string using binary units (e.g., "1.0 GiB").
4pub fn format_bytes(bytes: u64) -> String {
5    bytesize::ByteSize(bytes).to_string_as(true)
6}
7
8/// Calculate the total size of a directory by walking all files.
9pub fn dir_size(path: &Path) -> u64 {
10    if !path.exists() {
11        return 0;
12    }
13
14    if path.is_file() {
15        return path.metadata().map(|m| m.len()).unwrap_or(0);
16    }
17
18    let resolved = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
19    jwalk::WalkDir::new(resolved)
20        .skip_hidden(false)
21        .into_iter()
22        .filter_map(|entry| entry.ok())
23        .filter(|entry| entry.file_type().is_file())
24        .map(|entry| entry.metadata().map(|m| m.len()).unwrap_or(0))
25        .sum()
26}
27
28/// Return (total_bytes, free_bytes) for the filesystem containing `path`.
29pub fn disk_usage(path: &std::path::Path) -> Option<(u64, u64)> {
30    let total = fs2::total_space(path).ok()?;
31    let free = fs2::free_space(path).ok()?;
32    Some((total, free))
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn test_format_bytes() {
41        assert_eq!(format_bytes(0), "0 B");
42        assert_eq!(format_bytes(1024), "1.0 kiB");
43        assert_eq!(format_bytes(1_073_741_824), "1.0 GiB");
44    }
45
46    #[test]
47    fn test_dir_size_nonexistent() {
48        assert_eq!(dir_size(Path::new("/nonexistent/path")), 0);
49    }
50}