parallel_disk_usage/
args.rs

1pub mod fraction;
2pub mod quantity;
3pub mod threads;
4
5pub use fraction::Fraction;
6pub use quantity::Quantity;
7pub use threads::Threads;
8
9use crate::{bytes_format::BytesFormat, visualizer::ColumnWidthDistribution};
10use clap::{ColorChoice, Parser};
11use derive_setters::Setters;
12use smart_default::SmartDefault;
13use std::{num::NonZeroU64, path::PathBuf};
14use terminal_size::{terminal_size, Width};
15use text_block_macros::text_block;
16
17/// The CLI arguments.
18#[derive(Debug, SmartDefault, Setters, Clone, Parser)]
19#[clap(
20    name = "pdu",
21
22    version,
23
24    about = "Summarize disk usage of the set of files, recursively for directories.",
25
26    long_about = text_block! {
27        "Summarize disk usage of the set of files, recursively for directories."
28        ""
29        "Copyright: Apache-2.0 © 2021 Hoàng Văn Khải <https://github.com/KSXGitHub/>"
30        "Sponsor: https://github.com/sponsors/KSXGitHub"
31    },
32
33    after_help = text_block! {
34        "Examples:"
35        "    $ pdu"
36        "    $ pdu path/to/file/or/directory"
37        "    $ pdu file.txt dir/"
38        "    $ pdu --quantity=apparent-size"
39        "    $ pdu --deduplicate-hardlinks"
40        "    $ pdu --bytes-format=plain"
41        "    $ pdu --bytes-format=binary"
42        "    $ pdu --min-ratio=0"
43        "    $ pdu --min-ratio=0.05"
44        "    $ pdu --min-ratio=0 --json-output | jq"
45        "    $ pdu --min-ratio=0 < disk-usage.json"
46    },
47
48    after_long_help = text_block! {
49        "Examples:"
50        "    Show disk usage chart of current working directory"
51        "    $ pdu"
52        ""
53        "    Show disk usage chart of a single file or directory"
54        "    $ pdu path/to/file/or/directory"
55        ""
56        "    Compare disk usages of multiple files and/or directories"
57        "    $ pdu file.txt dir/"
58        ""
59        "    Show chart in apparent sizes instead of block sizes"
60        "    $ pdu --quantity=apparent-size"
61        ""
62        "    Detect and subtract the sizes of hardlinks from their parent nodes"
63        "    $ pdu --deduplicate-hardlinks"
64        ""
65        "    Show sizes in plain numbers instead of metric units"
66        "    $ pdu --bytes-format=plain"
67        ""
68        "    Show sizes in base 2¹⁰ units (binary) instead of base 10³ units (metric)"
69        "    $ pdu --bytes-format=binary"
70        ""
71        "    Show disk usage chart of all entries regardless of size"
72        "    $ pdu --min-ratio=0"
73        ""
74        "    Only show disk usage chart of entries whose size is at least 5% of total"
75        "    $ pdu --min-ratio=0.05"
76        ""
77        "    Show disk usage data as JSON instead of chart"
78        "    $ pdu --min-ratio=0 --json-output | jq"
79        ""
80        "    Visualize existing JSON representation of disk usage data"
81        "    $ pdu --min-ratio=0 < disk-usage.json"
82    },
83
84    color = ColorChoice::Never,
85)]
86#[setters(prefix = "with_")]
87#[non_exhaustive]
88pub struct Args {
89    /// List of files and/or directories.
90    pub files: Vec<PathBuf>,
91
92    /// Read JSON data from stdin.
93    #[clap(
94        long,
95        conflicts_with_all = ["quantity", "deduplicate_hardlinks"]
96    )]
97    pub json_input: bool,
98
99    /// Print JSON data instead of an ASCII chart.
100    #[clap(long)]
101    pub json_output: bool,
102
103    /// How to display the numbers of bytes.
104    #[clap(long, value_enum, default_value_t = BytesFormat::MetricUnits)]
105    #[default(BytesFormat::MetricUnits)]
106    pub bytes_format: BytesFormat,
107
108    /// Detect and subtract the sizes of hardlinks from their parent directory totals.
109    #[clap(long, visible_aliases = ["detect-links", "dedupe-links"])]
110    #[cfg_attr(not(unix), clap(hide = true))]
111    pub deduplicate_hardlinks: bool,
112
113    /// Print the tree top-down instead of bottom-up.
114    #[clap(long)]
115    pub top_down: bool,
116
117    /// Set the root of the bars to the right.
118    #[clap(long)]
119    pub align_right: bool,
120
121    /// Aspect of the files/directories to be measured.
122    #[clap(long, value_enum, default_value_t = Quantity::DEFAULT)]
123    #[default(Quantity::DEFAULT)]
124    pub quantity: Quantity,
125
126    /// Maximum depth to display the data (must be greater than 0).
127    #[clap(long, default_value = "10", visible_alias = "depth")]
128    #[default(_code = "10.try_into().unwrap()")]
129    pub max_depth: NonZeroU64,
130
131    /// Width of the visualization.
132    #[clap(long, conflicts_with = "column_width", visible_alias = "width")]
133    pub total_width: Option<usize>,
134
135    /// Maximum widths of the tree column and width of the bar column.
136    #[clap(long, number_of_values = 2, value_names = &["TREE_WIDTH", "BAR_WIDTH"])]
137    pub column_width: Option<Vec<usize>>,
138
139    /// Minimal size proportion required to appear.
140    #[clap(long, default_value = "0.01")]
141    pub min_ratio: Fraction,
142
143    /// Do not sort the branches in the tree.
144    #[clap(long)]
145    pub no_sort: bool,
146
147    /// Prevent filesystem error messages from appearing in stderr.
148    #[clap(long, visible_alias = "no-errors")]
149    pub silent_errors: bool,
150
151    /// Report progress being made at the expense of performance.
152    #[clap(long)]
153    pub progress: bool,
154
155    /// Set the maximum number of threads to spawn. Could be either "auto", "max", or a number.
156    #[clap(long, default_value_t = Threads::Auto)]
157    pub threads: Threads,
158
159    /// Do not output `.shared.details` in the JSON output.
160    #[clap(long, requires = "json_output", requires = "deduplicate_hardlinks")]
161    pub omit_json_shared_details: bool,
162
163    /// Do not output `.shared.summary` in the JSON output.
164    #[clap(long, requires = "json_output", requires = "deduplicate_hardlinks")]
165    pub omit_json_shared_summary: bool,
166}
167
168impl Args {
169    /// Deduce [`ColumnWidthDistribution`] from `--total-width` or `--column-width`.
170    pub(crate) fn column_width_distribution(&self) -> ColumnWidthDistribution {
171        match (self.total_width, self.column_width.as_deref()) {
172            (None, None) => {
173                ColumnWidthDistribution::total(if let Some((Width(width), _)) = terminal_size() {
174                    width as usize
175                } else {
176                    150
177                })
178            }
179            (Some(total_width), None) => ColumnWidthDistribution::total(total_width),
180            (None, Some([tree_width, bar_width])) => {
181                ColumnWidthDistribution::components(*tree_width, *bar_width)
182            }
183            (total_width, column_width) => {
184                dbg!(total_width, column_width);
185                panic!("Something goes wrong")
186            }
187        }
188    }
189}