rust_filesearch/fs/
size.rs

1use crate::models::{Entry, EntryKind};
2use std::collections::HashMap;
3use std::path::PathBuf;
4
5/// Compute directory sizes by aggregating file sizes
6pub fn compute_dir_sizes(entries: &[Entry]) -> HashMap<PathBuf, u64> {
7    let mut sizes: HashMap<PathBuf, u64> = HashMap::new();
8
9    // First, collect all file sizes
10    for entry in entries {
11        if entry.kind == EntryKind::File {
12            // Add size to file's own path
13            sizes.insert(entry.path.clone(), entry.size);
14
15            // Add size to all parent directories
16            let mut current = entry.path.parent();
17            while let Some(parent) = current {
18                *sizes.entry(parent.to_path_buf()).or_insert(0) += entry.size;
19                current = parent.parent();
20            }
21        }
22    }
23
24    sizes
25}
26
27/// Update entries with computed directory sizes
28pub fn update_entries_with_dir_sizes(entries: &mut [Entry], dir_sizes: &HashMap<PathBuf, u64>) {
29    for entry in entries.iter_mut() {
30        if entry.kind == EntryKind::Dir {
31            if let Some(&size) = dir_sizes.get(&entry.path) {
32                entry.size = size;
33            }
34        }
35    }
36}
37
38/// Get top N entries by size
39pub fn get_top_by_size(entries: &[Entry], n: usize) -> Vec<Entry> {
40    let mut sorted = entries.to_vec();
41    sorted.sort_by(|a, b| b.size.cmp(&a.size));
42    sorted.into_iter().take(n).collect()
43}
44
45/// Compute total size of all entries
46pub fn compute_total_size(entries: &[Entry]) -> u64 {
47    entries.iter().map(|e| e.size).sum()
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use std::path::PathBuf;
54
55    fn make_entry(path: &str, size: u64, kind: EntryKind) -> Entry {
56        use chrono::Utc;
57
58        let path_buf = PathBuf::from(path);
59        let name = path_buf
60            .file_name()
61            .and_then(|n| n.to_str())
62            .unwrap_or("")
63            .to_string();
64
65        Entry {
66            path: path_buf,
67            name,
68            size,
69            kind,
70            mtime: Utc::now(),
71            perms: None,
72            owner: None,
73            depth: 0,
74        }
75    }
76
77    #[test]
78    fn test_compute_dir_sizes() {
79        use std::path::Path;
80
81        let entries = vec![
82            make_entry("/root", 0, EntryKind::Dir),
83            make_entry("/root/file1.txt", 100, EntryKind::File),
84            make_entry("/root/file2.txt", 200, EntryKind::File),
85            make_entry("/root/subdir", 0, EntryKind::Dir),
86            make_entry("/root/subdir/file3.txt", 50, EntryKind::File),
87        ];
88
89        let sizes = compute_dir_sizes(&entries);
90
91        assert_eq!(sizes.get(Path::new("/root")), Some(&350));
92        assert_eq!(sizes.get(Path::new("/root/subdir")), Some(&50));
93    }
94
95    #[test]
96    fn test_update_entries_with_dir_sizes() {
97        let mut entries = vec![
98            make_entry("/root", 0, EntryKind::Dir),
99            make_entry("/root/file.txt", 100, EntryKind::File),
100        ];
101
102        let sizes = compute_dir_sizes(&entries);
103        update_entries_with_dir_sizes(&mut entries, &sizes);
104
105        assert_eq!(entries[0].size, 100); // Directory size updated
106        assert_eq!(entries[1].size, 100); // File size unchanged
107    }
108
109    #[test]
110    fn test_get_top_by_size() {
111        let entries = vec![
112            make_entry("small.txt", 10, EntryKind::File),
113            make_entry("large.txt", 1000, EntryKind::File),
114            make_entry("medium.txt", 100, EntryKind::File),
115        ];
116
117        let top = get_top_by_size(&entries, 2);
118        assert_eq!(top.len(), 2);
119        assert_eq!(top[0].size, 1000);
120        assert_eq!(top[1].size, 100);
121    }
122
123    #[test]
124    fn test_compute_total_size() {
125        let entries = vec![
126            make_entry("file1.txt", 100, EntryKind::File),
127            make_entry("file2.txt", 200, EntryKind::File),
128            make_entry("file3.txt", 50, EntryKind::File),
129        ];
130
131        assert_eq!(compute_total_size(&entries), 350);
132    }
133}