parallel_disk_usage/
fs_tree_builder.rs

1use 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/// Build a [`DataTree`] from a directory tree using [`From`] or [`Into`].
16///
17/// **Example:**
18///
19/// ```no_run
20/// # use parallel_disk_usage::fs_tree_builder::FsTreeBuilder;
21/// use parallel_disk_usage::{
22///     data_tree::DataTree,
23///     get_size::GetApparentSize,
24///     os_string_display::OsStringDisplay,
25///     reporter::{ErrorOnlyReporter, ErrorReport},
26///     size::Bytes,
27/// };
28/// let builder = FsTreeBuilder {
29///     root: std::env::current_dir().unwrap(),
30///     size_getter: GetApparentSize,
31///     reporter: ErrorOnlyReporter::new(ErrorReport::SILENT),
32///     max_depth: 10,
33/// };
34/// let data_tree: DataTree<OsStringDisplay, Bytes> = builder.into();
35/// ```
36#[derive(Debug)]
37pub struct FsTreeBuilder<Size, SizeGetter, Report>
38where
39    Report: Reporter<Size> + Sync,
40    Size: size::Size + Send + Sync,
41    SizeGetter: GetSize<Size = Size> + Sync,
42{
43    /// Root of the directory tree.
44    pub root: PathBuf,
45    /// Returns size of an item.
46    pub size_getter: SizeGetter,
47    /// Reports progress to external system.
48    pub reporter: Report,
49    /// Deepest level of descendent display in the graph. The sizes beyond the max depth still count toward total.
50    pub max_depth: u64,
51}
52
53impl<Size, SizeGetter, Report> From<FsTreeBuilder<Size, SizeGetter, Report>>
54    for DataTree<OsStringDisplay, Size>
55where
56    Report: Reporter<Size> + Sync,
57    Size: size::Size + Send + Sync,
58    SizeGetter: GetSize<Size = Size> + Sync,
59{
60    /// Create a [`DataTree`] from an [`FsTreeBuilder`].
61    fn from(builder: FsTreeBuilder<Size, SizeGetter, Report>) -> Self {
62        let FsTreeBuilder {
63            root,
64            size_getter,
65            reporter,
66            max_depth,
67        } = builder;
68
69        TreeBuilder::<PathBuf, OsStringDisplay, Size, _, _> {
70            name: OsStringDisplay::os_string_from(&root),
71
72            path: root,
73
74            get_info: |path| {
75                let stats = match symlink_metadata(path) {
76                    Err(error) => {
77                        reporter.report(Event::EncounterError(ErrorReport {
78                            operation: SymlinkMetadata,
79                            path,
80                            error,
81                        }));
82                        return Info {
83                            size: Size::default(),
84                            children: Vec::new(),
85                        };
86                    }
87                    Ok(stats) => stats,
88                };
89
90                let children: Vec<_> = if stats.file_type().is_dir() {
91                    match read_dir(path) {
92                        Err(error) => {
93                            reporter.report(Event::EncounterError(ErrorReport {
94                                operation: ReadDirectory,
95                                path,
96                                error,
97                            }));
98                            return Info::default();
99                        }
100                        Ok(entries) => entries,
101                    }
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()
114                } else {
115                    Vec::new()
116                };
117
118                let size = size_getter.get_size(&stats);
119                reporter.report(Event::ReceiveData(size));
120
121                Info { size, children }
122            },
123
124            join_path: |prefix, name| prefix.join(&name.0),
125
126            max_depth,
127        }
128        .into()
129    }
130}