1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use std::{collections::BTreeMap, io, path::Path};
pub struct FileInfo<'a> {
_path: &'a str,
_name: String,
_real: &'a Path,
}
impl<'a> FileInfo<'a> {
pub fn name(&self) -> &str {
self._name.as_ref()
}
pub fn path(&self) -> String {
win_join(self._path, &self._name)
}
pub fn real(&self) -> &Path {
self._real
}
}
fn win_join(base: &str, name: &str) -> String {
if base.is_empty() {
name.to_string()
} else if base.ends_with('\\') {
format!("{}{}", base, name)
} else {
format!("{}\\{}", base, name)
}
}
pub trait FsVisitor {
fn visit_file(&mut self, info: FileInfo);
fn failed_read_dir(&mut self, real: &Path, e: io::Error) {
log::error!("Failed to read_dir {}: {}", real.display(), e);
}
fn failed_next_dir_entry(&mut self, real: &Path, e: io::Error) {
log::error!("Failed next dir entry {}: {}", real.display(), e);
}
}
pub fn scan_dir<V: FsVisitor>(visitor: &mut V, path: String, real: &Path, recurse: bool) {
let rd = match std::fs::read_dir(&real) {
Ok(rd) => rd,
Err(e) => {
visitor.failed_read_dir(real, e);
return;
}
};
let mut entries = BTreeMap::new();
for e in rd {
match e {
Ok(e) => {
let new_real_path = e.path();
let name = new_real_path
.file_name()
.unwrap()
.to_string_lossy()
.into_owned();
entries.insert(name, (new_real_path, e));
}
Err(e) => {
visitor.failed_next_dir_entry(real, e);
return;
}
};
}
for (name, (new_real_path, e)) in entries {
let t = e.file_type().unwrap();
if t.is_file() {
visitor.visit_file(FileInfo {
_path: &path,
_name: name,
_real: &new_real_path,
});
} else if t.is_dir() && recurse {
let new_path = win_join(&path, &name);
scan_dir(visitor, new_path, &new_real_path, recurse);
}
}
}