mirror of
https://github.com/bootandy/dust.git
synced 2026-06-08 11:29:05 +03:00
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:
@@ -21,6 +21,8 @@ _dust() {
|
|||||||
'--number-of-lines=[Number of lines of output to show. (Default is terminal_height - 10)]: : ' \
|
'--number-of-lines=[Number of lines of output to show. (Default is terminal_height - 10)]: : ' \
|
||||||
'*-X+[Exclude any file or directory with this name]: : ' \
|
'*-X+[Exclude any file or directory with this name]: : ' \
|
||||||
'*--ignore-directory=[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)*-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$" ]: : ' \
|
'(-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$" ]: : ' \
|
'(-t --file_types)*-e+[Only include filepaths matching this regex. For png files type: -e "\\.png$" ]: : ' \
|
||||||
|
|||||||
@@ -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('--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('-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('--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('-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('--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$" ')
|
[CompletionResult]::new('-e', 'e', [CompletionResultType]::ParameterName, 'Only include filepaths matching this regex. For png files type: -e "\.png$" ')
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ _dust() {
|
|||||||
|
|
||||||
case "${cmd}" in
|
case "${cmd}" in
|
||||||
dust)
|
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
|
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
return 0
|
return 0
|
||||||
@@ -49,6 +49,14 @@ _dust() {
|
|||||||
COMPREPLY=($(compgen -f "${cur}"))
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
--min-size)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-z)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
--invert-filter)
|
--invert-filter)
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -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 --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 -X 'Exclude any file or directory with this name'
|
||||||
cand --ignore-directory '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 -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 --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$" '
|
cand -e 'Only include filepaths matching this regex. For png files type: -e "\.png$" '
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
complete -c dust -s d -l depth -d 'Depth to show' -r
|
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 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 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 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 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 w -l terminal_width -d 'Specify width of output overriding the auto detection of terminal width' -r
|
||||||
|
|||||||
@@ -64,6 +64,14 @@ pub fn build_cli() -> Command<'static> {
|
|||||||
.long("no-percent-bars")
|
.long("no-percent-bars")
|
||||||
.help("No percent bars or percentages will be displayed"),
|
.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(
|
||||||
Arg::new("skip_total")
|
Arg::new("skip_total")
|
||||||
.long("skip-total")
|
.long("skip-total")
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ use serde::Deserialize;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::display::UNITS;
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
@@ -16,6 +18,7 @@ pub struct Config {
|
|||||||
pub skip_total: Option<bool>,
|
pub skip_total: Option<bool>,
|
||||||
pub ignore_hidden: Option<bool>,
|
pub ignore_hidden: Option<bool>,
|
||||||
pub iso: Option<bool>,
|
pub iso: Option<bool>,
|
||||||
|
pub min_size: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -43,6 +46,46 @@ impl Config {
|
|||||||
pub fn get_skip_total(&self, options: &ArgMatches) -> bool {
|
pub fn get_skip_total(&self, options: &ArgMatches) -> bool {
|
||||||
Some(true) == self.skip_total || options.is_present("skip_total")
|
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> {
|
fn get_config_locations(base: &Path) -> Vec<PathBuf> {
|
||||||
@@ -66,3 +109,33 @@ pub fn get_config() -> Config {
|
|||||||
..Default::default()
|
..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
@@ -14,7 +14,7 @@ use std::iter::repeat;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use thousands::Separable;
|
use thousands::Separable;
|
||||||
|
|
||||||
static UNITS: [char; 4] = ['T', 'G', 'M', 'K'];
|
pub static UNITS: [char; 4] = ['T', 'G', 'M', 'K'];
|
||||||
static BLOCKS: [char; 5] = ['█', '▓', '▒', '░', ' '];
|
static BLOCKS: [char; 5] = ['█', '▓', '▒', '░', ' '];
|
||||||
|
|
||||||
pub struct DisplayData {
|
pub struct DisplayData {
|
||||||
|
|||||||
+8
-8
@@ -7,6 +7,7 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
pub fn get_biggest(
|
pub fn get_biggest(
|
||||||
top_level_nodes: Vec<Node>,
|
top_level_nodes: Vec<Node>,
|
||||||
|
min_size: Option<usize>,
|
||||||
n: usize,
|
n: usize,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
using_a_filter: bool,
|
using_a_filter: bool,
|
||||||
@@ -22,14 +23,14 @@ pub fn get_biggest(
|
|||||||
let mut allowed_nodes = HashSet::new();
|
let mut allowed_nodes = HashSet::new();
|
||||||
|
|
||||||
allowed_nodes.insert(root.name.as_path());
|
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 {
|
for _ in number_top_level_nodes..n {
|
||||||
let line = heap.pop();
|
let line = heap.pop();
|
||||||
match line {
|
match line {
|
||||||
Some(line) => {
|
Some(line) => {
|
||||||
allowed_nodes.insert(line.name.as_path());
|
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,
|
None => break,
|
||||||
}
|
}
|
||||||
@@ -39,17 +40,16 @@ pub fn get_biggest(
|
|||||||
|
|
||||||
fn add_children<'a>(
|
fn add_children<'a>(
|
||||||
using_a_filter: bool,
|
using_a_filter: bool,
|
||||||
|
min_size: Option<usize>,
|
||||||
file_or_folder: &'a Node,
|
file_or_folder: &'a Node,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
mut heap: BinaryHeap<&'a Node>,
|
mut heap: BinaryHeap<&'a Node>,
|
||||||
) -> BinaryHeap<&'a Node> {
|
) -> BinaryHeap<&'a Node> {
|
||||||
if depth > file_or_folder.depth {
|
if depth > file_or_folder.depth {
|
||||||
heap.extend(
|
heap.extend(file_or_folder.children.iter().filter(|c| match min_size {
|
||||||
file_or_folder
|
Some(ms) => c.size > ms as u64,
|
||||||
.children
|
None => !using_a_filter || c.name.is_file() || c.size > 0,
|
||||||
.iter()
|
}))
|
||||||
.filter(|c| !using_a_filter || c.name.is_file() || c.size > 0),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
heap
|
heap
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -91,7 +91,6 @@ fn get_regex_value(maybe_value: Option<Values>) -> Vec<Regex> {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let options = build_cli().get_matches();
|
let options = build_cli().get_matches();
|
||||||
|
|
||||||
let config = get_config();
|
let config = get_config();
|
||||||
|
|
||||||
let target_dirs = options
|
let target_dirs = options
|
||||||
@@ -161,12 +160,14 @@ fn main() {
|
|||||||
.build_global()
|
.build_global()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let iso = config.get_iso(&options);
|
||||||
let (top_level_nodes, has_errors) = walk_it(simplified_dirs, walk_data);
|
let (top_level_nodes, has_errors) = walk_it(simplified_dirs, walk_data);
|
||||||
|
|
||||||
let tree = match summarize_file_types {
|
let tree = match summarize_file_types {
|
||||||
true => get_all_file_types(&top_level_nodes, number_of_lines),
|
true => get_all_file_types(&top_level_nodes, number_of_lines),
|
||||||
false => get_biggest(
|
false => get_biggest(
|
||||||
top_level_nodes,
|
top_level_nodes,
|
||||||
|
config.get_min_size(&options, iso),
|
||||||
number_of_lines,
|
number_of_lines,
|
||||||
depth,
|
depth,
|
||||||
options.values_of("filter").is_some() || options.value_of("invert_filter").is_some(),
|
options.values_of("filter").is_some() || options.value_of("invert_filter").is_some(),
|
||||||
@@ -185,7 +186,7 @@ fn main() {
|
|||||||
terminal_width,
|
terminal_width,
|
||||||
by_filecount,
|
by_filecount,
|
||||||
&root_node,
|
&root_node,
|
||||||
config.get_iso(&options),
|
iso,
|
||||||
config.get_skip_total(&options),
|
config.get_skip_total(&options),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user