parallel_disk_usage/
fs_tree_builder.rs1use super::{
2 data_tree::DataTree,
3 get_size::GetSize,
4 os_string_display::OsStringDisplay,
5 reporter::{error_report::Operation::*, ErrorReport, Event, Reporter},
6 size,
7 tree_builder::{Info, TreeBuilder},
8};
9use pipe_trait::Pipe;
10use std::{
11 fs::{read_dir, symlink_metadata},
12 path::PathBuf,
13};
14
15#[derive(Debug)]
36pub struct FsTreeBuilder<Size, SizeGetter, Report>
37where
38 Report: Reporter<Size> + Sync,
39 Size: size::Size + Send + Sync,
40 SizeGetter: GetSize<Size = Size> + Sync,
41{
42 pub root: PathBuf,
44 pub size_getter: SizeGetter,
46 pub reporter: Report,
48}
49
50impl<Size, SizeGetter, Report> From<FsTreeBuilder<Size, SizeGetter, Report>>
51 for DataTree<OsStringDisplay, Size>
52where
53 Report: Reporter<Size> + Sync,
54 Size: size::Size + Send + Sync,
55 SizeGetter: GetSize<Size = Size> + Sync,
56{
57 fn from(builder: FsTreeBuilder<Size, SizeGetter, Report>) -> Self {
59 let FsTreeBuilder {
60 root,
61 size_getter,
62 reporter,
63 } = builder;
64
65 TreeBuilder::<PathBuf, OsStringDisplay, Size, _, _> {
66 name: OsStringDisplay::os_string_from(&root),
67
68 path: root,
69
70 get_info: |path| {
71 let stats = match symlink_metadata(path) {
72 Err(error) => {
73 reporter.report(Event::EncounterError(ErrorReport {
74 operation: SymlinkMetadata,
75 path,
76 error,
77 }));
78 return Info {
79 size: Size::default(),
80 children: Vec::new(),
81 };
82 }
83 Ok(stats) => stats,
84 };
85
86 let children: Vec<_> = if stats.file_type().is_dir() {
87 match read_dir(path) {
88 Err(error) => {
89 reporter.report(Event::EncounterError(ErrorReport {
90 operation: ReadDirectory,
91 path,
92 error,
93 }));
94 return Info::default();
95 }
96 Ok(entries) => entries,
97 }
98 .filter_map(|entry| match entry {
99 Err(error) => {
100 reporter.report(Event::EncounterError(ErrorReport {
101 operation: AccessEntry,
102 path,
103 error,
104 }));
105 None
106 }
107 Ok(entry) => entry.file_name().pipe(OsStringDisplay::from).pipe(Some),
108 })
109 .collect()
110 } else {
111 Vec::new()
112 };
113
114 let size = size_getter.get_size(&stats);
115 reporter.report(Event::ReceiveData(size));
116
117 Info { size, children }
118 },
119
120 join_path: |prefix, name| prefix.join(&name.0),
121 }
122 .into()
123 }
124}