1use std::path::Path;
2
3pub fn format_bytes(bytes: u64) -> String {
5 bytesize::ByteSize(bytes).to_string_as(true)
6}
7
8pub 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
28pub 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}