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 max_depth,
61 ..
62 } = self.args;
63 let direction = Direction::from_top_down(top_down);
64 let bar_alignment = BarAlignment::from_align_right(align_right);
65
66 let unit_and_tree = stdin()
67 .pipe(serde_json::from_reader::<_, JsonData>)
68 .map_err(RuntimeError::DeserializationFailure)?
69 .unit_and_tree;
70
71 macro_rules! visualize {
72 ($reflection:expr, $bytes_format: expr) => {{
73 let data_tree = $reflection
74 .par_try_into_tree()
75 .map_err(|error| RuntimeError::InvalidInputReflection(error.to_string()))?;
76 Visualizer {
77 data_tree: &data_tree,
78 bytes_format: $bytes_format,
79 column_width_distribution,
80 direction,
81 bar_alignment,
82 max_depth,
83 }
84 .to_string()
85 }};
86 }
87
88 let visualization = match unit_and_tree {
89 UnitAndTree::Bytes(reflection) => visualize!(reflection, bytes_format),
90 UnitAndTree::Blocks(reflection) => visualize!(reflection, ()),
91 };
92
93 print!("{visualization}"); return Ok(());
95 }
96
97 let threads = match self.args.threads {
98 Threads::Auto => {
99 let disks = Disks::new_with_refreshed_list();
100 if any_path_is_in_hdd::<hdd::RealApi>(&self.args.files, &disks) {
101 eprintln!("warning: HDD detected, the thread limit will be set to 1");
102 Some(1)
103 } else {
104 None
105 }
106 }
107 Threads::Max => None,
108 Threads::Fixed(threads) => Some(threads),
109 };
110
111 if let Some(threads) = threads {
112 rayon::ThreadPoolBuilder::new()
113 .num_threads(threads)
114 .build_global()
115 .unwrap_or_else(|_| eprintln!("warning: Failed to set thread limit to {threads}"));
116 }
117
118 let report_error = if self.args.silent_errors {
119 ErrorReport::SILENT
120 } else {
121 ErrorReport::TEXT
122 };
123
124 #[allow(clippy::extra_unused_type_parameters)]
125 fn error_only_reporter<Size>(
126 report_error: fn(ErrorReport),
127 ) -> ErrorOnlyReporter<fn(ErrorReport)> {
128 ErrorOnlyReporter::new(report_error)
129 }
130
131 fn progress_and_error_reporter<Size>(
132 report_error: fn(ErrorReport),
133 ) -> ProgressAndErrorReporter<Size, fn(ErrorReport)>
134 where
135 Size: size::Size + Into<u64> + Send + Sync,
136 ProgressReport<Size>: Default + 'static,
137 u64: Into<Size>,
138 {
139 ProgressAndErrorReporter::new(
140 ProgressReport::TEXT,
141 Duration::from_millis(100),
142 report_error,
143 )
144 }
145
146 macro_rules! run {
147 ($(
148 $(#[$variant_attrs:meta])*
149 {
150 $size:ty => $format:expr;
151 $quantity:ident => $size_getter:ident;
152 $progress:literal => $create_reporter:ident;
153 }
154 )*) => { match self.args {$(
155 $(#[$variant_attrs])*
156 Args {
157 quantity: Quantity::$quantity,
158 progress: $progress,
159 files,
160 json_output,
161 bytes_format,
162 top_down,
163 align_right,
164 max_depth,
165 min_ratio,
166 no_sort,
167 ..
168 } => Sub {
169 direction: Direction::from_top_down(top_down),
170 bar_alignment: BarAlignment::from_align_right(align_right),
171 size_getter: $size_getter,
172 reporter: $create_reporter::<$size>(report_error),
173 bytes_format: $format(bytes_format),
174 files,
175 json_output,
176 column_width_distribution,
177 max_depth,
178 min_ratio,
179 no_sort,
180 }
181 .run(),
182 )*} };
183 }
184
185 run! {
186 {
187 Bytes => |x| x;
188 ApparentSize => GetApparentSize;
189 false => error_only_reporter;
190 }
191
192 {
193 Bytes => |x| x;
194 ApparentSize => GetApparentSize;
195 true => progress_and_error_reporter;
196 }
197
198 #[cfg(unix)]
199 {
200 Bytes => |x| x;
201 BlockSize => GetBlockSize;
202 false => error_only_reporter;
203 }
204
205 #[cfg(unix)]
206 {
207 Bytes => |x| x;
208 BlockSize => GetBlockSize;
209 true => progress_and_error_reporter;
210 }
211
212 #[cfg(unix)]
213 {
214 Blocks => |_| ();
215 BlockCount => GetBlockCount;
216 false => error_only_reporter;
217 }
218
219 #[cfg(unix)]
220 {
221 Blocks => |_| ();
222 BlockCount => GetBlockCount;
223 true => progress_and_error_reporter;
224 }
225 }
226 }
227}
228
229mod hdd;
230mod mount_point;