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
|
use crate::{Sorting, WalkOptions, WalkResult};
use failure::Error;
use std::borrow::Cow;
use std::{io, path::Path};
/// Aggregate the given `paths` and write information about them to `out` in a human-readable format.
/// If `compute_total` is set, it will write an additional line with the total size across all given `paths`.
/// If `sort_by_size_in_bytes` is set, we will sort all sizes (ascending) before outputting them.
pub fn aggregate(
mut out: impl io::Write,
options: WalkOptions,
compute_total: bool,
sort_by_size_in_bytes: bool,
paths: impl IntoIterator<Item = impl AsRef<Path>>,
) -> Result<WalkResult, Error> {
let mut res = WalkResult::default();
res.stats.smallest_file_in_bytes = u64::max_value();
let mut total = 0;
let mut num_roots = 0;
let mut aggregates = Vec::new();
for path in paths.into_iter() {
num_roots += 1;
let mut num_bytes = 0u64;
let mut num_errors = 0u64;
for entry in options.iter_from_path(path.as_ref(), Sorting::None) {
res.stats.files_traversed += 1;
match entry {
Ok(entry) => {
let file_size = match entry.metadata {
Some(Ok(ref m)) if !m.is_dir() => m.len(),
Some(Ok(_)) => 0,
Some(Err(_)) => {
num_errors += 1;
0
}
None => unreachable!(
"we ask for metadata, so we at least have Some(Err(..))). Issue in jwalk?"
),
};
res.stats.largest_file_in_bytes =
res.stats.largest_file_in_bytes.max(file_size);
res.stats.smallest_file_in_bytes =
res.stats.smallest_file_in_bytes.min(file_size);
num_bytes += file_size;
}
Err(_) => num_errors += 1,
}
}
if sort_by_size_in_bytes {
aggregates.push((path.as_ref().to_owned(), num_bytes, num_errors));
} else {
write_path(&mut out, &options, path, num_bytes, num_errors)?;
}
total += num_bytes;
res.num_errors += num_errors;
}
if res.stats.files_traversed == 0 {
res.stats.smallest_file_in_bytes = 0;
}
if sort_by_size_in_bytes {
aggregates.sort_by_key(|&(_, num_bytes, _)| num_bytes);
for (path, num_bytes, num_errors) in aggregates.into_iter() {
write_path(&mut out, &options, path, num_bytes, num_errors)?;
}
}
if num_roots > 1 && compute_total {
write_path(
&mut out,
&options,
Path::new("total"),
total,
res.num_errors,
)?;
}
Ok(res)
}
fn write_path(
out: &mut impl io::Write,
options: &WalkOptions,
path: impl AsRef<Path>,
num_bytes: u64,
num_errors: u64,
) -> Result<(), io::Error> {
use termion::color;
writeln!(
out,
"{}{:>10}{}\t{}{}",
options.color.display(color::Fg(color::Green)),
options.format_bytes(num_bytes),
options.color.display(color::Fg(color::Reset)),
path.as_ref().display(),
if num_errors == 0 {
Cow::Borrowed("")
} else {
Cow::Owned(format!("\t<{} IO Error(s)>", num_errors))
}
)
}
|