parallel_disk_usage/
app.rs1pub mod sub;
2
3pub use sub::Sub;
4
5use crate::{
6 args::{Args, Quantity, Threads},
7 get_size::GetApparentSize,
8 json_data::{JsonData, UnitAndTree},
9 reporter::{ErrorOnlyReporter, ErrorReport, ProgressAndErrorReporter, ProgressReport},
10 runtime_error::RuntimeError,
11 size::{self, Bytes},
12 visualizer::{BarAlignment, Direction, Visualizer},
13};
14use clap::Parser;
15use hdd::any_path_is_in_hdd;
16use pipe_trait::Pipe;
17use std::{io::stdin, time::Duration};
18use sysinfo::Disks;
19
20#[cfg(unix)]
21use crate::{
22 get_size::{GetBlockCount, GetBlockSize},
23 size::Blocks,
24};
25
26pub struct App {
28 args: Args,
30}
31
32impl App {
33 pub fn from_env() -> Self {
35 App {
36 args: Args::parse(),
37 }
38 }
39
40 pub fn run(self) -> Result<(), RuntimeError> {
42 let column_width_distribution = self.args.column_width_distribution();
50
51 if self.args.json_input {
52 if !self.args.files.is_empty() {
53 return Err(RuntimeError::JsonInputArgConflict);
54 }
55
56 let Args {
57 bytes_format,
58 top_down,
59 align_right,
60 ..
61 } = self.args;
62 let direction = Direction::from_top_down(top_down);
63 let bar_alignment = BarAlignment::from_align_right(align_right);
64
65 let unit_and_tree = stdin()
66 .pipe(serde_json::from_reader::<_, JsonData>)
67 .map_err(RuntimeError::DeserializationFailure)?
68 .unit_and_tree;
69
70 macro_rules! visualize {
71 ($reflection:expr, $bytes_format: expr) => {{
72 let data_tree = $reflection
73 .par_try_into_tree()
74 .map_err(|error| RuntimeError::InvalidInputReflection(error.to_string()))?;
75 Visualizer {
76 data_tree: &data_tree,
77 bytes_format: $bytes_format,
78 column_width_distribution,
79 direction,
80 bar_alignment,
81 }
82 .to_string()
83 }};
84 }
85
86 let visualization = match unit_and_tree {
87 UnitAndTree::Bytes(reflection) => visualize!(reflection, bytes_format),
88 UnitAndTree::Blocks(reflection) => visualize!(reflection, ()),
89 };
90
91 print!("{visualization}"); return Ok(());
93 }
94
95 let threads = match self.args.threads {
96 Threads::Auto => {
97 let disks = Disks::new_with_refreshed_list();
98 if any_path_is_in_hdd::<hdd::RealApi>(&self.args.files, &disks) {
99 eprintln!("warning: HDD detected, the thread limit will be set to 1");
100 Some(1)
101 } else {
102 None
103 }
104 }
105 Threads::Max => None,
106 Threads::Fixed(threads) => Some(threads),
107 };
108
109 if let Some(threads) = threads {
110 rayon::ThreadPoolBuilder::new()
111 .num_threads(threads)
112 .build_global()
113 .unwrap_or_else(|_| eprintln!("warning: Failed to set thread limit to {threads}"));
114 }
115
116 let report_error = if self.args.silent_errors {
117 ErrorReport::SILENT
118 } else {
119 ErrorReport::TEXT
120 };
121
122 #[allow(clippy::extra_unused_type_parameters)]
123 fn error_only_reporter<Size>(
124 report_error: fn(ErrorReport),
125 ) -> ErrorOnlyReporter<fn(ErrorReport)> {
126 ErrorOnlyReporter::new(report_error)
127 }
128
129 fn progress_and_error_reporter<Size>(
130 report_error: fn(ErrorReport),
131 ) -> ProgressAndErrorReporter<Size, fn(ErrorReport)>
132 where
133 Size: size::Size + Into<u64> + Send + Sync,
134 ProgressReport<Size>: Default + 'static,
135 u64: Into<Size>,
136 {
137 ProgressAndErrorReporter::new(
138 ProgressReport::TEXT,
139 Duration::from_millis(100),
140 report_error,
141 )
142 }
143
144 macro_rules! run {
145 ($(
146 $(#[$variant_attrs:meta])*
147 {
148 $size:ty => $format:expr;
149 $quantity:ident => $size_getter:ident;
150 $progress:literal => $create_reporter:ident;
151 }
152 )*) => { match self.args {$(
153 $(#[$variant_attrs])*
154 Args {
155 quantity: Quantity::$quantity,
156 progress: $progress,
157 files,
158 json_output,
159 bytes_format,
160 top_down,
161 align_right,
162 max_depth,
163 min_ratio,
164 no_sort,
165 ..
166 } => Sub {
167 direction: Direction::from_top_down(top_down),
168 bar_alignment: BarAlignment::from_align_right(align_right),
169 size_getter: $size_getter,
170 reporter: $create_reporter::<$size>(report_error),
171 bytes_format: $format(bytes_format),
172 files,
173 json_output,
174 column_width_distribution,
175 max_depth,
176 min_ratio,
177 no_sort,
178 }
179 .run(),
180 )*} };
181 }
182
183 run! {
184 {
185 Bytes => |x| x;
186 ApparentSize => GetApparentSize;
187 false => error_only_reporter;
188 }
189
190 {
191 Bytes => |x| x;
192 ApparentSize => GetApparentSize;
193 true => progress_and_error_reporter;
194 }
195
196 #[cfg(unix)]
197 {
198 Bytes => |x| x;
199 BlockSize => GetBlockSize;
200 false => error_only_reporter;
201 }
202
203 #[cfg(unix)]
204 {
205 Bytes => |x| x;
206 BlockSize => GetBlockSize;
207 true => progress_and_error_reporter;
208 }
209
210 #[cfg(unix)]
211 {
212 Blocks => |_| ();
213 BlockCount => GetBlockCount;
214 false => error_only_reporter;
215 }
216
217 #[cfg(unix)]
218 {
219 Blocks => |_| ();
220 BlockCount => GetBlockCount;
221 true => progress_and_error_reporter;
222 }
223 }
224 }
225}
226
227mod hdd;
228mod mount_point;