Skip to content

Commit 84cb3cd

Browse files
authored
Merge pull request #49 from KSXGitHub/bar-alignment
Customize the alignment of the bars
2 parents 85a9a6b + e8431ba commit 84cb3cd

16 files changed

+262
-38
lines changed

exports/completion.bash

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ _pdu() {
2020

2121
case "${cmd}" in
2222
pdu)
23-
opts=" -h -V --json-input --json-output --top-down --no-sort --silent-errors --progress --help --version --bytes-format --quantity --max-depth --total-width --column-width --min-ratio <files>... "
23+
opts=" -h -V --json-input --json-output --top-down --align-left --no-sort --silent-errors --progress --help --version --bytes-format --quantity --max-depth --total-width --column-width --min-ratio <files>... "
2424
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
2525
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
2626
return 0

exports/completion.elv

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ edit:completion:arg-completer[pdu] = [@words]{
2424
cand --json-input 'Read JSON data from stdin'
2525
cand --json-output 'Print JSON data instead of an ASCII chart'
2626
cand --top-down 'Print the tree top-down instead of bottom-up'
27+
cand --align-left 'Fill the bars from left to right'
2728
cand --no-sort 'Preserve order of entries'
2829
cand --silent-errors 'Prevent filesystem error messages from appearing in stderr'
2930
cand --progress 'Report progress being made at the expense of performance'

exports/completion.fish

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ complete -c pdu -n "__fish_use_subcommand" -l min-ratio -d 'Minimal size proport
77
complete -c pdu -n "__fish_use_subcommand" -l json-input -d 'Read JSON data from stdin'
88
complete -c pdu -n "__fish_use_subcommand" -l json-output -d 'Print JSON data instead of an ASCII chart'
99
complete -c pdu -n "__fish_use_subcommand" -l top-down -d 'Print the tree top-down instead of bottom-up'
10+
complete -c pdu -n "__fish_use_subcommand" -l align-left -d 'Fill the bars from left to right'
1011
complete -c pdu -n "__fish_use_subcommand" -l no-sort -d 'Preserve order of entries'
1112
complete -c pdu -n "__fish_use_subcommand" -l silent-errors -d 'Prevent filesystem error messages from appearing in stderr'
1213
complete -c pdu -n "__fish_use_subcommand" -l progress -d 'Report progress being made at the expense of performance'

exports/completion.ps1

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Register-ArgumentCompleter -Native -CommandName 'pdu' -ScriptBlock {
2929
[CompletionResult]::new('--json-input', 'json-input', [CompletionResultType]::ParameterName, 'Read JSON data from stdin')
3030
[CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'Print JSON data instead of an ASCII chart')
3131
[CompletionResult]::new('--top-down', 'top-down', [CompletionResultType]::ParameterName, 'Print the tree top-down instead of bottom-up')
32+
[CompletionResult]::new('--align-left', 'align-left', [CompletionResultType]::ParameterName, 'Fill the bars from left to right')
3233
[CompletionResult]::new('--no-sort', 'no-sort', [CompletionResultType]::ParameterName, 'Preserve order of entries')
3334
[CompletionResult]::new('--silent-errors', 'silent-errors', [CompletionResultType]::ParameterName, 'Prevent filesystem error messages from appearing in stderr')
3435
[CompletionResult]::new('--progress', 'progress', [CompletionResultType]::ParameterName, 'Report progress being made at the expense of performance')

exports/completion.zsh

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ _pdu() {
2424
'(--quantity)--json-input[Read JSON data from stdin]' \
2525
'--json-output[Print JSON data instead of an ASCII chart]' \
2626
'--top-down[Print the tree top-down instead of bottom-up]' \
27+
'--align-left[Fill the bars from left to right]' \
2728
'--no-sort[Preserve order of entries]' \
2829
'--silent-errors[Prevent filesystem error messages from appearing in stderr]' \
2930
'--progress[Report progress being made at the expense of performance]' \

src/app.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
runtime_error::RuntimeError,
1010
size::{Bytes, Size},
1111
size_getters::GET_APPARENT_SIZE,
12-
visualizer::{Direction, Visualizer},
12+
visualizer::{BarAlignment, Direction, Visualizer},
1313
};
1414
use pipe_trait::Pipe;
1515
use std::{io::stdin, time::Duration};
@@ -54,10 +54,12 @@ impl App {
5454
let Args {
5555
bytes_format,
5656
top_down,
57+
align_left,
5758
max_depth,
5859
..
5960
} = self.args;
6061
let direction = Direction::from_top_down(top_down);
62+
let bar_alignment = BarAlignment::from_align_left(align_left);
6163

6264
let unit_and_tree = stdin()
6365
.pipe(serde_json::from_reader::<_, JsonData>)
@@ -74,6 +76,7 @@ impl App {
7476
bytes_format: $bytes_format,
7577
column_width_distribution,
7678
direction,
79+
bar_alignment,
7780
max_depth,
7881
}
7982
.to_string()
@@ -128,6 +131,7 @@ impl App {
128131
json_output,
129132
bytes_format,
130133
top_down,
134+
align_left,
131135
max_depth,
132136
min_ratio,
133137
no_sort,
@@ -136,6 +140,7 @@ impl App {
136140
{
137141
return Sub {
138142
direction: Direction::from_top_down(top_down),
143+
bar_alignment: BarAlignment::from_align_left(align_left),
139144
get_data: $get_data,
140145
reporter: $create_reporter::<$data>(report_error),
141146
bytes_format: $format(bytes_format),

src/app/sub.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
runtime_error::RuntimeError,
99
size::Size,
1010
status_board::GLOBAL_STATUS_BOARD,
11-
visualizer::{ColumnWidthDistribution, Direction, Visualizer},
11+
visualizer::{BarAlignment, ColumnWidthDistribution, Direction, Visualizer},
1212
};
1313
use serde::Serialize;
1414
use std::{fs::Metadata, io::stdout, iter::once, num::NonZeroUsize, path::PathBuf};
@@ -29,6 +29,8 @@ where
2929
pub bytes_format: Data::DisplayFormat,
3030
/// The direction of the visualization.
3131
pub direction: Direction,
32+
/// The alignment of the bars.
33+
pub bar_alignment: BarAlignment,
3234
/// Distribution and number of characters/blocks can be placed in a line.
3335
pub column_width_distribution: ColumnWidthDistribution,
3436
/// Maximum number of levels that should be visualized.
@@ -57,6 +59,7 @@ where
5759
json_output,
5860
bytes_format,
5961
direction,
62+
bar_alignment,
6063
column_width_distribution,
6164
max_depth,
6265
get_data,
@@ -136,6 +139,7 @@ where
136139
data_tree: &data_tree,
137140
bytes_format,
138141
direction,
142+
bar_alignment,
139143
column_width_distribution,
140144
max_depth,
141145
};

src/args.rs

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ pub struct Args {
7777
#[structopt(long)]
7878
pub top_down: bool,
7979

80+
/// Fill the bars from left to right.
81+
#[structopt(long)]
82+
pub align_left: bool,
83+
8084
/// Aspect of the files/directories to be measured.
8185
#[structopt(long, possible_values = Quantity::VARIANTS, default_value = Quantity::default_value())]
8286
pub quantity: Quantity,

src/visualizer.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
pub mod bar_alignment;
12
pub mod child_position;
23
pub mod column_width_distribution;
34
pub mod direction;
45
pub mod parenthood;
56
pub mod proportion_bar;
67
pub mod tree;
78

9+
pub use bar_alignment::BarAlignment;
810
pub use child_position::ChildPosition;
911
pub use column_width_distribution::ColumnWidthDistribution;
1012
pub use direction::Direction;
@@ -27,13 +29,14 @@ use std::{fmt::Display, num::NonZeroUsize};
2729
/// # use parallel_disk_usage::os_string_display::OsStringDisplay;
2830
/// # use parallel_disk_usage::size::Bytes;
2931
/// # use parallel_disk_usage::bytes_format::BytesFormat;
30-
/// # use parallel_disk_usage::visualizer::{Visualizer, Direction, ColumnWidthDistribution};
32+
/// # use parallel_disk_usage::visualizer::{Visualizer, Direction, BarAlignment, ColumnWidthDistribution};
3133
/// # fn _wrapper(create_data_tree: fn() -> DataTree<OsStringDisplay, Bytes>) {
3234
/// let data_tree: DataTree<OsStringDisplay, Bytes> = create_data_tree();
3335
/// let visualizer = Visualizer {
3436
/// data_tree: &data_tree,
3537
/// bytes_format: BytesFormat::MetricUnits,
3638
/// direction: Direction::BottomUp,
39+
/// bar_alignment: BarAlignment::Right,
3740
/// column_width_distribution: ColumnWidthDistribution::total(100),
3841
/// max_depth: std::num::NonZeroUsize::new(10).unwrap(),
3942
/// };
@@ -52,6 +55,8 @@ where
5255
pub bytes_format: Data::DisplayFormat,
5356
/// The direction of the visualization of the tree.
5457
pub direction: Direction,
58+
/// The alignment of the bars.
59+
pub bar_alignment: BarAlignment,
5560
/// Distribution and total number of characters/blocks can be placed in a line.
5661
pub column_width_distribution: ColumnWidthDistribution,
5762
/// Maximum number of levels that should be visualized.

src/visualizer/bar_alignment.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/// The alignment of the bars.
2+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3+
pub enum BarAlignment {
4+
/// Fill the bars from left to right.
5+
Left,
6+
/// Fill the bars from right to left.
7+
Right,
8+
}
9+
10+
impl BarAlignment {
11+
#[cfg(feature = "cli")]
12+
pub(crate) const fn from_align_left(align_left: bool) -> Self {
13+
if align_left {
14+
BarAlignment::Left
15+
} else {
16+
BarAlignment::Right
17+
}
18+
}
19+
}

src/visualizer/methods.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ where
8686
"{size} {tree}│{bar}│{ratio}",
8787
size = align_right(&row.size, size_width),
8888
tree = align_left(&row.tree_horizontal_slice, tree_width),
89-
bar = &row.proportion_bar,
89+
bar = row.proportion_bar.display(self.bar_alignment),
9090
ratio = align_right(&row.percentage, PERCENTAGE_COLUMN_MAX_WIDTH),
9191
)
9292
})

src/visualizer/proportion_bar.rs

+37-15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::BarAlignment;
12
use derive_more::{AsRef, Deref, Display, From, Into};
23
use fmt_iter::repeat;
34
use std::fmt::{Display, Error, Formatter};
@@ -29,37 +30,58 @@ pub struct ProportionBar {
2930
}
3031

3132
impl ProportionBar {
32-
pub fn display_level0(self) -> impl Display {
33+
fn display_level0(self) -> impl Display {
3334
repeat(LEVEL0_BLOCK, self.level0)
3435
}
3536

36-
pub fn display_level1(self) -> impl Display {
37+
fn display_level1(self) -> impl Display {
3738
repeat(LEVEL1_BLOCK, self.level1)
3839
}
3940

40-
pub fn display_level2(self) -> impl Display {
41+
fn display_level2(self) -> impl Display {
4142
repeat(LEVEL2_BLOCK, self.level2)
4243
}
4344

44-
pub fn display_level3(self) -> impl Display {
45+
fn display_level3(self) -> impl Display {
4546
repeat(LEVEL3_BLOCK, self.level3)
4647
}
4748

48-
pub fn display_level4(self) -> impl Display {
49+
fn display_level4(self) -> impl Display {
4950
repeat(LEVEL4_BLOCK, self.level4)
5051
}
52+
53+
/// Create a [displayable](Display) value.
54+
pub fn display(self, align: BarAlignment) -> ProportionBarDisplay {
55+
ProportionBarDisplay { bar: self, align }
56+
}
57+
}
58+
59+
/// Result of [`ProportionBar::display`].
60+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61+
pub struct ProportionBarDisplay {
62+
pub bar: ProportionBar,
63+
pub align: BarAlignment,
5164
}
5265

53-
impl Display for ProportionBar {
66+
impl Display for ProportionBarDisplay {
5467
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), Error> {
55-
write!(
56-
formatter,
57-
"{level4}{level3}{level2}{level1}{level0}",
58-
level4 = self.display_level4(),
59-
level3 = self.display_level3(),
60-
level2 = self.display_level2(),
61-
level1 = self.display_level1(),
62-
level0 = self.display_level0(),
63-
)
68+
let ProportionBarDisplay { bar, align } = self;
69+
macro_rules! fmt {
70+
($pattern:literal) => {
71+
write!(
72+
formatter,
73+
$pattern,
74+
level0 = bar.display_level0(),
75+
level1 = bar.display_level1(),
76+
level2 = bar.display_level2(),
77+
level3 = bar.display_level3(),
78+
level4 = bar.display_level4(),
79+
);
80+
};
81+
}
82+
match align {
83+
BarAlignment::Left => fmt!("{level0}{level1}{level2}{level3}{level4}"),
84+
BarAlignment::Right => fmt!("{level4}{level3}{level2}{level1}{level0}"),
85+
}
6486
}
6587
}

tests/cli_errors.rs

+18-15
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,20 @@ pub use _utils::*;
55

66
use command_extra::CommandExtra;
77
use maplit::btreeset;
8+
use parallel_disk_usage::{
9+
bytes_format::BytesFormat,
10+
data_tree::DataTree,
11+
fs_tree_builder::FsTreeBuilder,
12+
os_string_display::OsStringDisplay,
13+
reporter::{ErrorOnlyReporter, ErrorReport},
14+
size_getters::GET_APPARENT_SIZE,
15+
visualizer::{BarAlignment, ColumnWidthDistribution, Direction, Visualizer},
16+
};
817
use pipe_trait::Pipe;
918
use pretty_assertions::assert_eq;
1019
use std::{
1120
collections::BTreeSet,
21+
convert::TryInto,
1222
path::Path,
1323
process::{Command, Output, Stdio},
1424
};
@@ -88,18 +98,6 @@ fn max_depth_0() {
8898
#[cfg(unix)]
8999
#[test]
90100
fn fs_errors() {
91-
use std::convert::TryInto;
92-
93-
use parallel_disk_usage::{
94-
bytes_format::BytesFormat,
95-
data_tree::DataTree,
96-
fs_tree_builder::FsTreeBuilder,
97-
os_string_display::OsStringDisplay,
98-
reporter::{ErrorOnlyReporter, ErrorReport},
99-
size_getters::GET_APPARENT_SIZE,
100-
visualizer::{ColumnWidthDistribution, Direction, Visualizer},
101-
};
102-
103101
let workspace = SampleWorkspace::default();
104102
fs_permission(workspace.join("empty-dir"), "-r", false);
105103
fs_permission(workspace.join("nested").join("0"), "-r", false);
@@ -133,6 +131,7 @@ fn fs_errors() {
133131
data_tree: &data_tree,
134132
bytes_format: BytesFormat::MetricUnits,
135133
direction: Direction::BottomUp,
134+
bar_alignment: BarAlignment::Right,
136135
column_width_distribution: ColumnWidthDistribution::total(100),
137136
max_depth: 10.try_into().unwrap(),
138137
};
@@ -141,10 +140,14 @@ fn fs_errors() {
141140

142141
fs_permission(workspace.as_path(), "+rwx", true); // to allow SampleWorkspace destructor to clean itself
143142

144-
let actual_stderr_lines: BTreeSet<_> = stderr.trim_end().lines().collect();
143+
let actual_stderr_lines: BTreeSet<_> = stderr
144+
.trim_end()
145+
.lines()
146+
.map(|line| line.trim_start_matches('\r'))
147+
.collect();
145148
let expected_stderr_lines = btreeset! {
146-
"\r\r[error] read_dir \"./nested/0\": Permission denied (os error 13)",
147-
"\r\r[error] read_dir \"./empty-dir\": Permission denied (os error 13)",
149+
"[error] read_dir \"./nested/0\": Permission denied (os error 13)",
150+
"[error] read_dir \"./empty-dir\": Permission denied (os error 13)",
148151
};
149152
assert_eq!(actual_stderr_lines, expected_stderr_lines);
150153

tests/json.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use parallel_disk_usage::{
1313
reporter::{ErrorOnlyReporter, ErrorReport},
1414
size::Bytes,
1515
size_getters::GET_APPARENT_SIZE,
16-
visualizer::{ColumnWidthDistribution, Direction, Visualizer},
16+
visualizer::{BarAlignment, ColumnWidthDistribution, Direction, Visualizer},
1717
};
1818
use pipe_trait::Pipe;
1919
use pretty_assertions::assert_eq;
@@ -131,6 +131,7 @@ fn json_input() {
131131
data_tree: &sample_tree(),
132132
bytes_format: BytesFormat::MetricUnits,
133133
direction: Direction::BottomUp,
134+
bar_alignment: BarAlignment::Right,
134135
column_width_distribution: ColumnWidthDistribution::total(100),
135136
max_depth: 10.try_into().unwrap(),
136137
};

0 commit comments

Comments
 (0)