Skip to content

Commit a945d7f

Browse files
authored
Merge 93bbea3 into f128a9e
2 parents f128a9e + 93bbea3 commit a945d7f

4 files changed

Lines changed: 94 additions & 50 deletions

File tree

src/fs_tree_builder.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -66,59 +66,55 @@ where
6666
max_depth,
6767
} = builder;
6868

69-
TreeBuilder::<PathBuf, OsStringDisplay, Size, _, _> {
69+
TreeBuilder::<PathBuf, _, Size, _, _> {
7070
name: OsStringDisplay::os_string_from(&root),
7171

7272
path: root,
7373

7474
get_info: |path| {
75+
let make_info = |size: Size, children: Option<_>| Info {
76+
size,
77+
children: children.into_iter().flatten(),
78+
};
79+
80+
let empty_info = || make_info(Size::default(), None);
81+
7582
let stats = match symlink_metadata(path) {
7683
Err(error) => {
7784
reporter.report(Event::EncounterError(ErrorReport {
7885
operation: SymlinkMetadata,
7986
path,
8087
error,
8188
}));
82-
return Info {
83-
size: Size::default(),
84-
children: Vec::new(),
85-
};
89+
return empty_info();
8690
}
8791
Ok(stats) => stats,
8892
};
8993

90-
let children: Vec<_> = if stats.is_dir() {
94+
let children = if stats.is_dir() {
9195
match read_dir(path) {
9296
Err(error) => {
9397
reporter.report(Event::EncounterError(ErrorReport {
9498
operation: ReadDirectory,
9599
path,
96100
error,
97101
}));
98-
return Info::default();
102+
return empty_info();
99103
}
100104
Ok(entries) => entries,
101105
}
102-
.filter_map(|entry| match entry {
103-
Err(error) => {
104-
reporter.report(Event::EncounterError(ErrorReport {
105-
operation: AccessEntry,
106-
path,
107-
error,
108-
}));
109-
None
110-
}
111-
Ok(entry) => entry.file_name().pipe(OsStringDisplay::from).pipe(Some),
112-
})
113-
.collect()
106+
.flatten()
107+
.map(|entry| entry.file_name())
108+
.map(OsStringDisplay::from)
109+
.pipe(Some)
114110
} else {
115-
Vec::new()
111+
None
116112
};
117113

118114
let size = size_getter.get_size(&stats);
119115
reporter.report(Event::ReceiveData(size));
120116

121-
Info { size, children }
117+
make_info(size, children)
122118
},
123119

124120
join_path: |prefix, name| prefix.join(&name.0),

src/reporter/error_report/operation.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ pub enum Operation {
55
SymlinkMetadata,
66
/// Error is caused by calling [`std::fs::read_dir`].
77
ReadDirectory,
8-
/// Error when trying to access [`std::fs::DirEntry`] of one of the element of [`std::fs::read_dir`].
9-
AccessEntry,
108
}
119

1210
impl Operation {
@@ -16,7 +14,6 @@ impl Operation {
1614
match self {
1715
SymlinkMetadata => "symlink_metadata",
1816
ReadDirectory => "read_dir",
19-
AccessEntry => "access entry",
2017
}
2118
}
2219
}
@@ -36,5 +33,4 @@ mod test_operation {
3633

3734
name_display!(symlink_metadata, SymlinkMetadata, "symlink_metadata");
3835
name_display!(read_directory, ReadDirectory, "read_dir");
39-
name_display!(access_entry, AccessEntry, "access entry");
4036
}

src/tree_builder.rs

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,26 @@ pub mod info;
33
pub use info::Info;
44

55
use super::{data_tree::DataTree, size};
6+
use pipe_trait::Pipe;
67
use rayon::prelude::*;
8+
use std::{marker::PhantomData, ops::Not};
79

810
/// Collection of functions and starting points in order to build a [`DataTree`] with [`From`] or [`Into`].
911
#[derive(Debug)]
10-
pub struct TreeBuilder<Path, Name, Size, GetInfo, JoinPath>
12+
pub struct TreeBuilder<Path, NameIter, Size, GetInfo, JoinPath>
1113
where
1214
Path: Send + Sync,
13-
Name: Send + Sync,
14-
GetInfo: Fn(&Path) -> Info<Name, Size> + Copy + Send + Sync,
15-
JoinPath: Fn(&Path, &Name) -> Path + Copy + Send + Sync,
15+
NameIter: IntoIterator,
16+
NameIter::IntoIter: Send,
17+
NameIter::Item: Send,
18+
GetInfo: Fn(&Path) -> Info<NameIter, Size> + Copy + Send + Sync,
19+
JoinPath: Fn(&Path, &NameIter::Item) -> Path + Copy + Send + Sync,
1620
Size: size::Size + Send,
1721
{
1822
/// Path to the root.
1923
pub path: Path,
2024
/// Name of the root.
21-
pub name: Name,
25+
pub name: NameIter::Item,
2226
/// Function to extract necessary information from `path` (`size` and `children`).
2327
pub get_info: GetInfo,
2428
/// Function to join parent's `path` with a child's name to make the child's `name`.
@@ -27,17 +31,19 @@ where
2731
pub max_depth: u64,
2832
}
2933

30-
impl<Path, Name, Size, GetInfo, JoinPath> From<TreeBuilder<Path, Name, Size, GetInfo, JoinPath>>
31-
for DataTree<Name, Size>
34+
impl<Path, NameIter, Size, GetInfo, JoinPath>
35+
From<TreeBuilder<Path, NameIter, Size, GetInfo, JoinPath>> for DataTree<NameIter::Item, Size>
3236
where
3337
Path: Send + Sync,
34-
Name: Send + Sync,
35-
GetInfo: Fn(&Path) -> Info<Name, Size> + Copy + Send + Sync,
36-
JoinPath: Fn(&Path, &Name) -> Path + Copy + Send + Sync,
38+
NameIter: IntoIterator,
39+
NameIter::IntoIter: Send,
40+
NameIter::Item: Send,
41+
GetInfo: Fn(&Path) -> Info<NameIter, Size> + Copy + Send + Sync,
42+
JoinPath: Fn(&Path, &NameIter::Item) -> Path + Copy + Send + Sync,
3743
Size: size::Size + Send,
3844
{
3945
/// Create a [`DataTree`] from a [`TreeBuilder`].
40-
fn from(builder: TreeBuilder<Path, Name, Size, GetInfo, JoinPath>) -> Self {
46+
fn from(builder: TreeBuilder<Path, NameIter, Size, GetInfo, JoinPath>) -> Self {
4147
let TreeBuilder {
4248
path,
4349
name,
@@ -50,21 +56,68 @@ where
5056
let max_depth = max_depth.saturating_sub(1);
5157

5258
let children = children
53-
.into_par_iter()
54-
.map(|name| TreeBuilder {
55-
path: join_path(&path, &name),
56-
name,
57-
get_info,
58-
join_path,
59-
max_depth,
59+
.into_iter()
60+
.pipe(Chunks::<64, _>::new)
61+
.par_bridge()
62+
.map(|names: Vec<_>| -> Vec<_> {
63+
names
64+
.into_par_iter()
65+
.map(|name| TreeBuilder {
66+
path: join_path(&path, &name),
67+
name,
68+
get_info,
69+
join_path,
70+
max_depth,
71+
})
72+
.map(Self::from)
73+
.collect()
6074
})
61-
.map(Self::from);
75+
.reduce(Vec::new, add_short_vec_to_long);
6276

6377
if max_depth > 0 {
64-
DataTree::dir(name, size, children.collect())
78+
DataTree::dir(name, size, children)
6579
} else {
66-
let size = size + children.map(|child| child.size()).sum();
80+
let size = size + children.into_iter().map(|child| child.size()).sum();
6781
DataTree::dir(name, size, Vec::new())
6882
}
6983
}
7084
}
85+
86+
/// Concat 2 `Vec`s in any order with minimal copying.
87+
fn add_short_vec_to_long<Item>(mut a: Vec<Item>, mut b: Vec<Item>) -> Vec<Item> {
88+
if a.len() > b.len() {
89+
a.extend(b);
90+
a
91+
} else {
92+
b.extend(a);
93+
b
94+
}
95+
}
96+
97+
/// Utility type to iterate over each `Vec` of at most `LEN` items.
98+
#[derive(Debug, Clone, Copy)]
99+
struct Chunks<const LEN: usize, Iter> {
100+
iter: Iter,
101+
_phantom: PhantomData<[(); LEN]>,
102+
}
103+
104+
impl<const LEN: usize, Iter> Chunks<LEN, Iter> {
105+
/// Start iterating chunks.
106+
fn new(iter: Iter) -> Self {
107+
Chunks {
108+
iter,
109+
_phantom: PhantomData,
110+
}
111+
}
112+
}
113+
114+
impl<const LEN: usize, Iter> Iterator for Chunks<LEN, Iter>
115+
where
116+
Iter: Iterator,
117+
{
118+
type Item = Vec<Iter::Item>;
119+
fn next(&mut self) -> Option<Self::Item> {
120+
let chunk: Vec<_> = self.iter.by_ref().take(LEN).collect();
121+
chunk.is_empty().not().then_some(chunk)
122+
}
123+
}

src/tree_builder/info.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use crate::size;
22
use derive_more::From;
3-
use smart_default::SmartDefault;
43

54
/// Information to return from `get_info` of [`super::TreeBuilder`].
6-
#[derive(Debug, SmartDefault, From)]
7-
pub struct Info<Name, Size: size::Size> {
5+
#[derive(Debug, Default, From)]
6+
pub struct Info<NameIter, Size: size::Size> {
87
/// Size associated with given `path`.
98
pub size: Size,
109
/// Direct descendants of given `path`.
11-
pub children: Vec<Name>,
10+
pub children: NameIter,
1211
}

0 commit comments

Comments
 (0)