Feature: Add min-size parameter

Add often requested feature. '--min-size 50000' will only include files
above a size of 50kB
This commit is contained in:
andy.boot
2022-08-22 11:17:21 +01:00
parent f70f4b7e12
commit d6f9bb3c47
10 changed files with 109 additions and 12 deletions
+2
View File
@@ -21,6 +21,8 @@ _dust() {
'--number-of-lines=[Number of lines of output to show. (Default is terminal_height - 10)]: : ' \
'*-X+[Exclude any file or directory with this name]: : ' \
'*--ignore-directory=[Exclude any file or directory with this name]: : ' \
'-z+[Minimum size file to include in output]: : ' \
'--min-size=[Minimum size file to include in output]: : ' \
'(-e --filter -t --file_types)*-v+[Exclude filepaths matching this regex. To ignore png files type: -v "\\.png$" ]: : ' \
'(-e --filter -t --file_types)*--invert-filter=[Exclude filepaths matching this regex. To ignore png files type: -v "\\.png$" ]: : ' \
'(-t --file_types)*-e+[Only include filepaths matching this regex. For png files type: -e "\\.png$" ]: : ' \
+2
View File
@@ -27,6 +27,8 @@ Register-ArgumentCompleter -Native -CommandName 'dust' -ScriptBlock {
[CompletionResult]::new('--number-of-lines', 'number-of-lines', [CompletionResultType]::ParameterName, 'Number of lines of output to show. (Default is terminal_height - 10)')
[CompletionResult]::new('-X', 'X', [CompletionResultType]::ParameterName, 'Exclude any file or directory with this name')
[CompletionResult]::new('--ignore-directory', 'ignore-directory', [CompletionResultType]::ParameterName, 'Exclude any file or directory with this name')
[CompletionResult]::new('-z', 'z', [CompletionResultType]::ParameterName, 'Minimum size file to include in output')
[CompletionResult]::new('--min-size', 'min-size', [CompletionResultType]::ParameterName, 'Minimum size file to include in output')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Exclude filepaths matching this regex. To ignore png files type: -v "\.png$" ')
[CompletionResult]::new('--invert-filter', 'invert-filter', [CompletionResultType]::ParameterName, 'Exclude filepaths matching this regex. To ignore png files type: -v "\.png$" ')
[CompletionResult]::new('-e', 'e', [CompletionResultType]::ParameterName, 'Only include filepaths matching this regex. For png files type: -e "\.png$" ')
+9 -1
View File
@@ -19,7 +19,7 @@ _dust() {
case "${cmd}" in
dust)
opts="-h -V -d -n -p -X -x -s -r -c -b -f -i -v -e -t -w -H --help --version --depth --number-of-lines --full-paths --ignore-directory --limit-filesystem --apparent-size --reverse --no-colors --no-percent-bars --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --si <inputs>..."
opts="-h -V -d -n -p -X -x -s -r -c -b -z -f -i -v -e -t -w -H --help --version --depth --number-of-lines --full-paths --ignore-directory --limit-filesystem --apparent-size --reverse --no-colors --no-percent-bars --min-size --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --si <inputs>..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -49,6 +49,14 @@ _dust() {
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--min-size)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-z)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--invert-filter)
COMPREPLY=($(compgen -f "${cur}"))
return 0
+2
View File
@@ -24,6 +24,8 @@ set edit:completion:arg-completer[dust] = {|@words|
cand --number-of-lines 'Number of lines of output to show. (Default is terminal_height - 10)'
cand -X 'Exclude any file or directory with this name'
cand --ignore-directory 'Exclude any file or directory with this name'
cand -z 'Minimum size file to include in output'
cand --min-size 'Minimum size file to include in output'
cand -v 'Exclude filepaths matching this regex. To ignore png files type: -v "\.png$" '
cand --invert-filter 'Exclude filepaths matching this regex. To ignore png files type: -v "\.png$" '
cand -e 'Only include filepaths matching this regex. For png files type: -e "\.png$" '
+1
View File
@@ -1,6 +1,7 @@
complete -c dust -s d -l depth -d 'Depth to show' -r
complete -c dust -s n -l number-of-lines -d 'Number of lines of output to show. (Default is terminal_height - 10)' -r
complete -c dust -s X -l ignore-directory -d 'Exclude any file or directory with this name' -r
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
+8
View File
@@ -64,6 +64,14 @@ pub fn build_cli() -> Command<'static> {
.long("no-percent-bars")
.help("No percent bars or percentages will be displayed"),
)
.arg(
Arg::new("min_size")
.short('z')
.long("min-size")
.takes_value(true)
.number_of_values(1)
.help("Minimum size file to include in output"),
)
.arg(
Arg::new("skip_total")
.long("skip-total")
+73
View File
@@ -4,6 +4,8 @@ use serde::Deserialize;
use std::path::Path;
use std::path::PathBuf;
use crate::display::UNITS;
#[derive(Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)]
@@ -16,6 +18,7 @@ pub struct Config {
pub skip_total: Option<bool>,
pub ignore_hidden: Option<bool>,
pub iso: Option<bool>,
pub min_size: Option<String>,
}
impl Config {
@@ -43,6 +46,46 @@ impl Config {
pub fn get_skip_total(&self, options: &ArgMatches) -> bool {
Some(true) == self.skip_total || options.is_present("skip_total")
}
pub fn get_min_size(&self, options: &ArgMatches, iso: bool) -> Option<usize> {
let size_from_param = options.value_of("min_size");
self._get_min_size(size_from_param, iso)
}
fn _get_min_size(&self, min_size: Option<&str>, iso: bool) -> Option<usize> {
let size_from_param = min_size.and_then(|a| convert_min_size(a, iso));
if size_from_param.is_none() {
self.min_size
.as_ref()
.and_then(|a| convert_min_size(a.as_ref(), iso))
} else {
size_from_param
}
}
}
fn convert_min_size(input: &str, iso: bool) -> Option<usize> {
let chars_as_vec: Vec<char> = input.chars().collect();
match chars_as_vec.split_last() {
Some((last, start)) => {
let mut starts: String = start.iter().collect::<String>();
for (i, u) in UNITS.iter().enumerate() {
if Some(*u) == last.to_uppercase().next() {
return match starts.parse::<usize>() {
Ok(pure) => {
let num: usize = if iso { 1000 } else { 1024 };
let marker = pure * num.pow((UNITS.len() - i) as u32);
Some(marker)
}
Err(_) => None,
};
}
}
starts.push(*last);
starts.parse().ok()
}
None => None,
}
}
fn get_config_locations(base: &Path) -> Vec<PathBuf> {
@@ -66,3 +109,33 @@ pub fn get_config() -> Config {
..Default::default()
}
}
mod tests {
#[allow(unused_imports)]
use super::*;
#[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)));
}
#[test]
fn test_config_min_size() {
let c = Config {
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("100"), false), Some(100));
assert_eq!(c._get_min_size(Some("2K"), false), Some(2048));
assert_eq!(c._get_min_size(None, true), Some(1000));
assert_eq!(c._get_min_size(Some("2K"), true), Some(2000));
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ use std::iter::repeat;
use std::path::Path;
use thousands::Separable;
static UNITS: [char; 4] = ['T', 'G', 'M', 'K'];
pub static UNITS: [char; 4] = ['T', 'G', 'M', 'K'];
static BLOCKS: [char; 5] = ['█', '▓', '▒', '░', ' '];
pub struct DisplayData {
+8 -8
View File
@@ -7,6 +7,7 @@ use std::path::PathBuf;
pub fn get_biggest(
top_level_nodes: Vec<Node>,
min_size: Option<usize>,
n: usize,
depth: usize,
using_a_filter: bool,
@@ -22,14 +23,14 @@ pub fn get_biggest(
let mut allowed_nodes = HashSet::new();
allowed_nodes.insert(root.name.as_path());
heap = add_children(using_a_filter, &root, depth, heap);
heap = add_children(using_a_filter, min_size, &root, depth, heap);
for _ in number_top_level_nodes..n {
let line = heap.pop();
match line {
Some(line) => {
allowed_nodes.insert(line.name.as_path());
heap = add_children(using_a_filter, line, depth, heap);
heap = add_children(using_a_filter, min_size, line, depth, heap);
}
None => break,
}
@@ -39,17 +40,16 @@ pub fn get_biggest(
fn add_children<'a>(
using_a_filter: bool,
min_size: Option<usize>,
file_or_folder: &'a Node,
depth: usize,
mut heap: BinaryHeap<&'a Node>,
) -> BinaryHeap<&'a Node> {
if depth > file_or_folder.depth {
heap.extend(
file_or_folder
.children
.iter()
.filter(|c| !using_a_filter || c.name.is_file() || c.size > 0),
)
heap.extend(file_or_folder.children.iter().filter(|c| match min_size {
Some(ms) => c.size > ms as u64,
None => !using_a_filter || c.name.is_file() || c.size > 0,
}))
}
heap
}
+3 -2
View File
@@ -91,7 +91,6 @@ fn get_regex_value(maybe_value: Option<Values>) -> Vec<Regex> {
fn main() {
let options = build_cli().get_matches();
let config = get_config();
let target_dirs = options
@@ -161,12 +160,14 @@ fn main() {
.build_global()
.unwrap();
let iso = config.get_iso(&options);
let (top_level_nodes, has_errors) = walk_it(simplified_dirs, walk_data);
let tree = match summarize_file_types {
true => get_all_file_types(&top_level_nodes, number_of_lines),
false => get_biggest(
top_level_nodes,
config.get_min_size(&options, iso),
number_of_lines,
depth,
options.values_of("filter").is_some() || options.value_of("invert_filter").is_some(),
@@ -185,7 +186,7 @@ fn main() {
terminal_width,
by_filecount,
&root_node,
config.get_iso(&options),
iso,
config.get_skip_total(&options),
)
}