use std::collections::HashMap;
use std::path::{Path, PathBuf};
use walkdir::{DirEntry, WalkDir};
pub fn is_file(entry: &DirEntry) -> bool {
entry.file_type().is_file()
}
pub fn compute_dir_sizes(base: &Path) -> HashMap<PathBuf, u64> {
let mut sizes: HashMap<PathBuf, u64> = HashMap::new();
for entry in WalkDir::new(base)
.follow_links(false)
.into_iter()
.filter_map(Result::ok)
.filter(is_file)
{
if let Ok(metadata) = entry.metadata() {
let file_size = metadata.len();
let mut current = entry.path();
while let Some(parent) = current.parent() {
if !parent.starts_with(base) {
break;
}
*sizes.entry(parent.to_path_buf()).or_default() += file_size;
current = parent;
}
}
}
sizes.entry(base.to_path_buf()).or_insert(0);
sizes
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn test_empty_dir() {
let dir = tempdir().unwrap();
let sizes = compute_dir_sizes(dir.path());
assert_eq!(sizes.len(), 1);
assert_eq!(sizes.get(dir.path()), Some(&0));
}
#[test]
fn test_single_file() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test.txt");
std::fs::write(file_path, "hello").unwrap();
let sizes = compute_dir_sizes(dir.path());
assert_eq!(sizes.get(dir.path()), Some(&5));
}
#[test]
fn test_nested_dirs() {
let dir = tempdir().unwrap();
let a = dir.path().join("a");
let b = a.join("b");
fs::create_dir_all(&b).unwrap();
let file_path_1 = a.join("foo.txt");
std::fs::write(file_path_1, "abcd").unwrap();
let file_path_2 = b.join("bar.txt");
std::fs::write(file_path_2, "xyz").unwrap();
let sizes = compute_dir_sizes(dir.path());
assert_eq!(sizes.get(dir.path()), Some(&7));
assert_eq!(sizes.get(&a), Some(&7));
assert_eq!(sizes.get(&b), Some(&3));
}
}