1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
pub mod fraction;
pub mod quantity;

pub use fraction::Fraction;
pub use quantity::Quantity;

use crate::{bytes_format::BytesFormat, visualizer::ColumnWidthDistribution};
use std::{num::NonZeroUsize, path::PathBuf};
use structopt::StructOpt;
use strum::VariantNames;
use terminal_size::{terminal_size, Width};
use text_block_macros::text_block;

/// The CLI arguments.
#[derive(Debug, Clone, StructOpt)]
#[structopt(
    name = "pdu",

    long_about = text_block! {
        "Summarize disk usage of the set of files, recursively for directories."
        ""
        "Copyright: Apache-2.0 © 2021 Hoàng Văn Khải <https://ksxgithub.github.io/>"
        "Donation: https://patreon.com/khai96_"
    },

    after_help = text_block! {
        "EXAMPLES:"
        "    Show disk usage chart of current working directory"
        "    $ pdu"
        ""
        "    Show disk usage chart of a single file or directory"
        "    $ pdu path/to/file/or/directory"
        ""
        "    Compare disk usages of multiple files and/or directories"
        "    $ pdu file.txt dir/"
        ""
        "    Show chart in block sizes instead of apparent sizes"
        "    $ pdu --quantity=blksize"
        ""
        "    Show data in plain numbers instead of metric units"
        "    $ pdu --bytes-format=plain"
        ""
        "    Show data in base 2¹⁰ units (binary) instead of base 10³ units (metric)"
        "    $ pdu --bytes-format=binary"
        ""
        "    Show disk usage chart of all entries regardless of size"
        "    $ pdu --min-ratio=0"
        ""
        "    Only show disk usage chart of entries whose size is at least 5% of total"
        "    $ pdu --min-ratio=0.05"
    },
)]
pub struct Args {
    /// List of files and/or directories.
    #[structopt(name = "files")]
    pub files: Vec<PathBuf>,

    /// How to display the numbers of bytes.
    #[structopt(long, possible_values = BytesFormat::VARIANTS, default_value = BytesFormat::default_value())]
    pub bytes_format: BytesFormat,

    /// Print the tree top-down instead of bottom-up.
    #[structopt(long)]
    pub top_down: bool,

    /// Aspect of the files/directories to be measured.
    #[structopt(long, possible_values = Quantity::VARIANTS, default_value = Quantity::default_value())]
    pub quantity: Quantity,

    /// Maximum depth to display the data (must be greater than 0).
    #[structopt(long, default_value = "10")]
    pub max_depth: NonZeroUsize,

    /// Width of the visualization.
    #[structopt(long, conflicts_with = "column-width")]
    pub total_width: Option<usize>,

    /// Maximum widths of the tree column and width of the bar column.
    #[structopt(long, number_of_values = 2, value_names = &["tree-width", "bar-width"])]
    pub column_width: Option<Vec<usize>>,

    /// Minimal size proportion required to appear.
    #[structopt(long, default_value = "0.01")]
    pub min_ratio: Fraction,

    /// Preserve order of entries.
    #[structopt(long)]
    pub no_sort: bool,

    /// Prevent filesystem error messages from appearing in stderr.
    #[structopt(long)]
    pub silent_errors: bool,

    /// Report progress being made at the expense of performance.
    #[structopt(long)]
    pub progress: bool,
}

impl Args {
    /// Deduce [`ColumnWidthDistribution`] from `--total-width` or `--column-width`.
    pub(crate) fn column_width_distribution(&self) -> ColumnWidthDistribution {
        match (self.total_width, self.column_width.as_deref()) {
            (None, None) => {
                ColumnWidthDistribution::total(if let Some((Width(width), _)) = terminal_size() {
                    width as usize
                } else {
                    150
                })
            }
            (Some(total_width), None) => ColumnWidthDistribution::total(total_width),
            (None, Some([tree_width, bar_width])) => {
                ColumnWidthDistribution::components(*tree_width, *bar_width)
            }
            (total_width, column_width) => {
                dbg!(total_width, column_width);
                panic!("Something goes wrong")
            }
        }
    }
}