oxihuman_core/
directory_scanner.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct DirEntry {
10 pub path: String,
11 pub is_dir: bool,
12 pub size_bytes: u64,
13}
14
15impl DirEntry {
16 pub fn file(path: &str, size_bytes: u64) -> Self {
17 DirEntry {
18 path: path.to_string(),
19 is_dir: false,
20 size_bytes,
21 }
22 }
23
24 pub fn dir(path: &str) -> Self {
25 DirEntry {
26 path: path.to_string(),
27 is_dir: true,
28 size_bytes: 0,
29 }
30 }
31}
32
33#[derive(Debug, Clone)]
35pub struct ScanConfig {
36 pub max_depth: usize,
37 pub follow_symlinks: bool,
38 pub include_hidden: bool,
39}
40
41impl Default for ScanConfig {
42 fn default() -> Self {
43 ScanConfig {
44 max_depth: 64,
45 follow_symlinks: false,
46 include_hidden: false,
47 }
48 }
49}
50
51pub struct DirectoryScanner {
53 pub config: ScanConfig,
54 virtual_tree: Vec<DirEntry>,
55}
56
57impl DirectoryScanner {
58 pub fn new(config: ScanConfig) -> Self {
59 DirectoryScanner {
60 config,
61 virtual_tree: Vec::new(),
62 }
63 }
64
65 pub fn add_entry(&mut self, entry: DirEntry) {
66 self.virtual_tree.push(entry);
67 }
68
69 pub fn scan(&self, _root: &str) -> Vec<DirEntry> {
70 self.virtual_tree.clone()
71 }
72
73 pub fn file_count(&self) -> usize {
74 self.virtual_tree.iter().filter(|e| !e.is_dir).count()
75 }
76
77 pub fn dir_count(&self) -> usize {
78 self.virtual_tree.iter().filter(|e| e.is_dir).count()
79 }
80}
81
82impl Default for DirectoryScanner {
83 fn default() -> Self {
84 Self::new(ScanConfig::default())
85 }
86}
87
88pub fn new_scanner() -> DirectoryScanner {
90 DirectoryScanner::default()
91}
92
93pub fn filter_by_ext<'a>(entries: &'a [DirEntry], ext: &str) -> Vec<&'a DirEntry> {
95 entries.iter().filter(|e| e.path.ends_with(ext)).collect()
96}
97
98pub fn total_size(entries: &[DirEntry]) -> u64 {
100 entries.iter().map(|e| e.size_bytes).sum()
101}
102
103pub fn find_by_name<'a>(entries: &'a [DirEntry], needle: &str) -> Vec<&'a DirEntry> {
105 entries.iter().filter(|e| e.path.contains(needle)).collect()
106}
107
108pub fn sort_entries(entries: &mut [DirEntry]) {
110 entries.sort_by(|a, b| a.path.cmp(&b.path));
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_new_scanner_empty() {
119 let s = new_scanner();
120 assert_eq!(s.file_count(), 0);
121 assert_eq!(s.dir_count(), 0);
122 }
123
124 #[test]
125 fn test_add_and_scan() {
126 let mut s = new_scanner();
127 s.add_entry(DirEntry::file("/tmp/a.txt", 100));
128 let entries = s.scan("/tmp");
129 assert_eq!(entries.len(), 1);
130 }
131
132 #[test]
133 fn test_file_dir_count() {
134 let mut s = new_scanner();
135 s.add_entry(DirEntry::file("/tmp/x", 50));
136 s.add_entry(DirEntry::dir("/tmp/subdir"));
137 assert_eq!(s.file_count(), 1);
138 assert_eq!(s.dir_count(), 1);
139 }
140
141 #[test]
142 fn test_filter_by_ext() {
143 let entries = vec![DirEntry::file("/a.rs", 10), DirEntry::file("/b.txt", 20)];
144 let rs = filter_by_ext(&entries, ".rs");
145 assert_eq!(rs.len(), 1);
146 }
147
148 #[test]
149 fn test_total_size() {
150 let entries = vec![DirEntry::file("/a", 100), DirEntry::file("/b", 200)];
151 assert_eq!(total_size(&entries), 300);
152 }
153
154 #[test]
155 fn test_find_by_name() {
156 let entries = vec![
157 DirEntry::file("/foo/bar.rs", 10),
158 DirEntry::file("/baz/qux.rs", 20),
159 ];
160 let found = find_by_name(&entries, "foo");
161 assert_eq!(found.len(), 1);
162 }
163
164 #[test]
165 fn test_sort_entries() {
166 let mut entries = vec![DirEntry::file("/z.txt", 1), DirEntry::file("/a.txt", 1)];
167 sort_entries(&mut entries);
168 assert_eq!(entries[0].path, "/a.txt");
169 }
170
171 #[test]
172 fn test_scan_config_default() {
173 let cfg = ScanConfig::default();
174 assert!(!cfg.follow_symlinks);
175 assert_eq!(cfg.max_depth, 64);
176 }
177
178 #[test]
179 fn test_dir_entry_is_dir_flag() {
180 let d = DirEntry::dir("/mydir");
181 assert!(d.is_dir);
182 let f = DirEntry::file("/myfile", 0);
183 assert!(!f.is_dir);
184 }
185
186 #[test]
187 fn test_total_size_dirs_excluded() {
188 let entries = vec![DirEntry::dir("/subdir"), DirEntry::file("/file", 77)];
189 assert_eq!(total_size(&entries), 77);
191 }
192}