From c8c2a523e86eb5b840a65b2e4011a174b26d202b Mon Sep 17 00:00:00 2001 From: zhaotao1 Date: Mon, 10 Apr 2023 09:57:40 +0800 Subject: [PATCH 1/4] feat: display the size of a file or directory in "kilobytes" feat: display the size of a file or directory in "kilobytes" --- completions/_dust | 2 ++ completions/_dust.ps1 | 2 ++ completions/dust.bash | 2 +- completions/dust.elv | 2 ++ completions/dust.fish | 1 + man-page/dust.1 | 11 +++++--- src/cli.rs | 7 +++++ src/config.rs | 4 +++ src/display.rs | 61 +++++++++++++++++++++++++++++++------------ src/main.rs | 6 ++++- src/progress.rs | 28 +++++++++++++++----- 11 files changed, 96 insertions(+), 30 deletions(-) diff --git a/completions/_dust b/completions/_dust index ec51dd1e..4b306627 100644 --- a/completions/_dust +++ b/completions/_dust @@ -66,6 +66,8 @@ _dust() { '(-F --only-file -t --file_types)--only-dir[Only directories will be displayed.]' \ '(-D --only-dir)-F[Only files will be displayed. (Finds your largest files)]' \ '(-D --only-dir)--only-file[Only files will be displayed. (Finds your largest files)]' \ +'-k[display the size of a file or directory in kilobytes]' \ +'--display-kb[display the size of a file or directory in kilobytes]' \ '-h[Print help]' \ '--help[Print help]' \ '-V[Print version]' \ diff --git a/completions/_dust.ps1 b/completions/_dust.ps1 index f59c04e9..b8cfb0de 100644 --- a/completions/_dust.ps1 +++ b/completions/_dust.ps1 @@ -72,6 +72,8 @@ Register-ArgumentCompleter -Native -CommandName 'dust' -ScriptBlock { [CompletionResult]::new('--only-dir', 'only-dir', [CompletionResultType]::ParameterName, 'Only directories will be displayed.') [CompletionResult]::new('-F', 'F ', [CompletionResultType]::ParameterName, 'Only files will be displayed. (Finds your largest files)') [CompletionResult]::new('--only-file', 'only-file', [CompletionResultType]::ParameterName, 'Only files will be displayed. (Finds your largest files)') + [CompletionResult]::new('-k', 'k', [CompletionResultType]::ParameterName, 'display the size of a file or directory in kilobytes') + [CompletionResult]::new('--display-kb', 'display-kb', [CompletionResultType]::ParameterName, 'display the size of a file or directory in kilobytes') [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help') [CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version') diff --git a/completions/dust.bash b/completions/dust.bash index eddf14f7..23f3a325 100644 --- a/completions/dust.bash +++ b/completions/dust.bash @@ -19,7 +19,7 @@ _dust() { case "${cmd}" in dust) - opts="-d -n -p -X -I -L -x -s -r -c -b -B -z -R -f -i -v -e -t -w -H -P -D -F -S -h -V --depth --number-of-lines --full-paths --ignore-directory --ignore-all-in-file --dereference-links --limit-filesystem --apparent-size --reverse --no-colors --no-percent-bars --bars-on-right --min-size --screen-reader --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --si --no-progress --only-dir --only-file --stack-size --help --version [params]..." + opts="-d -n -p -X -I -L -x -s -r -c -b -B -z -R -f -i -v -e -t -w -H -P -D -F -k -S -h -V --depth --number-of-lines --full-paths --ignore-directory --ignore-all-in-file --dereference-links --limit-filesystem --apparent-size --reverse --no-colors --no-percent-bars --bars-on-right --min-size --screen-reader --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --si --no-progress --only-dir --only-file --display-kb --stack-size --help --version [params]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/completions/dust.elv b/completions/dust.elv index f5faf473..915e7493 100644 --- a/completions/dust.elv +++ b/completions/dust.elv @@ -69,6 +69,8 @@ set edit:completion:arg-completer[dust] = {|@words| cand --only-dir 'Only directories will be displayed.' cand -F 'Only files will be displayed. (Finds your largest files)' cand --only-file 'Only files will be displayed. (Finds your largest files)' + cand -k 'display the size of a file or directory in kilobytes' + cand --display-kb 'display the size of a file or directory in kilobytes' cand -h 'Print help' cand --help 'Print help' cand -V 'Print version' diff --git a/completions/dust.fish b/completions/dust.fish index c36e86e1..a193a90a 100644 --- a/completions/dust.fish +++ b/completions/dust.fish @@ -24,5 +24,6 @@ complete -c dust -s H -l si -d 'print sizes in powers of 1000 (e.g., 1.1G)' complete -c dust -s P -l no-progress -d 'Disable the progress indication.' complete -c dust -s D -l only-dir -d 'Only directories will be displayed.' complete -c dust -s F -l only-file -d 'Only files will be displayed. (Finds your largest files)' +complete -c dust -s k -l display-kb -d 'display the size of a file or directory in kilobytes' complete -c dust -s h -l help -d 'Print help' complete -c dust -s V -l version -d 'Print version' diff --git a/man-page/dust.1 b/man-page/dust.1 index 7977f875..a9f77fcd 100644 --- a/man-page/dust.1 +++ b/man-page/dust.1 @@ -1,10 +1,10 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH Dust 1 "Dust 0.9.0" +.TH Dust 1 "Dust 0.9.0" .SH NAME Dust \- Like du but more intuitive .SH SYNOPSIS -\fBdust\fR [\fB\-d\fR|\fB\-\-depth\fR] [\fB\-n\fR|\fB\-\-number\-of\-lines\fR] [\fB\-p\fR|\fB\-\-full\-paths\fR] [\fB\-X\fR|\fB\-\-ignore\-directory\fR] [\fB\-I\fR|\fB\-\-ignore\-all\-in\-file\fR] [\fB\-L\fR|\fB\-\-dereference\-links\fR] [\fB\-x\fR|\fB\-\-limit\-filesystem\fR] [\fB\-s\fR|\fB\-\-apparent\-size\fR] [\fB\-r\fR|\fB\-\-reverse\fR] [\fB\-c\fR|\fB\-\-no\-colors\fR] [\fB\-b\fR|\fB\-\-no\-percent\-bars\fR] [\fB\-B\fR|\fB\-\-bars\-on\-right\fR] [\fB\-z\fR|\fB\-\-min\-size\fR] [\fB\-R\fR|\fB\-\-screen\-reader\fR] [\fB\-\-skip\-total\fR] [\fB\-f\fR|\fB\-\-filecount\fR] [\fB\-i\fR|\fB\-\-ignore_hidden\fR] [\fB\-v\fR|\fB\-\-invert\-filter\fR] [\fB\-e\fR|\fB\-\-filter\fR] [\fB\-t\fR|\fB\-\-file_types\fR] [\fB\-w\fR|\fB\-\-terminal_width\fR] [\fB\-H\fR|\fB\-\-si\fR] [\fB\-P\fR|\fB\-\-no\-progress\fR] [\fB\-D\fR|\fB\-\-only\-dir\fR] [\fB\-F\fR|\fB\-\-only\-file\fR] [\fB\-S\fR|\fB\-\-stack\-size\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIparams\fR] +\fBDust\fR [\fB\-d\fR|\fB\-\-depth\fR] [\fB\-n\fR|\fB\-\-number\-of\-lines\fR] [\fB\-p\fR|\fB\-\-full\-paths\fR] [\fB\-X\fR|\fB\-\-ignore\-directory\fR] [\fB\-I\fR|\fB\-\-ignore\-all\-in\-file\fR] [\fB\-L\fR|\fB\-\-dereference\-links\fR] [\fB\-x\fR|\fB\-\-limit\-filesystem\fR] [\fB\-s\fR|\fB\-\-apparent\-size\fR] [\fB\-r\fR|\fB\-\-reverse\fR] [\fB\-c\fR|\fB\-\-no\-colors\fR] [\fB\-b\fR|\fB\-\-no\-percent\-bars\fR] [\fB\-B\fR|\fB\-\-bars\-on\-right\fR] [\fB\-z\fR|\fB\-\-min\-size\fR] [\fB\-R\fR|\fB\-\-screen\-reader\fR] [\fB\-\-skip\-total\fR] [\fB\-f\fR|\fB\-\-filecount\fR] [\fB\-i\fR|\fB\-\-ignore_hidden\fR] [\fB\-v\fR|\fB\-\-invert\-filter\fR] [\fB\-e\fR|\fB\-\-filter\fR] [\fB\-t\fR|\fB\-\-file_types\fR] [\fB\-w\fR|\fB\-\-terminal_width\fR] [\fB\-H\fR|\fB\-\-si\fR] [\fB\-P\fR|\fB\-\-no\-progress\fR] [\fB\-D\fR|\fB\-\-only\-dir\fR] [\fB\-F\fR|\fB\-\-only\-file\fR] [\fB\-k\fR|\fB\-\-display\-kb\fR] [\fB\-S\fR|\fB\-\-stack\-size\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIparams\fR] .SH DESCRIPTION Like du but more intuitive .SH OPTIONS @@ -61,10 +61,10 @@ Directory \*(Aqsize\*(Aq is number of child files instead of disk size Do not display hidden files .TP \fB\-v\fR, \fB\-\-invert\-filter\fR -Exclude filepaths matching this regex. To ignore png files type: \-v "\\.png$" +Exclude filepaths matching this regex. To ignore png files type: \-v "\\.png$" .TP \fB\-e\fR, \fB\-\-filter\fR -Only include filepaths matching this regex. For png files type: \-e "\\.png$" +Only include filepaths matching this regex. For png files type: \-e "\\.png$" .TP \fB\-t\fR, \fB\-\-file_types\fR show only these file types @@ -84,6 +84,9 @@ Only directories will be displayed. \fB\-F\fR, \fB\-\-only\-file\fR Only files will be displayed. (Finds your largest files) .TP +\fB\-k\fR, \fB\-\-display\-kb\fR +display the size of a file or directory in kilobytes +.TP \fB\-S\fR, \fB\-\-stack\-size\fR Specify memory to use as stack size \- use if you see: \*(Aqfatal runtime error: stack overflow\*(Aq (default low memory=1048576, high memory=1073741824) .TP diff --git a/src/cli.rs b/src/cli.rs index 468f93c2..dd5d01b2 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -193,6 +193,13 @@ pub fn build_cli() -> Command { .action(clap::ArgAction::SetTrue) .help("Only files will be displayed. (Finds your largest files)"), ) + .arg( + Arg::new("display_kb") + .short('k') + .long("display-kb") + .action(clap::ArgAction::SetTrue) + .help("display the size of a file or directory in kilobytes"), + ) .arg( Arg::new("stack_size") .short('S') diff --git a/src/config.rs b/src/config.rs index d74528ab..3e058c22 100644 --- a/src/config.rs +++ b/src/config.rs @@ -26,6 +26,7 @@ pub struct Config { pub disable_progress: Option, pub depth: Option, pub bars_on_right: Option, + pub display_kb: Option, pub stack_size: Option, } @@ -104,6 +105,9 @@ impl Config { from_cmd_line.copied() } } + pub fn get_display_kb(&self, options: &ArgMatches) -> bool { + Some(true) == self.display_kb || options.get_flag("display_kb") + } } fn convert_min_size(input: &str, iso: bool) -> Option { diff --git a/src/display.rs b/src/display.rs index 0516f2fd..68bbb4dd 100644 --- a/src/display.rs +++ b/src/display.rs @@ -25,6 +25,7 @@ pub struct InitialDisplayData { pub is_screen_reader: bool, pub iso: bool, pub bars_on_right: bool, + pub display_kb: bool, } pub struct DisplayData { @@ -129,6 +130,7 @@ pub fn draw_it( terminal_width: usize, root_node: &DisplayNode, skip_total: bool, + display_kb: bool, ) { let biggest = match skip_total { false => root_node, @@ -142,7 +144,7 @@ pub fn draw_it( let max_size = biggest.size; max_size.separate_with_commas().chars().count() } else { - find_biggest_size_str(root_node, idd.iso) + find_biggest_size_str(root_node, idd.iso, display_kb) }; assert!( @@ -190,10 +192,12 @@ pub fn draw_it( } } -fn find_biggest_size_str(node: &DisplayNode, iso: bool) -> usize { - let mut mx = human_readable_number(node.size, iso).chars().count(); +fn find_biggest_size_str(node: &DisplayNode, iso: bool, display_kb: bool) -> usize { + let mut mx = human_readable_number(node.size, iso, display_kb) + .chars() + .count(); for n in node.children.iter() { - mx = max(mx, find_biggest_size_str(n, iso)); + mx = max(mx, find_biggest_size_str(n, iso, display_kb)); } mx } @@ -375,7 +379,11 @@ fn get_pretty_size(node: &DisplayNode, is_biggest: bool, display_data: &DisplayD let output = if display_data.initial.by_filecount { node.size.separate_with_commas() } else { - human_readable_number(node.size, display_data.initial.iso) + human_readable_number( + node.size, + display_data.initial.iso, + display_data.initial.display_kb, + ) }; let spaces_to_add = display_data.num_chars_needed_on_left_most - output.chars().count(); let output = " ".repeat(spaces_to_add) + output.as_str(); @@ -407,9 +415,12 @@ fn get_pretty_name( } } -pub fn human_readable_number(size: u64, iso: bool) -> String { +pub fn human_readable_number(size: u64, iso: bool, display_kb: bool) -> String { + let num: u64 = if iso { 1000 } else { 1024 }; + if display_kb { + return format!("{:.1}{}", (size as f32 / num as f32), 'K'); + } for (i, u) in UNITS.iter().enumerate() { - let num: u64 = if iso { 1000 } else { 1024 }; let marker = num.pow((UNITS.len() - i) as u32); if size >= marker { if size / marker < 10 { @@ -438,6 +449,7 @@ mod tests { is_screen_reader: false, iso: false, bars_on_right: false, + display_kb: false, }; DisplayData { initial, @@ -503,20 +515,23 @@ mod tests { #[test] fn test_human_readable_number() { - assert_eq!(human_readable_number(1, false), "1B"); - assert_eq!(human_readable_number(956, false), "956B"); - assert_eq!(human_readable_number(1004, false), "1004B"); - assert_eq!(human_readable_number(1024, false), "1.0K"); - assert_eq!(human_readable_number(1536, false), "1.5K"); - assert_eq!(human_readable_number(1024 * 512, false), "512K"); - assert_eq!(human_readable_number(1024 * 1024, false), "1.0M"); + assert_eq!(human_readable_number(1, false, false), "1B"); + assert_eq!(human_readable_number(956, false, false), "956B"); + assert_eq!(human_readable_number(1004, false, false), "1004B"); + assert_eq!(human_readable_number(1024, false, false), "1.0K"); + assert_eq!(human_readable_number(1536, false, false), "1.5K"); + assert_eq!(human_readable_number(1024 * 512, false, false), "512K"); + assert_eq!(human_readable_number(1024 * 1024, false, false), "1.0M"); assert_eq!( - human_readable_number(1024 * 1024 * 1024 - 1, false), + human_readable_number(1024 * 1024 * 1024 - 1, false, false), "1023M" ); - assert_eq!(human_readable_number(1024 * 1024 * 1024 * 20, false), "20G"); assert_eq!( - human_readable_number(1024 * 1024 * 1024 * 1024, false), + human_readable_number(1024 * 1024 * 1024 * 20, false, false), + "20G" + ); + assert_eq!( + human_readable_number(1024 * 1024 * 1024 * 1024, false, false), "1.0T" ); } @@ -574,4 +589,16 @@ mod tests { let bar = dd.generate_bar(&n, 5); assert_eq!(bar, "████▓▓▓▓▓▓▓▓▓"); } + #[test] + fn test_human_readable_number_kb() { + assert_eq!(human_readable_number(1, false, true), "0.0K"); + assert_eq!(human_readable_number(1024, false, true), "1.0K"); + assert_eq!(human_readable_number(1536, false, true), "1.5K"); + assert_eq!(human_readable_number(1024 * 512, false, true), "512.0K"); + assert_eq!(human_readable_number(1024 * 1024, false, true), "1024.0K"); + assert_eq!( + human_readable_number(1024 * 1000 * 1000 * 20, false, true), + "20000000.0K" + ); + } } diff --git a/src/main.rs b/src/main.rs index 0b81002d..ac93139e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -180,11 +180,13 @@ fn main() { let iso = config.get_iso(&options); + let display_kb = config.get_display_kb(&options); + let ignore_hidden = config.get_ignore_hidden(&options); let mut indicator = PIndicator::build_me(); if !config.get_disable_progress(&options) { - indicator.spawn(iso); + indicator.spawn(iso, display_kb); } let walk_data = WalkData { @@ -255,6 +257,7 @@ fn main() { iso, is_screen_reader: config.get_screen_reader(&options), bars_on_right: config.get_bars_on_right(&options), + display_kb, }; draw_it( idd, @@ -262,6 +265,7 @@ fn main() { terminal_width, &root_node, config.get_skip_total(&options), + display_kb, ) } } diff --git a/src/progress.rs b/src/progress.rs index b4d7a89d..afeee047 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -77,16 +77,26 @@ pub struct RuntimeErrors { /* -------------------------------------------------------------------------- */ -fn format_preparing_str(prog_char: char, data: &PAtomicInfo, is_iso: bool) -> String { +fn format_preparing_str( + prog_char: char, + data: &PAtomicInfo, + is_iso: bool, + is_display_kb: bool, +) -> String { let path_in = data.current_path.get(); - let size = human_readable_number(data.total_file_size.load(ORDERING), is_iso); + let size = human_readable_number(data.total_file_size.load(ORDERING), is_iso, is_display_kb); format!("Preparing: {path_in} {size} ... {prog_char}") } -fn format_indexing_str(prog_char: char, data: &PAtomicInfo, is_iso: bool) -> String { +fn format_indexing_str( + prog_char: char, + data: &PAtomicInfo, + is_iso: bool, + display_kb: bool, +) -> String { let path_in = data.current_path.get(); let file_count = data.num_files.load(ORDERING); - let size = human_readable_number(data.total_file_size.load(ORDERING), is_iso); + let size = human_readable_number(data.total_file_size.load(ORDERING), is_iso, display_kb); let file_str = format!("{file_count} files, {size}"); format!("Indexing: {path_in} {file_str} ... {prog_char}") } @@ -106,7 +116,7 @@ impl PIndicator { } } - pub fn spawn(&mut self, is_iso: bool) { + pub fn spawn(&mut self, is_iso: bool, display_kb: bool) { let data = self.data.clone(); let (stop_handler, receiver) = mpsc::channel::<()>(); @@ -125,8 +135,12 @@ impl PIndicator { let prog_char = PROGRESS_CHARS[progress_char_i]; msg = match data.state.load(ORDERING) { - Operation::INDEXING => format_indexing_str(prog_char, &data, is_iso), - Operation::PREPARING => format_preparing_str(prog_char, &data, is_iso), + Operation::INDEXING => { + format_indexing_str(prog_char, &data, is_iso, display_kb) + } + Operation::PREPARING => { + format_preparing_str(prog_char, &data, is_iso, display_kb) + } _ => panic!("Unknown State"), }; From 96cd5251f98b9d4fbdc15ed571e39f0029d3cae4 Mon Sep 17 00:00:00 2001 From: "andy.boot" Date: Wed, 21 Feb 2024 22:57:33 +0000 Subject: [PATCH 2/4] refactor: merge --si and --display-kb add new option: --output-format this controls how the output is summarised and takes: nothing = default behaviour si = SI units (same as old --si flag) b = bytes kb = kb kib = si kb mb = mb mib = si mb ....etc --- README.md | 2 +- completions/_dust | 6 +-- completions/_dust.ps1 | 6 +-- completions/dust.bash | 10 ++++- completions/dust.elv | 6 +-- completions/dust.fish | 3 +- man-page/dust.1 | 15 +++---- src/cli.rs | 17 +++---- src/config.rs | 55 ++++++++++++----------- src/display.rs | 100 +++++++++++++++++++++--------------------- src/main.rs | 12 ++--- src/progress.rs | 28 +++--------- 12 files changed, 120 insertions(+), 140 deletions(-) diff --git a/README.md b/README.md index 7a4b0ae8..79da23fd 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Usage: dust -d 3 (Shows 3 levels of subdirectories) Usage: dust -D (Show only directories (eg dust -D)) Usage: dust -F (Show only files - finds your largest files) Usage: dust -r (reverse order of output) -Usage: dust -H (si print sizes in powers of 1000 instead of 1024) +Usage: dust -o si/b/kb/kib/mb/mib/gb/gib (si - prints sizes in powers of 1000. Others print size in that format). Usage: dust -X ignore (ignore all files and directories with the name 'ignore') Usage: dust -x (Only show directories on the same filesystem) Usage: dust -b (Do not show percentages or draw ASCII bars) diff --git a/completions/_dust b/completions/_dust index 4b306627..4b850d74 100644 --- a/completions/_dust +++ b/completions/_dust @@ -31,6 +31,8 @@ _dust() { '(-t --file_types)*--filter=[Only include filepaths matching this regex. For png files type\: -e "\\.png\$" ]: : ' \ '-w+[Specify width of output overriding the auto detection of terminal width]: : ' \ '--terminal_width=[Specify width of output overriding the auto detection of terminal width]: : ' \ +'-o+[Changes output display size. si will print sizes in powers of 1000. b/bytes kb kib mb mib gb gib will print the whole tree in that size]: : ' \ +'--output-format=[Changes output display size. si will print sizes in powers of 1000. b/bytes kb kib mb mib gb gib will print the whole tree in that size]: : ' \ '-S+[Specify memory to use as stack size - use if you see\: '\''fatal runtime error\: stack overflow'\'' (default low memory=1048576, high memory=1073741824)]: : ' \ '--stack-size=[Specify memory to use as stack size - use if you see\: '\''fatal runtime error\: stack overflow'\'' (default low memory=1048576, high memory=1073741824)]: : ' \ '-p[Subdirectories will not have their path shortened]' \ @@ -58,16 +60,12 @@ _dust() { '--ignore_hidden[Do not display hidden files]' \ '(-d --depth -D --only-dir)-t[show only these file types]' \ '(-d --depth -D --only-dir)--file_types[show only these file types]' \ -'-H[print sizes in powers of 1000 (e.g., 1.1G)]' \ -'--si[print sizes in powers of 1000 (e.g., 1.1G)]' \ '-P[Disable the progress indication.]' \ '--no-progress[Disable the progress indication.]' \ '(-F --only-file -t --file_types)-D[Only directories will be displayed.]' \ '(-F --only-file -t --file_types)--only-dir[Only directories will be displayed.]' \ '(-D --only-dir)-F[Only files will be displayed. (Finds your largest files)]' \ '(-D --only-dir)--only-file[Only files will be displayed. (Finds your largest files)]' \ -'-k[display the size of a file or directory in kilobytes]' \ -'--display-kb[display the size of a file or directory in kilobytes]' \ '-h[Print help]' \ '--help[Print help]' \ '-V[Print version]' \ diff --git a/completions/_dust.ps1 b/completions/_dust.ps1 index b8cfb0de..ddfb26f6 100644 --- a/completions/_dust.ps1 +++ b/completions/_dust.ps1 @@ -37,6 +37,8 @@ Register-ArgumentCompleter -Native -CommandName 'dust' -ScriptBlock { [CompletionResult]::new('--filter', 'filter', [CompletionResultType]::ParameterName, 'Only include filepaths matching this regex. For png files type: -e "\.png$" ') [CompletionResult]::new('-w', 'w', [CompletionResultType]::ParameterName, 'Specify width of output overriding the auto detection of terminal width') [CompletionResult]::new('--terminal_width', 'terminal_width', [CompletionResultType]::ParameterName, 'Specify width of output overriding the auto detection of terminal width') + [CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'Changes output display size. si will print sizes in powers of 1000. b/bytes kb kib mb mib gb gib will print the whole tree in that size') + [CompletionResult]::new('--output-format', 'output-format', [CompletionResultType]::ParameterName, 'Changes output display size. si will print sizes in powers of 1000. b/bytes kb kib mb mib gb gib will print the whole tree in that size') [CompletionResult]::new('-S', 'S ', [CompletionResultType]::ParameterName, 'Specify memory to use as stack size - use if you see: ''fatal runtime error: stack overflow'' (default low memory=1048576, high memory=1073741824)') [CompletionResult]::new('--stack-size', 'stack-size', [CompletionResultType]::ParameterName, 'Specify memory to use as stack size - use if you see: ''fatal runtime error: stack overflow'' (default low memory=1048576, high memory=1073741824)') [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Subdirectories will not have their path shortened') @@ -64,16 +66,12 @@ Register-ArgumentCompleter -Native -CommandName 'dust' -ScriptBlock { [CompletionResult]::new('--ignore_hidden', 'ignore_hidden', [CompletionResultType]::ParameterName, 'Do not display hidden files') [CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'show only these file types') [CompletionResult]::new('--file_types', 'file_types', [CompletionResultType]::ParameterName, 'show only these file types') - [CompletionResult]::new('-H', 'H ', [CompletionResultType]::ParameterName, 'print sizes in powers of 1000 (e.g., 1.1G)') - [CompletionResult]::new('--si', 'si', [CompletionResultType]::ParameterName, 'print sizes in powers of 1000 (e.g., 1.1G)') [CompletionResult]::new('-P', 'P ', [CompletionResultType]::ParameterName, 'Disable the progress indication.') [CompletionResult]::new('--no-progress', 'no-progress', [CompletionResultType]::ParameterName, 'Disable the progress indication.') [CompletionResult]::new('-D', 'D ', [CompletionResultType]::ParameterName, 'Only directories will be displayed.') [CompletionResult]::new('--only-dir', 'only-dir', [CompletionResultType]::ParameterName, 'Only directories will be displayed.') [CompletionResult]::new('-F', 'F ', [CompletionResultType]::ParameterName, 'Only files will be displayed. (Finds your largest files)') [CompletionResult]::new('--only-file', 'only-file', [CompletionResultType]::ParameterName, 'Only files will be displayed. (Finds your largest files)') - [CompletionResult]::new('-k', 'k', [CompletionResultType]::ParameterName, 'display the size of a file or directory in kilobytes') - [CompletionResult]::new('--display-kb', 'display-kb', [CompletionResultType]::ParameterName, 'display the size of a file or directory in kilobytes') [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help') [CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version') diff --git a/completions/dust.bash b/completions/dust.bash index 23f3a325..24c37e0c 100644 --- a/completions/dust.bash +++ b/completions/dust.bash @@ -19,7 +19,7 @@ _dust() { case "${cmd}" in dust) - opts="-d -n -p -X -I -L -x -s -r -c -b -B -z -R -f -i -v -e -t -w -H -P -D -F -k -S -h -V --depth --number-of-lines --full-paths --ignore-directory --ignore-all-in-file --dereference-links --limit-filesystem --apparent-size --reverse --no-colors --no-percent-bars --bars-on-right --min-size --screen-reader --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --si --no-progress --only-dir --only-file --display-kb --stack-size --help --version [params]..." + opts="-d -n -p -X -I -L -x -s -r -c -b -B -z -R -f -i -v -e -t -w -P -D -F -o -S -h -V --depth --number-of-lines --full-paths --ignore-directory --ignore-all-in-file --dereference-links --limit-filesystem --apparent-size --reverse --no-colors --no-percent-bars --bars-on-right --min-size --screen-reader --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --no-progress --only-dir --only-file --output-format --stack-size --help --version [params]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -89,6 +89,14 @@ _dust() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --output-format) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -o) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --stack-size) COMPREPLY=($(compgen -f "${cur}")) return 0 diff --git a/completions/dust.elv b/completions/dust.elv index 915e7493..a001ccb3 100644 --- a/completions/dust.elv +++ b/completions/dust.elv @@ -34,6 +34,8 @@ set edit:completion:arg-completer[dust] = {|@words| cand --filter 'Only include filepaths matching this regex. For png files type: -e "\.png$" ' cand -w 'Specify width of output overriding the auto detection of terminal width' cand --terminal_width 'Specify width of output overriding the auto detection of terminal width' + cand -o 'Changes output display size. si will print sizes in powers of 1000. b/bytes kb kib mb mib gb gib will print the whole tree in that size' + cand --output-format 'Changes output display size. si will print sizes in powers of 1000. b/bytes kb kib mb mib gb gib will print the whole tree in that size' cand -S 'Specify memory to use as stack size - use if you see: ''fatal runtime error: stack overflow'' (default low memory=1048576, high memory=1073741824)' cand --stack-size 'Specify memory to use as stack size - use if you see: ''fatal runtime error: stack overflow'' (default low memory=1048576, high memory=1073741824)' cand -p 'Subdirectories will not have their path shortened' @@ -61,16 +63,12 @@ set edit:completion:arg-completer[dust] = {|@words| cand --ignore_hidden 'Do not display hidden files' cand -t 'show only these file types' cand --file_types 'show only these file types' - cand -H 'print sizes in powers of 1000 (e.g., 1.1G)' - cand --si 'print sizes in powers of 1000 (e.g., 1.1G)' cand -P 'Disable the progress indication.' cand --no-progress 'Disable the progress indication.' cand -D 'Only directories will be displayed.' cand --only-dir 'Only directories will be displayed.' cand -F 'Only files will be displayed. (Finds your largest files)' cand --only-file 'Only files will be displayed. (Finds your largest files)' - cand -k 'display the size of a file or directory in kilobytes' - cand --display-kb 'display the size of a file or directory in kilobytes' cand -h 'Print help' cand --help 'Print help' cand -V 'Print version' diff --git a/completions/dust.fish b/completions/dust.fish index a193a90a..5dbd22d6 100644 --- a/completions/dust.fish +++ b/completions/dust.fish @@ -6,6 +6,7 @@ complete -c dust -s z -l min-size -d 'Minimum size file to include in output' -r complete -c dust -s v -l invert-filter -d 'Exclude filepaths matching this regex. To ignore png files type: -v "\\.png$" ' -r complete -c dust -s e -l filter -d 'Only include filepaths matching this regex. For png files type: -e "\\.png$" ' -r complete -c dust -s w -l terminal_width -d 'Specify width of output overriding the auto detection of terminal width' -r +complete -c dust -s o -l output-format -d 'Changes output display size. si will print sizes in powers of 1000. b/bytes kb kib mb mib gb gib will print the whole tree in that size' -r complete -c dust -s S -l stack-size -d 'Specify memory to use as stack size - use if you see: \'fatal runtime error: stack overflow\' (default low memory=1048576, high memory=1073741824)' -r complete -c dust -s p -l full-paths -d 'Subdirectories will not have their path shortened' complete -c dust -s L -l dereference-links -d 'dereference sym links - Treat sym links as directories and go into them' @@ -20,10 +21,8 @@ complete -c dust -l skip-total -d 'No total row will be displayed' complete -c dust -s f -l filecount -d 'Directory \'size\' is number of child files instead of disk size' complete -c dust -s i -l ignore_hidden -d 'Do not display hidden files' complete -c dust -s t -l file_types -d 'show only these file types' -complete -c dust -s H -l si -d 'print sizes in powers of 1000 (e.g., 1.1G)' complete -c dust -s P -l no-progress -d 'Disable the progress indication.' complete -c dust -s D -l only-dir -d 'Only directories will be displayed.' complete -c dust -s F -l only-file -d 'Only files will be displayed. (Finds your largest files)' -complete -c dust -s k -l display-kb -d 'display the size of a file or directory in kilobytes' complete -c dust -s h -l help -d 'Print help' complete -c dust -s V -l version -d 'Print version' diff --git a/man-page/dust.1 b/man-page/dust.1 index a9f77fcd..e0320294 100644 --- a/man-page/dust.1 +++ b/man-page/dust.1 @@ -1,10 +1,10 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH Dust 1 "Dust 0.9.0" +.TH Dust 1 "Dust 0.9.0" .SH NAME Dust \- Like du but more intuitive .SH SYNOPSIS -\fBDust\fR [\fB\-d\fR|\fB\-\-depth\fR] [\fB\-n\fR|\fB\-\-number\-of\-lines\fR] [\fB\-p\fR|\fB\-\-full\-paths\fR] [\fB\-X\fR|\fB\-\-ignore\-directory\fR] [\fB\-I\fR|\fB\-\-ignore\-all\-in\-file\fR] [\fB\-L\fR|\fB\-\-dereference\-links\fR] [\fB\-x\fR|\fB\-\-limit\-filesystem\fR] [\fB\-s\fR|\fB\-\-apparent\-size\fR] [\fB\-r\fR|\fB\-\-reverse\fR] [\fB\-c\fR|\fB\-\-no\-colors\fR] [\fB\-b\fR|\fB\-\-no\-percent\-bars\fR] [\fB\-B\fR|\fB\-\-bars\-on\-right\fR] [\fB\-z\fR|\fB\-\-min\-size\fR] [\fB\-R\fR|\fB\-\-screen\-reader\fR] [\fB\-\-skip\-total\fR] [\fB\-f\fR|\fB\-\-filecount\fR] [\fB\-i\fR|\fB\-\-ignore_hidden\fR] [\fB\-v\fR|\fB\-\-invert\-filter\fR] [\fB\-e\fR|\fB\-\-filter\fR] [\fB\-t\fR|\fB\-\-file_types\fR] [\fB\-w\fR|\fB\-\-terminal_width\fR] [\fB\-H\fR|\fB\-\-si\fR] [\fB\-P\fR|\fB\-\-no\-progress\fR] [\fB\-D\fR|\fB\-\-only\-dir\fR] [\fB\-F\fR|\fB\-\-only\-file\fR] [\fB\-k\fR|\fB\-\-display\-kb\fR] [\fB\-S\fR|\fB\-\-stack\-size\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIparams\fR] +\fBdust\fR [\fB\-d\fR|\fB\-\-depth\fR] [\fB\-n\fR|\fB\-\-number\-of\-lines\fR] [\fB\-p\fR|\fB\-\-full\-paths\fR] [\fB\-X\fR|\fB\-\-ignore\-directory\fR] [\fB\-I\fR|\fB\-\-ignore\-all\-in\-file\fR] [\fB\-L\fR|\fB\-\-dereference\-links\fR] [\fB\-x\fR|\fB\-\-limit\-filesystem\fR] [\fB\-s\fR|\fB\-\-apparent\-size\fR] [\fB\-r\fR|\fB\-\-reverse\fR] [\fB\-c\fR|\fB\-\-no\-colors\fR] [\fB\-b\fR|\fB\-\-no\-percent\-bars\fR] [\fB\-B\fR|\fB\-\-bars\-on\-right\fR] [\fB\-z\fR|\fB\-\-min\-size\fR] [\fB\-R\fR|\fB\-\-screen\-reader\fR] [\fB\-\-skip\-total\fR] [\fB\-f\fR|\fB\-\-filecount\fR] [\fB\-i\fR|\fB\-\-ignore_hidden\fR] [\fB\-v\fR|\fB\-\-invert\-filter\fR] [\fB\-e\fR|\fB\-\-filter\fR] [\fB\-t\fR|\fB\-\-file_types\fR] [\fB\-w\fR|\fB\-\-terminal_width\fR] [\fB\-P\fR|\fB\-\-no\-progress\fR] [\fB\-D\fR|\fB\-\-only\-dir\fR] [\fB\-F\fR|\fB\-\-only\-file\fR] [\fB\-o\fR|\fB\-\-output\-format\fR] [\fB\-S\fR|\fB\-\-stack\-size\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIparams\fR] .SH DESCRIPTION Like du but more intuitive .SH OPTIONS @@ -61,10 +61,10 @@ Directory \*(Aqsize\*(Aq is number of child files instead of disk size Do not display hidden files .TP \fB\-v\fR, \fB\-\-invert\-filter\fR -Exclude filepaths matching this regex. To ignore png files type: \-v "\\.png$" +Exclude filepaths matching this regex. To ignore png files type: \-v "\\.png$" .TP \fB\-e\fR, \fB\-\-filter\fR -Only include filepaths matching this regex. For png files type: \-e "\\.png$" +Only include filepaths matching this regex. For png files type: \-e "\\.png$" .TP \fB\-t\fR, \fB\-\-file_types\fR show only these file types @@ -72,9 +72,6 @@ show only these file types \fB\-w\fR, \fB\-\-terminal_width\fR Specify width of output overriding the auto detection of terminal width .TP -\fB\-H\fR, \fB\-\-si\fR -print sizes in powers of 1000 (e.g., 1.1G) -.TP \fB\-P\fR, \fB\-\-no\-progress\fR Disable the progress indication. .TP @@ -84,8 +81,8 @@ Only directories will be displayed. \fB\-F\fR, \fB\-\-only\-file\fR Only files will be displayed. (Finds your largest files) .TP -\fB\-k\fR, \fB\-\-display\-kb\fR -display the size of a file or directory in kilobytes +\fB\-o\fR, \fB\-\-output\-format\fR +Changes output display size. si will print sizes in powers of 1000. b/bytes kb kib mb mib gb gib will print the whole tree in that size .TP \fB\-S\fR, \fB\-\-stack\-size\fR Specify memory to use as stack size \- use if you see: \*(Aqfatal runtime error: stack overflow\*(Aq (default low memory=1048576, high memory=1073741824) diff --git a/src/cli.rs b/src/cli.rs index dd5d01b2..67a88f7f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -162,13 +162,6 @@ pub fn build_cli() -> Command { .value_parser(value_parser!(usize)) .help("Specify width of output overriding the auto detection of terminal width"), ) - .arg( - Arg::new("iso") - .short('H') - .long("si") - .action(clap::ArgAction::SetTrue) - .help("print sizes in powers of 1000 (e.g., 1.1G)") - ) .arg( Arg::new("disable_progress") .short('P') @@ -194,11 +187,11 @@ pub fn build_cli() -> Command { .help("Only files will be displayed. (Finds your largest files)"), ) .arg( - Arg::new("display_kb") - .short('k') - .long("display-kb") - .action(clap::ArgAction::SetTrue) - .help("display the size of a file or directory in kilobytes"), + Arg::new("output_format") + .short('o') + .long("output-format") + .value_parser(value_parser!(String)) + .help("Changes output display size. si will print sizes in powers of 1000. b/bytes kb kib mb mib gb gib will print the whole tree in that size") ) .arg( Arg::new("stack_size") diff --git a/src/config.rs b/src/config.rs index 3e058c22..d7fedb66 100644 --- a/src/config.rs +++ b/src/config.rs @@ -19,14 +19,13 @@ pub struct Config { pub skip_total: Option, pub screen_reader: Option, pub ignore_hidden: Option, - pub iso: Option, + pub output_format: Option, pub min_size: Option, pub only_dir: Option, pub only_file: Option, pub disable_progress: Option, pub depth: Option, pub bars_on_right: Option, - pub display_kb: Option, pub stack_size: Option, } @@ -57,8 +56,16 @@ impl Config { pub fn get_no_bars(&self, options: &ArgMatches) -> bool { Some(true) == self.no_bars || options.get_flag("no_bars") } - pub fn get_iso(&self, options: &ArgMatches) -> bool { - Some(true) == self.iso || options.get_flag("iso") + pub fn get_output_format(&self, options: &ArgMatches) -> String { + let out_fmt = options.get_one::("output_format"); + (match out_fmt { + None => match &self.output_format { + None => "".to_string(), + Some(x) => x.to_string(), + }, + Some(x) => x.into(), + }) + .to_lowercase() } pub fn get_skip_total(&self, options: &ArgMatches) -> bool { Some(true) == self.skip_total || options.get_flag("skip_total") @@ -73,17 +80,17 @@ impl Config { self.depth.unwrap_or(usize::MAX) } - pub fn get_min_size(&self, options: &ArgMatches, iso: bool) -> Option { + pub fn get_min_size(&self, options: &ArgMatches, output_format: &str) -> Option { let size_from_param = options.get_one::("min_size"); - self._get_min_size(size_from_param, iso) + self._get_min_size(size_from_param, output_format) } - fn _get_min_size(&self, min_size: Option<&String>, iso: bool) -> Option { - let size_from_param = min_size.and_then(|a| convert_min_size(a, iso)); + fn _get_min_size(&self, min_size: Option<&String>, output_format: &str) -> Option { + let size_from_param = min_size.and_then(|a| convert_min_size(a, output_format)); if size_from_param.is_none() { self.min_size .as_ref() - .and_then(|a| convert_min_size(a.as_ref(), iso)) + .and_then(|a| convert_min_size(a.as_ref(), output_format)) } else { size_from_param } @@ -105,12 +112,9 @@ impl Config { from_cmd_line.copied() } } - pub fn get_display_kb(&self, options: &ArgMatches) -> bool { - Some(true) == self.display_kb || options.get_flag("display_kb") - } } -fn convert_min_size(input: &str, iso: bool) -> Option { +fn convert_min_size(input: &str, output_format: &str) -> Option { let chars_as_vec: Vec = input.chars().collect(); match chars_as_vec.split_last() { Some((last, start)) => { @@ -120,7 +124,8 @@ fn convert_min_size(input: &str, iso: bool) -> Option { if Some(*u) == last.to_uppercase().next() { return match starts.parse::() { Ok(pure) => { - let num: usize = if iso { 1000 } else { 1024 }; + //fix + let num: usize = if output_format.eq("si") { 1000 } else { 1024 }; let marker = pure * num.pow((i + 1) as u32); Some(marker) } @@ -173,13 +178,13 @@ mod tests { #[test] fn test_conversion() { - assert_eq!(convert_min_size("55", false), Some(55)); - assert_eq!(convert_min_size("12344321", false), Some(12344321)); - assert_eq!(convert_min_size("95RUBBISH", false), None); - assert_eq!(convert_min_size("10K", false), Some(10 * 1024)); - assert_eq!(convert_min_size("10M", false), Some(10 * 1024usize.pow(2))); - assert_eq!(convert_min_size("10M", true), Some(10 * 1000usize.pow(2))); - assert_eq!(convert_min_size("2G", false), Some(2 * 1024usize.pow(3))); + assert_eq!(convert_min_size("55", ""), Some(55)); + assert_eq!(convert_min_size("12344321", ""), Some(12344321)); + assert_eq!(convert_min_size("95RUBBISH", ""), None); + assert_eq!(convert_min_size("10K", ""), Some(10 * 1024)); + assert_eq!(convert_min_size("10M", ""), Some(10 * 1024usize.pow(2))); + assert_eq!(convert_min_size("10M", "si"), Some(10 * 1000usize.pow(2))); + assert_eq!(convert_min_size("2G", ""), Some(2 * 1024usize.pow(3))); } #[test] @@ -188,11 +193,11 @@ mod tests { min_size: Some("1K".to_owned()), ..Default::default() }; - assert_eq!(c._get_min_size(None, false), Some(1024)); - assert_eq!(c._get_min_size(Some(&"2K".into()), false), Some(2048)); + assert_eq!(c._get_min_size(None, ""), Some(1024)); + assert_eq!(c._get_min_size(Some(&"2K".into()), ""), Some(2048)); - assert_eq!(c._get_min_size(None, true), Some(1000)); - assert_eq!(c._get_min_size(Some(&"2K".into()), true), Some(2000)); + assert_eq!(c._get_min_size(None, "si"), Some(1000)); + assert_eq!(c._get_min_size(Some(&"2K".into()), "si"), Some(2000)); } #[test] diff --git a/src/display.rs b/src/display.rs index 68bbb4dd..9f96741d 100644 --- a/src/display.rs +++ b/src/display.rs @@ -23,9 +23,8 @@ pub struct InitialDisplayData { pub colors_on: bool, pub by_filecount: bool, pub is_screen_reader: bool, - pub iso: bool, + pub output_format: String, pub bars_on_right: bool, - pub display_kb: bool, } pub struct DisplayData { @@ -130,7 +129,6 @@ pub fn draw_it( terminal_width: usize, root_node: &DisplayNode, skip_total: bool, - display_kb: bool, ) { let biggest = match skip_total { false => root_node, @@ -144,7 +142,7 @@ pub fn draw_it( let max_size = biggest.size; max_size.separate_with_commas().chars().count() } else { - find_biggest_size_str(root_node, idd.iso, display_kb) + find_biggest_size_str(root_node, &idd.output_format) }; assert!( @@ -192,12 +190,12 @@ pub fn draw_it( } } -fn find_biggest_size_str(node: &DisplayNode, iso: bool, display_kb: bool) -> usize { - let mut mx = human_readable_number(node.size, iso, display_kb) +fn find_biggest_size_str(node: &DisplayNode, output_format: &str) -> usize { + let mut mx = human_readable_number(node.size, output_format) .chars() .count(); for n in node.children.iter() { - mx = max(mx, find_biggest_size_str(n, iso, display_kb)); + mx = max(mx, find_biggest_size_str(n, output_format)); } mx } @@ -379,11 +377,7 @@ fn get_pretty_size(node: &DisplayNode, is_biggest: bool, display_data: &DisplayD let output = if display_data.initial.by_filecount { node.size.separate_with_commas() } else { - human_readable_number( - node.size, - display_data.initial.iso, - display_data.initial.display_kb, - ) + human_readable_number(node.size, &display_data.initial.output_format) }; let spaces_to_add = display_data.num_chars_needed_on_left_most - output.chars().count(); let output = " ".repeat(spaces_to_add) + output.as_str(); @@ -415,11 +409,20 @@ fn get_pretty_name( } } -pub fn human_readable_number(size: u64, iso: bool, display_kb: bool) -> String { - let num: u64 = if iso { 1000 } else { 1024 }; - if display_kb { - return format!("{:.1}{}", (size as f32 / num as f32), 'K'); +pub fn human_readable_number(size: u64, output_str: &str) -> String { + let is_si = output_str.contains('i'); // si, KiB, MiB, etc + let num: u64 = if is_si { 1000 } else { 1024 }; + + if output_str.starts_with('b') { + return format!("{}B", size); + } + for (i, u) in UNITS.iter().enumerate() { + if output_str.starts_with((*u).to_ascii_lowercase()) { + let marker = num.pow((UNITS.len() - i) as u32); + return format!("{}{}", (size / marker), u); + } } + for (i, u) in UNITS.iter().enumerate() { let marker = num.pow((UNITS.len() - i) as u32); if size >= marker { @@ -447,9 +450,8 @@ mod tests { colors_on: false, by_filecount: false, is_screen_reader: false, - iso: false, + output_format: "".into(), bars_on_right: false, - display_kb: false, }; DisplayData { initial, @@ -515,25 +517,37 @@ mod tests { #[test] fn test_human_readable_number() { - assert_eq!(human_readable_number(1, false, false), "1B"); - assert_eq!(human_readable_number(956, false, false), "956B"); - assert_eq!(human_readable_number(1004, false, false), "1004B"); - assert_eq!(human_readable_number(1024, false, false), "1.0K"); - assert_eq!(human_readable_number(1536, false, false), "1.5K"); - assert_eq!(human_readable_number(1024 * 512, false, false), "512K"); - assert_eq!(human_readable_number(1024 * 1024, false, false), "1.0M"); - assert_eq!( - human_readable_number(1024 * 1024 * 1024 - 1, false, false), - "1023M" - ); - assert_eq!( - human_readable_number(1024 * 1024 * 1024 * 20, false, false), - "20G" - ); - assert_eq!( - human_readable_number(1024 * 1024 * 1024 * 1024, false, false), - "1.0T" - ); + assert_eq!(human_readable_number(1, ""), "1B"); + assert_eq!(human_readable_number(956, ""), "956B"); + assert_eq!(human_readable_number(1004, ""), "1004B"); + assert_eq!(human_readable_number(1024, ""), "1.0K"); + assert_eq!(human_readable_number(1536, ""), "1.5K"); + assert_eq!(human_readable_number(1024 * 512, ""), "512K"); + assert_eq!(human_readable_number(1024 * 1024, ""), "1.0M"); + assert_eq!(human_readable_number(1024 * 1024 * 1024 - 1, ""), "1023M"); + assert_eq!(human_readable_number(1024 * 1024 * 1024 * 20, ""), "20G"); + assert_eq!(human_readable_number(1024 * 1024 * 1024 * 1024, ""), "1.0T"); + } + + #[test] + fn test_human_readable_number_si() { + assert_eq!(human_readable_number(1024 * 100, ""), "100K"); + assert_eq!(human_readable_number(1024 * 100, "si"), "102K"); + } + + #[test] + fn test_human_readable_number_kb() { + let hrn = human_readable_number; + assert_eq!(hrn(1023, "b"), "1023B"); + assert_eq!(hrn(1000 * 1000, "bytes"), "1000000B"); + assert_eq!(hrn(1023, "kb"), "0K"); + assert_eq!(hrn(1023, "kib"), "1K"); + assert_eq!(hrn(1024, "kb"), "1K"); + assert_eq!(hrn(1024 * 512, "kb"), "512K"); + assert_eq!(hrn(1024 * 1024, "kb"), "1024K"); + assert_eq!(hrn(1024 * 1000 * 1000 * 20, "kb"), "20000000K"); + assert_eq!(hrn(1024 * 1024 * 1000 * 20, "mb"), "20000M"); + assert_eq!(hrn(1024 * 1024 * 1024 * 20, "gb"), "20G"); } #[cfg(test)] @@ -589,16 +603,4 @@ mod tests { let bar = dd.generate_bar(&n, 5); assert_eq!(bar, "████▓▓▓▓▓▓▓▓▓"); } - #[test] - fn test_human_readable_number_kb() { - assert_eq!(human_readable_number(1, false, true), "0.0K"); - assert_eq!(human_readable_number(1024, false, true), "1.0K"); - assert_eq!(human_readable_number(1536, false, true), "1.5K"); - assert_eq!(human_readable_number(1024 * 512, false, true), "512.0K"); - assert_eq!(human_readable_number(1024 * 1024, false, true), "1024.0K"); - assert_eq!( - human_readable_number(1024 * 1000 * 1000 * 20, false, true), - "20000000.0K" - ); - } } diff --git a/src/main.rs b/src/main.rs index ac93139e..ec024e89 100644 --- a/src/main.rs +++ b/src/main.rs @@ -178,15 +178,13 @@ fn main() { .flat_map(|x| simplified_dirs.iter().map(move |d| d.join(&x))) .collect(); - let iso = config.get_iso(&options); - - let display_kb = config.get_display_kb(&options); + let output_format = config.get_output_format(&options); let ignore_hidden = config.get_ignore_hidden(&options); let mut indicator = PIndicator::build_me(); if !config.get_disable_progress(&options) { - indicator.spawn(iso, display_kb); + indicator.spawn(output_format.clone()) } let walk_data = WalkData { @@ -210,7 +208,7 @@ fn main() { true => get_all_file_types(&top_level_nodes, number_of_lines), false => { let agg_data = AggregateData { - min_size: config.get_min_size(&options, iso), + min_size: config.get_min_size(&options, &output_format), only_dir: config.get_only_dir(&options), only_file: config.get_only_file(&options), number_of_lines, @@ -254,10 +252,9 @@ fn main() { is_reversed: !config.get_reverse(&options), colors_on: !no_colors, by_filecount, - iso, is_screen_reader: config.get_screen_reader(&options), + output_format, bars_on_right: config.get_bars_on_right(&options), - display_kb, }; draw_it( idd, @@ -265,7 +262,6 @@ fn main() { terminal_width, &root_node, config.get_skip_total(&options), - display_kb, ) } } diff --git a/src/progress.rs b/src/progress.rs index afeee047..6d5b7bec 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -77,26 +77,16 @@ pub struct RuntimeErrors { /* -------------------------------------------------------------------------- */ -fn format_preparing_str( - prog_char: char, - data: &PAtomicInfo, - is_iso: bool, - is_display_kb: bool, -) -> String { +fn format_preparing_str(prog_char: char, data: &PAtomicInfo, output_display: &str) -> String { let path_in = data.current_path.get(); - let size = human_readable_number(data.total_file_size.load(ORDERING), is_iso, is_display_kb); + let size = human_readable_number(data.total_file_size.load(ORDERING), output_display); format!("Preparing: {path_in} {size} ... {prog_char}") } -fn format_indexing_str( - prog_char: char, - data: &PAtomicInfo, - is_iso: bool, - display_kb: bool, -) -> String { +fn format_indexing_str(prog_char: char, data: &PAtomicInfo, output_display: &str) -> String { let path_in = data.current_path.get(); let file_count = data.num_files.load(ORDERING); - let size = human_readable_number(data.total_file_size.load(ORDERING), is_iso, display_kb); + let size = human_readable_number(data.total_file_size.load(ORDERING), output_display); let file_str = format!("{file_count} files, {size}"); format!("Indexing: {path_in} {file_str} ... {prog_char}") } @@ -116,7 +106,7 @@ impl PIndicator { } } - pub fn spawn(&mut self, is_iso: bool, display_kb: bool) { + pub fn spawn(&mut self, output_display: String) { let data = self.data.clone(); let (stop_handler, receiver) = mpsc::channel::<()>(); @@ -135,12 +125,8 @@ impl PIndicator { let prog_char = PROGRESS_CHARS[progress_char_i]; msg = match data.state.load(ORDERING) { - Operation::INDEXING => { - format_indexing_str(prog_char, &data, is_iso, display_kb) - } - Operation::PREPARING => { - format_preparing_str(prog_char, &data, is_iso, display_kb) - } + Operation::INDEXING => format_indexing_str(prog_char, &data, &output_display), + Operation::PREPARING => format_preparing_str(prog_char, &data, &output_display), _ => panic!("Unknown State"), }; From 5b4c1903fbf95a798b78bf56f7b3489c1d11a257 Mon Sep 17 00:00:00 2001 From: "andy.boot" Date: Sun, 25 Feb 2024 10:09:17 +0000 Subject: [PATCH 3/4] refactor: minimum-size Change so it ignores the 'si' flag of output. But allow it to work with kib/kb/mib/mb etc --- README.md | 2 +- src/config.rs | 70 ++++++++++++++++++++++++++++++--------------------- src/main.rs | 2 +- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 79da23fd..987afbac 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ Usage: dust -P (Disable the progress indicator) Usage: dust -R (For screen readers. Removes bars/symbols. Adds new column: depth level. (May want to use -p for full path too)) Usage: dust -S (Custom Stack size - Use if you see: 'fatal runtime error: stack overflow' (default allocation: low memory=1048576, high memory=1073741824)"), Usage: dust --skip-total (No total row will be displayed) -Usage: dust -z 4000000 (Exclude output files/directories below size 4MB) +Usage: dust -z 40000/30MB/20kib (Exclude output files/directories below size 40000 bytes / 30MB / 20KiB) ``` ## Config file diff --git a/src/config.rs b/src/config.rs index d7fedb66..fda83930 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,6 @@ use clap::ArgMatches; use config_file::FromConfigFile; +use regex::Regex; use serde::Deserialize; use std::io::IsTerminal; use std::path::Path; @@ -80,17 +81,17 @@ impl Config { self.depth.unwrap_or(usize::MAX) } - pub fn get_min_size(&self, options: &ArgMatches, output_format: &str) -> Option { + pub fn get_min_size(&self, options: &ArgMatches) -> Option { let size_from_param = options.get_one::("min_size"); - self._get_min_size(size_from_param, output_format) + self._get_min_size(size_from_param) } - fn _get_min_size(&self, min_size: Option<&String>, output_format: &str) -> Option { - let size_from_param = min_size.and_then(|a| convert_min_size(a, output_format)); + fn _get_min_size(&self, min_size: Option<&String>) -> Option { + let size_from_param = min_size.and_then(|a| convert_min_size(a)); if size_from_param.is_none() { self.min_size .as_ref() - .and_then(|a| convert_min_size(a.as_ref(), output_format)) + .and_then(|a| convert_min_size(a.as_ref())) } else { size_from_param } @@ -114,19 +115,26 @@ impl Config { } } -fn convert_min_size(input: &str, output_format: &str) -> Option { - let chars_as_vec: Vec = input.chars().collect(); - match chars_as_vec.split_last() { - Some((last, start)) => { - let mut starts: String = start.iter().collect::(); +fn convert_min_size(input: &str) -> Option { + // let chars_as_vec: Vec = input.chars().collect(); + let re = Regex::new(r"([0-9]+)(\w*)").unwrap(); + if let Some(cap) = re.captures(input) { + let (_, [digits, letters]) = cap.extract(); + let letters = letters.to_uppercase(); + let first = letters.chars().next(); + + // If we did specify a letter and it doesnt begin with 'b' + if first.is_some() && first != Some('b') { + // Are we using KB, MB, GB etc ? for (i, u) in UNITS.iter().rev().enumerate() { - if Some(*u) == last.to_uppercase().next() { - return match starts.parse::() { + if Some(*u) == first { + return match digits.parse::() { Ok(pure) => { - //fix - let num: usize = if output_format.eq("si") { 1000 } else { 1024 }; - let marker = pure * num.pow((i + 1) as u32); + let is_si = letters.contains('I'); // KiB, MiB, etc + let num: usize = if is_si { 1000 } else { 1024 }; + + let marker = pure * (num.pow((i + 1) as u32)); Some(marker) } Err(_) => { @@ -136,15 +144,19 @@ fn convert_min_size(input: &str, output_format: &str) -> Option { }; } } - starts.push(*last); - starts + eprintln!("Ignoring invalid min-size: {input}"); + None + // Else we are working with bytes + } else { + digits .parse() .map_err(|_| { eprintln!("Ignoring invalid min-size: {input}"); }) .ok() } - None => None, + } else { + None } } @@ -178,13 +190,13 @@ mod tests { #[test] fn test_conversion() { - assert_eq!(convert_min_size("55", ""), Some(55)); - assert_eq!(convert_min_size("12344321", ""), Some(12344321)); - assert_eq!(convert_min_size("95RUBBISH", ""), None); - assert_eq!(convert_min_size("10K", ""), Some(10 * 1024)); - assert_eq!(convert_min_size("10M", ""), Some(10 * 1024usize.pow(2))); - assert_eq!(convert_min_size("10M", "si"), Some(10 * 1000usize.pow(2))); - assert_eq!(convert_min_size("2G", ""), Some(2 * 1024usize.pow(3))); + assert_eq!(convert_min_size("55"), Some(55)); + assert_eq!(convert_min_size("12344321"), Some(12344321)); + assert_eq!(convert_min_size("95RUBBISH"), None); + assert_eq!(convert_min_size("10K"), Some(10 * 1024)); + assert_eq!(convert_min_size("10M"), Some(10 * 1024usize.pow(2))); + assert_eq!(convert_min_size("10MiB"), Some(10 * 1000usize.pow(2))); + assert_eq!(convert_min_size("2G"), Some(2 * 1024usize.pow(3))); } #[test] @@ -193,11 +205,11 @@ mod tests { min_size: Some("1K".to_owned()), ..Default::default() }; - assert_eq!(c._get_min_size(None, ""), Some(1024)); - assert_eq!(c._get_min_size(Some(&"2K".into()), ""), Some(2048)); + assert_eq!(c._get_min_size(None), Some(1024)); + assert_eq!(c._get_min_size(Some(&"2K".into())), Some(2048)); - assert_eq!(c._get_min_size(None, "si"), Some(1000)); - assert_eq!(c._get_min_size(Some(&"2K".into()), "si"), Some(2000)); + assert_eq!(c._get_min_size(Some(&"1kib".into())), Some(1000)); + assert_eq!(c._get_min_size(Some(&"2KiB".into())), Some(2000)); } #[test] diff --git a/src/main.rs b/src/main.rs index ec024e89..31abb8d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -208,7 +208,7 @@ fn main() { true => get_all_file_types(&top_level_nodes, number_of_lines), false => { let agg_data = AggregateData { - min_size: config.get_min_size(&options, &output_format), + min_size: config.get_min_size(&options), only_dir: config.get_only_dir(&options), only_file: config.get_only_file(&options), number_of_lines, From 37a488ed784da95011d7e8d729c74bc4de6fe92b Mon Sep 17 00:00:00 2001 From: "andy.boot" Date: Sun, 25 Feb 2024 11:39:23 +0000 Subject: [PATCH 4/4] refactor: minimum-size & output-format share code for handling kb/kib/mb/mib logic --- src/config.rs | 46 ++++++++++++++++------------------------------ src/display.rs | 43 ++++++++++++++++++++++++++++++------------- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/src/config.rs b/src/config.rs index fda83930..1346fffd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,7 +6,7 @@ use std::io::IsTerminal; use std::path::Path; use std::path::PathBuf; -use crate::display::UNITS; +use crate::display::get_number_format; #[derive(Deserialize, Default)] #[serde(rename_all = "kebab-case")] @@ -116,44 +116,30 @@ impl Config { } fn convert_min_size(input: &str) -> Option { - // let chars_as_vec: Vec = input.chars().collect(); let re = Regex::new(r"([0-9]+)(\w*)").unwrap(); if let Some(cap) = re.captures(input) { let (_, [digits, letters]) = cap.extract(); - let letters = letters.to_uppercase(); - let first = letters.chars().next(); - - // If we did specify a letter and it doesnt begin with 'b' - if first.is_some() && first != Some('b') { - // Are we using KB, MB, GB etc ? - for (i, u) in UNITS.iter().rev().enumerate() { - if Some(*u) == first { - return match digits.parse::() { - Ok(pure) => { - let is_si = letters.contains('I'); // KiB, MiB, etc - let num: usize = if is_si { 1000 } else { 1024 }; - - let marker = pure * (num.pow((i + 1) as u32)); - Some(marker) - } - Err(_) => { + + // Failure to parse should be impossible due to regex match + let digits_as_usize: Option = digits.parse().ok(); + + match digits_as_usize { + Some(parsed_digits) => { + let number_format = get_number_format(&letters.to_lowercase()); + match number_format { + Some((multiple, _)) => Some(parsed_digits * (multiple as usize)), + None => { + if letters.eq("") { + Some(parsed_digits) + } else { eprintln!("Ignoring invalid min-size: {input}"); None } - }; + } } } - eprintln!("Ignoring invalid min-size: {input}"); - None - // Else we are working with bytes - } else { - digits - .parse() - .map_err(|_| { - eprintln!("Ignoring invalid min-size: {input}"); - }) - .ok() + None => None, } } else { None diff --git a/src/display.rs b/src/display.rs index 9f96741d..de828c69 100644 --- a/src/display.rs +++ b/src/display.rs @@ -409,31 +409,48 @@ fn get_pretty_name( } } -pub fn human_readable_number(size: u64, output_str: &str) -> String { +// If we are working with SI units or not +pub fn get_type_of_thousand(output_str: &str) -> u64 { let is_si = output_str.contains('i'); // si, KiB, MiB, etc - let num: u64 = if is_si { 1000 } else { 1024 }; + if is_si { + 1000 + } else { + 1024 + } +} +pub fn get_number_format(output_str: &str) -> Option<(u64, char)> { if output_str.starts_with('b') { - return format!("{}B", size); + return Some((1, 'B')); } for (i, u) in UNITS.iter().enumerate() { if output_str.starts_with((*u).to_ascii_lowercase()) { - let marker = num.pow((UNITS.len() - i) as u32); - return format!("{}{}", (size / marker), u); + let marker = get_type_of_thousand(output_str).pow((UNITS.len() - i) as u32); + return Some((marker, *u)); } } + None +} - for (i, u) in UNITS.iter().enumerate() { - let marker = num.pow((UNITS.len() - i) as u32); - if size >= marker { - if size / marker < 10 { - return format!("{:.1}{}", (size as f32 / marker as f32), u); - } else { - return format!("{}{}", (size / marker), u); +pub fn human_readable_number(size: u64, output_str: &str) -> String { + match get_number_format(output_str) { + Some((x, u)) => { + format!("{}{}", (size / x), u) + } + None => { + for (i, u) in UNITS.iter().enumerate() { + let marker = get_type_of_thousand(output_str).pow((UNITS.len() - i) as u32); + if size >= marker { + if size / marker < 10 { + return format!("{:.1}{}", (size as f32 / marker as f32), u); + } else { + return format!("{}{}", (size / marker), u); + } + } } + format!("{size}B") } } - format!("{size}B") } mod tests {