parallel_disk_usage/
args.rs

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