diff --git a/Cargo.lock b/Cargo.lock index 5c3c028..edfe591 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,6 +178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -201,6 +202,18 @@ dependencies = [ "clap", ] +[[package]] +name = "clap_derive" +version = "4.5.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "clap_lex" version = "0.7.4" @@ -390,6 +403,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 4ceb6d2..2df0154 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ strip = true [dependencies] ansi_term = "0.12" -clap = "4.4" +clap = { version = "4.4", features = ["derive"] } lscolors = "0.13" terminal_size = "0.2" unicode-width = "0.1" @@ -56,7 +56,7 @@ assert_cmd = "2" tempfile = "=3" [build-dependencies] -clap = "4.4" +clap = { version = "4.4", features = ["derive"] } clap_complete = "4.4" clap_mangen = "0.2" diff --git a/build.rs b/build.rs index 86e8f7c..bfeeeee 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,4 @@ +use clap::CommandFactory; use clap_complete::{generate_to, shells::*}; use clap_mangen::Man; use std::fs::File; @@ -9,7 +10,7 @@ include!("src/cli.rs"); fn main() -> Result<(), Error> { let outdir = "completions"; let app_name = "dust"; - let mut cmd = build_cli(); + let mut cmd = Cli::command(); generate_to(Bash, &mut cmd, app_name, outdir)?; generate_to(Zsh, &mut cmd, app_name, outdir)?; diff --git a/completions/_dust b/completions/_dust index 6555352..93cc156 100644 --- a/completions/_dust +++ b/completions/_dust @@ -17,8 +17,8 @@ _dust() { _arguments "${_arguments_options[@]}" : \ '-d+[Depth to show]:DEPTH:_default' \ '--depth=[Depth to show]:DEPTH:_default' \ -'-T+[Number of threads to use]: :_default' \ -'--threads=[Number of threads to use]: :_default' \ +'-T+[Number of threads to use]:THREADS:_default' \ +'--threads=[Number of threads to use]:THREADS:_default' \ '--config=[Specify a config file to use]:FILE:_files' \ '-n+[Number of lines of output to show. (Default is terminal_height - 10)]:NUMBER:_default' \ '--number-of-lines=[Number of lines of output to show. (Default is terminal_height - 10)]:NUMBER:_default' \ @@ -28,26 +28,48 @@ _dust() { '--ignore-all-in-file=[Exclude any file or directory with a regex matching that listed in this file, the file entries will be added to the ignore regexs provided by --invert_filter]:FILE:_files' \ '-z+[Minimum size file to include in output]:MIN_SIZE:_default' \ '--min-size=[Minimum size file to include in output]:MIN_SIZE:_default' \ -'(-e --filter -t --file-types)*-v+[Exclude filepaths matching this regex. To ignore png files type\: -v "\\.png\$" ]:REGEX:_default' \ -'(-e --filter -t --file-types)*--invert-filter=[Exclude filepaths matching this regex. To ignore png files type\: -v "\\.png\$" ]:REGEX:_default' \ -'(-t --file-types)*-e+[Only include filepaths matching this regex. For png files type\: -e "\\.png\$" ]:REGEX:_default' \ -'(-t --file-types)*--filter=[Only include filepaths matching this regex. For png files type\: -e "\\.png\$" ]:REGEX:_default' \ +'(-e --filter -t --file-types)*-v+[Exclude filepaths matching this regex. To ignore png files type\: -v "\\.png\$"]:REGEX:_default' \ +'(-e --filter -t --file-types)*--invert-filter=[Exclude filepaths matching this regex. To ignore png files type\: -v "\\.png\$"]:REGEX:_default' \ +'(-t --file-types)*-e+[Only include filepaths matching this regex. For png files type\: -e "\\.png\$"]:REGEX:_default' \ +'(-t --file-types)*--filter=[Only include filepaths matching this regex. For png files type\: -e "\\.png\$"]:REGEX:_default' \ '-w+[Specify width of output overriding the auto detection of terminal width]:WIDTH:_default' \ '--terminal-width=[Specify width of output overriding the auto detection of terminal width]:WIDTH:_default' \ -'-o+[Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb will print the whole tree in that size.]:FORMAT:(si b k m g t kb mb gb tb)' \ -'--output-format=[Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb will print the whole tree in that size.]:FORMAT:(si b k m g t kb mb gb tb)' \ +'-o+[Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb will print the whole tree in that size]:FORMAT:((si\:"SI prefix (powers of 1000)" +b\:"byte (B)" +k\:"kibibyte (KiB)" +m\:"mebibyte (MiB)" +g\:"gibibyte (GiB)" +t\:"tebibyte (TiB)" +kb\:"kilobyte (kB)" +mb\:"megabyte (MB)" +gb\:"gigabyte (GB)" +tb\:"terabyte (TB)"))' \ +'--output-format=[Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb will print the whole tree in that size]:FORMAT:((si\:"SI prefix (powers of 1000)" +b\:"byte (B)" +k\:"kibibyte (KiB)" +m\:"mebibyte (MiB)" +g\:"gibibyte (GiB)" +t\:"tebibyte (TiB)" +kb\:"kilobyte (kB)" +mb\:"megabyte (MB)" +gb\:"gigabyte (GB)" +tb\:"terabyte (TB)"))' \ '-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:_default' \ '--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)]:STACK_SIZE:_default' \ -'-M+[+/-n matches files modified more/less than n days ago , and n matches files modified exactly n days ago, days are rounded down.That is +n => (−∞, curr−(n+1)), n => \[curr−(n+1), curr−n), and -n => (𝑐𝑢𝑟𝑟−𝑛, +∞)]: :_default' \ -'--mtime=[+/-n matches files modified more/less than n days ago , and n matches files modified exactly n days ago, days are rounded down.That is +n => (−∞, curr−(n+1)), n => \[curr−(n+1), curr−n), and -n => (𝑐𝑢𝑟𝑟−𝑛, +∞)]: :_default' \ -'-A+[just like -mtime, but based on file access time]: :_default' \ -'--atime=[just like -mtime, but based on file access time]: :_default' \ -'-y+[just like -mtime, but based on file change time]: :_default' \ -'--ctime=[just like -mtime, but based on file change time]: :_default' \ -'--files0-from=[run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input]: :_files' \ -'*--collapse=[Keep these directories collapsed]: :_files' \ -'-m+[Directory '\''size'\'' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time]: :(a c m)' \ -'--filetime=[Directory '\''size'\'' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time]: :(a c m)' \ +'-M+[+/-n matches files modified more/less than n days ago , and n matches files modified exactly n days ago, days are rounded down.That is +n => (−∞, curr−(n+1)), n => \[curr−(n+1), curr−n), and -n => (𝑐𝑢𝑟𝑟−𝑛, +∞)]:MTIME:_default' \ +'--mtime=[+/-n matches files modified more/less than n days ago , and n matches files modified exactly n days ago, days are rounded down.That is +n => (−∞, curr−(n+1)), n => \[curr−(n+1), curr−n), and -n => (𝑐𝑢𝑟𝑟−𝑛, +∞)]:MTIME:_default' \ +'-A+[just like -mtime, but based on file access time]:ATIME:_default' \ +'--atime=[just like -mtime, but based on file access time]:ATIME:_default' \ +'-y+[just like -mtime, but based on file change time]:CTIME:_default' \ +'--ctime=[just like -mtime, but based on file change time]:CTIME:_default' \ +'--files0-from=[run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input]:FILES0_FROM:_files' \ +'*--collapse=[Keep these directories collapsed]:COLLAPSE:_files' \ +'-m+[Directory '\''size'\'' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time]:FILETIME:((a\:"last accessed time" +c\:"last changed time" +m\:"last modified time"))' \ +'--filetime=[Directory '\''size'\'' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time]:FILETIME:((a\:"last accessed time" +c\:"last changed time" +m\:"last modified time"))' \ '-p[Subdirectories will not have their path shortened]' \ '--full-paths[Subdirectories will not have their path shortened]' \ '-L[dereference sym links - Treat sym links as directories and go into them]' \ @@ -75,20 +97,20 @@ _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]' \ -'-P[Disable the progress indication.]' \ -'--no-progress[Disable the progress indication.]' \ -'--print-errors[Print path with errors.]' \ -'(-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.]' \ +'-P[Disable the progress indication]' \ +'--no-progress[Disable the progress indication]' \ +'--print-errors[Print path with errors]' \ +'(-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)]' \ '-j[Output the directory tree as json to the current directory]' \ '--output-json[Output the directory tree as json to the current directory]' \ -'-h[Print help]' \ -'--help[Print help]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ '-V[Print version]' \ '--version[Print version]' \ -'*::params:_files' \ +'*::params -- Input files or directories:_files' \ && ret=0 } diff --git a/completions/_dust.ps1 b/completions/_dust.ps1 index 71030cb..54f69c8 100644 --- a/completions/_dust.ps1 +++ b/completions/_dust.ps1 @@ -34,14 +34,14 @@ Register-ArgumentCompleter -Native -CommandName 'dust' -ScriptBlock { [CompletionResult]::new('--ignore-all-in-file', '--ignore-all-in-file', [CompletionResultType]::ParameterName, 'Exclude any file or directory with a regex matching that listed in this file, the file entries will be added to the ignore regexs provided by --invert_filter') [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$" ') - [CompletionResult]::new('--filter', '--filter', [CompletionResultType]::ParameterName, 'Only include filepaths matching this regex. For png files type: -e "\.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('-e', '-e', [CompletionResultType]::ParameterName, 'Only include filepaths matching this regex. For png files type: -e "\.png$"') + [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 k m g t kb mb gb tb 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 k m g t kb mb gb tb will print the whole tree in that size.') + [CompletionResult]::new('-o', '-o', [CompletionResultType]::ParameterName, 'Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb 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 k m g t kb mb gb tb 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('-M', '-M ', [CompletionResultType]::ParameterName, '+/-n matches files modified more/less than n days ago , and n matches files modified exactly n days ago, days are rounded down.That is +n => (−∞, curr−(n+1)), n => [curr−(n+1), curr−n), and -n => (𝑐𝑢𝑟𝑟−𝑛, +∞)') @@ -81,17 +81,17 @@ 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('-P', '-P ', [CompletionResultType]::ParameterName, 'Disable the progress indication.') - [CompletionResult]::new('--no-progress', '--no-progress', [CompletionResultType]::ParameterName, 'Disable the progress indication.') - [CompletionResult]::new('--print-errors', '--print-errors', [CompletionResultType]::ParameterName, 'Print path with errors.') - [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('-P', '-P ', [CompletionResultType]::ParameterName, 'Disable the progress indication') + [CompletionResult]::new('--no-progress', '--no-progress', [CompletionResultType]::ParameterName, 'Disable the progress indication') + [CompletionResult]::new('--print-errors', '--print-errors', [CompletionResultType]::ParameterName, 'Print path with errors') + [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('-j', '-j', [CompletionResultType]::ParameterName, 'Output the directory tree as json to the current directory') [CompletionResult]::new('--output-json', '--output-json', [CompletionResultType]::ParameterName, 'Output the directory tree as json to the current directory') - [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help') - [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help') + [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version') [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version') break diff --git a/completions/dust.elv b/completions/dust.elv index 0c81f15..43fb374 100644 --- a/completions/dust.elv +++ b/completions/dust.elv @@ -31,14 +31,14 @@ set edit:completion:arg-completer[dust] = {|@words| cand --ignore-all-in-file 'Exclude any file or directory with a regex matching that listed in this file, the file entries will be added to the ignore regexs provided by --invert_filter' 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$" ' - cand --filter 'Only include filepaths matching this regex. For png files type: -e "\.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 -e 'Only include filepaths matching this regex. For png files type: -e "\.png$"' + 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 k m g t kb mb gb tb will print the whole tree in that size.' - cand --output-format 'Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb will print the whole tree in that size.' + cand -o 'Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb will print the whole tree in that size' + cand --output-format 'Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb 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 -M '+/-n matches files modified more/less than n days ago , and n matches files modified exactly n days ago, days are rounded down.That is +n => (−∞, curr−(n+1)), n => [curr−(n+1), curr−n), and -n => (𝑐𝑢𝑟𝑟−𝑛, +∞)' @@ -78,17 +78,17 @@ 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 -P 'Disable the progress indication.' - cand --no-progress 'Disable the progress indication.' - cand --print-errors 'Print path with errors.' - cand -D 'Only directories will be displayed.' - cand --only-dir 'Only directories will be displayed.' + cand -P 'Disable the progress indication' + cand --no-progress 'Disable the progress indication' + cand --print-errors 'Print path with errors' + 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 -j 'Output the directory tree as json to the current directory' cand --output-json 'Output the directory tree as json to the current directory' - cand -h 'Print help' - cand --help 'Print help' + cand -h 'Print help (see more with ''--help'')' + cand --help 'Print help (see more with ''--help'')' cand -V 'Print version' cand --version 'Print version' } diff --git a/completions/dust.fish b/completions/dust.fish index ba8dcf9..cef0b98 100644 --- a/completions/dust.fish +++ b/completions/dust.fish @@ -5,28 +5,28 @@ complete -c dust -s n -l number-of-lines -d 'Number of lines of output to show. complete -c dust -s X -l ignore-directory -d 'Exclude any file or directory with this path' -r -F complete -c dust -s I -l ignore-all-in-file -d 'Exclude any file or directory with a regex matching that listed in this file, the file entries will be added to the ignore regexs provided by --invert_filter' -r -F 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 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 k m g t kb mb gb tb will print the whole tree in that size.' -r -f -a "si\t'' -b\t'' -k\t'' -m\t'' -g\t'' -t\t'' -kb\t'' -mb\t'' -gb\t'' -tb\t''" +complete -c dust -s o -l output-format -d 'Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb will print the whole tree in that size' -r -f -a "si\t'SI prefix (powers of 1000)' +b\t'byte (B)' +k\t'kibibyte (KiB)' +m\t'mebibyte (MiB)' +g\t'gibibyte (GiB)' +t\t'tebibyte (TiB)' +kb\t'kilobyte (kB)' +mb\t'megabyte (MB)' +gb\t'gigabyte (GB)' +tb\t'terabyte (TB)'" 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 M -l mtime -d '+/-n matches files modified more/less than n days ago , and n matches files modified exactly n days ago, days are rounded down.That is +n => (−∞, curr−(n+1)), n => [curr−(n+1), curr−n), and -n => (𝑐𝑢𝑟𝑟−𝑛, +∞)' -r complete -c dust -s A -l atime -d 'just like -mtime, but based on file access time' -r complete -c dust -s y -l ctime -d 'just like -mtime, but based on file change time' -r complete -c dust -l files0-from -d 'run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input' -r -F complete -c dust -l collapse -d 'Keep these directories collapsed' -r -F -complete -c dust -s m -l filetime -d 'Directory \'size\' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time' -r -f -a "a\t'' -c\t'' -m\t''" +complete -c dust -s m -l filetime -d 'Directory \'size\' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time' -r -f -a "a\t'last accessed time' +c\t'last changed time' +m\t'last modified time'" 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' complete -c dust -s x -l limit-filesystem -d 'Only count the files and directories on the same filesystem as the supplied directory' @@ -41,10 +41,10 @@ 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 P -l no-progress -d 'Disable the progress indication.' -complete -c dust -l print-errors -d 'Print path with errors.' -complete -c dust -s D -l only-dir -d 'Only directories will be displayed.' +complete -c dust -s P -l no-progress -d 'Disable the progress indication' +complete -c dust -l print-errors -d 'Print path with errors' +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 j -l output-json -d 'Output the directory tree as json to the current directory' -complete -c dust -s h -l help -d 'Print help' +complete -c dust -s h -l help -d 'Print help (see more with \'--help\')' complete -c dust -s V -l version -d 'Print version' diff --git a/man-page/dust.1 b/man-page/dust.1 index 62b9ac7..d10c575 100644 --- a/man-page/dust.1 +++ b/man-page/dust.1 @@ -12,7 +12,7 @@ Like du but more intuitive \fB\-d\fR, \fB\-\-depth\fR=\fIDEPTH\fR Depth to show .TP -\fB\-T\fR, \fB\-\-threads\fR +\fB\-T\fR, \fB\-\-threads\fR=\fITHREADS\fR Number of threads to use .TP \fB\-\-config\fR=\fIFILE\fR @@ -70,10 +70,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=\fIREGEX\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=\fIREGEX\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 @@ -82,23 +82,45 @@ show only these file types Specify width of output overriding the auto detection of terminal width .TP \fB\-P\fR, \fB\-\-no\-progress\fR -Disable the progress indication. +Disable the progress indication .TP \fB\-\-print\-errors\fR -Print path with errors. +Print path with errors .TP \fB\-D\fR, \fB\-\-only\-dir\fR -Only directories will be displayed. +Only directories will be displayed .TP \fB\-F\fR, \fB\-\-only\-file\fR Only files will be displayed. (Finds your largest files) .TP \fB\-o\fR, \fB\-\-output\-format\fR=\fIFORMAT\fR -Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb will print the whole tree in that size. +Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb will print the whole tree in that size .br .br -[\fIpossible values: \fRsi, b, k, m, g, t, kb, mb, gb, tb] +\fIPossible values:\fR +.RS 14 +.IP \(bu 2 +si: SI prefix (powers of 1000) +.IP \(bu 2 +b: byte (B) +.IP \(bu 2 +k: kibibyte (KiB) +.IP \(bu 2 +m: mebibyte (MiB) +.IP \(bu 2 +g: gibibyte (GiB) +.IP \(bu 2 +t: tebibyte (TiB) +.IP \(bu 2 +kb: kilobyte (kB) +.IP \(bu 2 +mb: megabyte (MB) +.IP \(bu 2 +gb: gigabyte (GB) +.IP \(bu 2 +tb: terabyte (TB) +.RE .TP \fB\-S\fR, \fB\-\-stack\-size\fR=\fISTACK_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) @@ -106,35 +128,43 @@ Specify memory to use as stack size \- use if you see: \*(Aqfatal runtime error: \fB\-j\fR, \fB\-\-output\-json\fR Output the directory tree as json to the current directory .TP -\fB\-M\fR, \fB\-\-mtime\fR +\fB\-M\fR, \fB\-\-mtime\fR=\fIMTIME\fR +/\-n matches files modified more/less than n days ago , and n matches files modified exactly n days ago, days are rounded down.That is +n => (−∞, curr−(n+1)), n => [curr−(n+1), curr−n), and \-n => (𝑐𝑢𝑟𝑟−𝑛, +∞) .TP -\fB\-A\fR, \fB\-\-atime\fR +\fB\-A\fR, \fB\-\-atime\fR=\fIATIME\fR just like \-mtime, but based on file access time .TP -\fB\-y\fR, \fB\-\-ctime\fR +\fB\-y\fR, \fB\-\-ctime\fR=\fICTIME\fR just like \-mtime, but based on file change time .TP -\fB\-\-files0\-from\fR +\fB\-\-files0\-from\fR=\fIFILES0_FROM\fR run dust on NUL\-terminated file names specified in file; if argument is \-, then read names from standard input .TP -\fB\-\-collapse\fR +\fB\-\-collapse\fR=\fICOLLAPSE\fR Keep these directories collapsed .TP -\fB\-m\fR, \fB\-\-filetime\fR +\fB\-m\fR, \fB\-\-filetime\fR=\fIFILETIME\fR Directory \*(Aqsize\*(Aq is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time .br .br -[\fIpossible values: \fRa, c, m] +\fIPossible values:\fR +.RS 14 +.IP \(bu 2 +a: last accessed time +.IP \(bu 2 +c: last changed time +.IP \(bu 2 +m: last modified time +.RE .TP \fB\-h\fR, \fB\-\-help\fR -Print help +Print help (see a summary with \*(Aq\-h\*(Aq) .TP \fB\-V\fR, \fB\-\-version\fR Print version .TP [\fIPATH\fR] - +Input files or directories .SH VERSION v1.2.0 diff --git a/src/cli.rs b/src/cli.rs index b3ba446..889d76d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,326 +1,258 @@ -use clap::{Arg, Command, builder::PossibleValue, value_parser}; +use std::fmt; + +use clap::{Parser, ValueEnum, ValueHint}; // For single thread mode set this variable on your command line: // export RAYON_NUM_THREADS=1 -pub fn build_cli() -> Command { - Command::new("Dust") - .about("Like du but more intuitive") - .version(env!("CARGO_PKG_VERSION")) - .arg( - Arg::new("depth") - .short('d') - .long("depth") - .value_name("DEPTH") - .value_parser(value_parser!(usize)) - .help("Depth to show") - .num_args(1) - ) - .arg( - Arg::new("threads") - .short('T') - .long("threads") - .value_parser(value_parser!(usize)) - .help("Number of threads to use") - .num_args(1) - ) - .arg( - Arg::new("config") - .long("config") - .help("Specify a config file to use") - .value_name("FILE") - .value_hint(clap::ValueHint::FilePath) - .value_parser(value_parser!(String)) - .num_args(1) - ) - .arg( - Arg::new("number_of_lines") - .short('n') - .long("number-of-lines") - .value_name("NUMBER") - .value_parser(value_parser!(usize)) - .help("Number of lines of output to show. (Default is terminal_height - 10)") - .num_args(1) - ) - .arg( - Arg::new("display_full_paths") - .short('p') - .long("full-paths") - .action(clap::ArgAction::SetTrue) - .help("Subdirectories will not have their path shortened"), - ) - .arg( - Arg::new("ignore_directory") - .short('X') - .long("ignore-directory") - .value_name("PATH") - .value_hint(clap::ValueHint::AnyPath) - .action(clap::ArgAction::Append) - .help("Exclude any file or directory with this path"), - ) - .arg( - Arg::new("ignore_all_in_file") - .short('I') - .long("ignore-all-in-file") - .value_name("FILE") - .value_hint(clap::ValueHint::FilePath) - .value_parser(value_parser!(String)) - .help("Exclude any file or directory with a regex matching that listed in this file, the file entries will be added to the ignore regexs provided by --invert_filter"), - ) - .arg( - Arg::new("dereference_links") - .short('L') - .long("dereference-links") - .action(clap::ArgAction::SetTrue) - .help("dereference sym links - Treat sym links as directories and go into them"), - ) - .arg( - Arg::new("limit_filesystem") - .short('x') - .long("limit-filesystem") - .action(clap::ArgAction::SetTrue) - .help("Only count the files and directories on the same filesystem as the supplied directory"), - ) - .arg( - Arg::new("display_apparent_size") - .short('s') - .long("apparent-size") - .action(clap::ArgAction::SetTrue) - .help("Use file length instead of blocks"), - ) - .arg( - Arg::new("reverse") - .short('r') - .long("reverse") - .action(clap::ArgAction::SetTrue) - .help("Print tree upside down (biggest highest)"), - ) - .arg( - Arg::new("no_colors") - .short('c') - .long("no-colors") - .action(clap::ArgAction::SetTrue) - .help("No colors will be printed (Useful for commands like: watch)"), - ) - .arg( - Arg::new("force_colors") - .short('C') - .long("force-colors") - .action(clap::ArgAction::SetTrue) - .help("Force colors print"), - ) - .arg( - Arg::new("no_bars") - .short('b') - .long("no-percent-bars") - .action(clap::ArgAction::SetTrue) - .help("No percent bars or percentages will be displayed"), - ) - .arg( - Arg::new("bars_on_right") - .short('B') - .long("bars-on-right") - .action(clap::ArgAction::SetTrue) - .help("percent bars moved to right side of screen"), - ) - .arg( - Arg::new("min_size") - .short('z') - .long("min-size") - .value_name("MIN_SIZE") - .num_args(1) - .help("Minimum size file to include in output"), - ) - .arg( - Arg::new("screen_reader") - .short('R') - .long("screen-reader") - .action(clap::ArgAction::SetTrue) - .help("For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)"), - ) - .arg( - Arg::new("skip_total") - .long("skip-total") - .action(clap::ArgAction::SetTrue) - .help("No total row will be displayed"), - ) - .arg( - Arg::new("by_filecount") - .short('f') - .long("filecount") - .action(clap::ArgAction::SetTrue) - .help("Directory 'size' is number of child files instead of disk size"), - ) - .arg( - Arg::new("ignore_hidden") - .short('i') // Do not use 'h' this is used by 'help' - .long("ignore-hidden") - .action(clap::ArgAction::SetTrue) - .help("Do not display hidden files"), - ) - .arg( - Arg::new("invert_filter") - .short('v') - .long("invert-filter") - .value_name("REGEX") - .action(clap::ArgAction::Append) - .conflicts_with("filter") - .conflicts_with("types") - .help("Exclude filepaths matching this regex. To ignore png files type: -v \"\\.png$\" "), - ) - .arg( - Arg::new("filter") - .short('e') - .long("filter") - .value_name("REGEX") - .action(clap::ArgAction::Append) - .conflicts_with("types") - .help("Only include filepaths matching this regex. For png files type: -e \"\\.png$\" "), - ) - .arg( - Arg::new("types") - .short('t') - .long("file-types") - .conflicts_with("depth") - .conflicts_with("only_dir") - .action(clap::ArgAction::SetTrue) - .help("show only these file types"), - ) - .arg( - Arg::new("width") - .short('w') - .long("terminal-width") - .value_name("WIDTH") - .value_parser(value_parser!(usize)) - .num_args(1) - .help("Specify width of output overriding the auto detection of terminal width"), - ) - .arg( - Arg::new("disable_progress") - .short('P') - .long("no-progress") - .action(clap::ArgAction::SetTrue) - .help("Disable the progress indication."), - ) - .arg( - Arg::new("print_errors") - .long("print-errors") - .action(clap::ArgAction::SetTrue) - .help("Print path with errors."), - ) - .arg( - Arg::new("only_dir") - .short('D') - .long("only-dir") - .conflicts_with("only_file") - .conflicts_with("types") - .action(clap::ArgAction::SetTrue) - .help("Only directories will be displayed."), - ) - .arg( - Arg::new("only_file") - .short('F') - .long("only-file") - .conflicts_with("only_dir") - .action(clap::ArgAction::SetTrue) - .help("Only files will be displayed. (Finds your largest files)"), - ) - .arg( - Arg::new("output_format") - .short('o') - .long("output-format") - .value_name("FORMAT") - .value_parser([ - PossibleValue::new("si"), - PossibleValue::new("b"), - PossibleValue::new("k").alias("kib"), - PossibleValue::new("m").alias("mib"), - PossibleValue::new("g").alias("gib"), - PossibleValue::new("t").alias("tib"), - PossibleValue::new("kb"), - PossibleValue::new("mb"), - PossibleValue::new("gb"), - PossibleValue::new("tb"), - ]) - .ignore_case(true) - .help("Changes output display size. si will print sizes in powers of 1000. b k m g t kb mb gb tb will print the whole tree in that size.") - ) - .arg( - Arg::new("stack_size") - .short('S') - .long("stack-size") - .value_name("STACK_SIZE") - .value_parser(value_parser!(usize)) - .num_args(1) - .help("Specify memory to use as stack size - use if you see: 'fatal runtime error: stack overflow' (default low memory=1048576, high memory=1073741824)"), - ) - .arg( - Arg::new("params") - .value_name("PATH") - .value_hint(clap::ValueHint::AnyPath) - .value_parser(value_parser!(String)) - .num_args(1..) - ) - .arg( - Arg::new("output_json") - .short('j') - .long("output-json") - .action(clap::ArgAction::SetTrue) - .help("Output the directory tree as json to the current directory"), - ) - .arg( - Arg::new("mtime") - .short('M') - .long("mtime") - .num_args(1) - .allow_hyphen_values(true) - .value_parser(value_parser!(String)) - .help("+/-n matches files modified more/less than n days ago , and n matches files modified exactly n days ago, days are rounded down.That is +n => (−∞, curr−(n+1)), n => [curr−(n+1), curr−n), and -n => (𝑐𝑢𝑟𝑟−𝑛, +∞)") - ) - .arg( - Arg::new("atime") - .short('A') - .long("atime") - .num_args(1) - .allow_hyphen_values(true) - .value_parser(value_parser!(String)) - .help("just like -mtime, but based on file access time") - ) - .arg( - Arg::new("ctime") - .short('y') - .long("ctime") - .num_args(1) - .allow_hyphen_values(true) - .value_parser(value_parser!(String)) - .help("just like -mtime, but based on file change time") - ) - .arg( - Arg::new("files0_from") - .long("files0-from") - .value_hint(clap::ValueHint::AnyPath) - .value_parser(value_parser!(String)) - .num_args(1) - .help("run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input"), - ) - .arg( - Arg::new("collapse") - .long("collapse") - .value_hint(clap::ValueHint::AnyPath) - .value_parser(value_parser!(String)) - .action(clap::ArgAction::Append) - .help("Keep these directories collapsed"), - ) - .arg( - Arg::new("filetime") - .short('m') - .long("filetime") - .num_args(1) - .value_parser([ - PossibleValue::new("a").alias("accessed"), - PossibleValue::new("c").alias("changed"), - PossibleValue::new("m").alias("modified"), - ]) - .help("Directory 'size' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time"), - ) +/// Like du but more intuitive +#[derive(Debug, Parser)] +#[command(name("Dust"), version)] +pub struct Cli { + /// Depth to show + #[arg(short, long)] + pub depth: Option, + + /// Number of threads to use + #[arg(short('T'), long)] + pub threads: Option, + + /// Specify a config file to use + #[arg(long, value_name("FILE"), value_hint(ValueHint::FilePath))] + pub config: Option, + + /// Number of lines of output to show. (Default is terminal_height - 10) + #[arg(short, long, value_name("NUMBER"))] + pub number_of_lines: Option, + + /// Subdirectories will not have their path shortened + #[arg(short('p'), long)] + pub full_paths: bool, + + /// Exclude any file or directory with this path + #[arg(short('X'), long, value_name("PATH"), value_hint(ValueHint::AnyPath))] + pub ignore_directory: Option>, + + /// Exclude any file or directory with a regex matching that listed in this + /// file, the file entries will be added to the ignore regexs provided by + /// --invert_filter + #[arg(short('I'), long, value_name("FILE"), value_hint(ValueHint::FilePath))] + pub ignore_all_in_file: Option, + + /// dereference sym links - Treat sym links as directories and go into them + #[arg(short('L'), long)] + pub dereference_links: bool, + + /// Only count the files and directories on the same filesystem as the + /// supplied directory + #[arg(short('x'), long)] + pub limit_filesystem: bool, + + /// Use file length instead of blocks + #[arg(short('s'), long)] + pub apparent_size: bool, + + /// Print tree upside down (biggest highest) + #[arg(short, long)] + pub reverse: bool, + + /// No colors will be printed (Useful for commands like: watch) + #[arg(short('c'), long)] + pub no_colors: bool, + + /// Force colors print + #[arg(short('C'), long)] + pub force_colors: bool, + + /// No percent bars or percentages will be displayed + #[arg(short('b'), long)] + pub no_percent_bars: bool, + + /// percent bars moved to right side of screen + #[arg(short('B'), long)] + pub bars_on_right: bool, + + /// Minimum size file to include in output + #[arg(short('z'), long)] + pub min_size: Option, + + /// For screen readers. Removes bars. Adds new column: depth level (May want + /// to use -p too for full path) + #[arg(short('R'), long)] + pub screen_reader: bool, + + /// No total row will be displayed + #[arg(long)] + pub skip_total: bool, + + /// Directory 'size' is number of child files instead of disk size + #[arg(short, long)] + pub filecount: bool, + + /// Do not display hidden files + // Do not use 'h' this is used by 'help' + #[arg(short, long)] + pub ignore_hidden: bool, + + /// Exclude filepaths matching this regex. To ignore png files type: -v + /// "\.png$" + #[arg( + short('v'), + long, + value_name("REGEX"), + conflicts_with("filter"), + conflicts_with("file_types") + )] + pub invert_filter: Option>, + + /// Only include filepaths matching this regex. For png files type: -e + /// "\.png$" + #[arg(short('e'), long, value_name("REGEX"), conflicts_with("file_types"))] + pub filter: Option>, + + /// show only these file types + #[arg(short('t'), long, conflicts_with("depth"), conflicts_with("only_dir"))] + pub file_types: bool, + + /// Specify width of output overriding the auto detection of terminal width + #[arg(short('w'), long, value_name("WIDTH"))] + pub terminal_width: Option, + + /// Disable the progress indication. + #[arg(short('P'), long)] + pub no_progress: bool, + + /// Print path with errors. + #[arg(long)] + pub print_errors: bool, + + /// Only directories will be displayed. + #[arg( + short('D'), + long, + conflicts_with("only_file"), + conflicts_with("file_types") + )] + pub only_dir: bool, + + /// Only files will be displayed. (Finds your largest files) + #[arg(short('F'), long, conflicts_with("only_dir"))] + pub only_file: bool, + + /// Changes output display size. si will print sizes in powers of 1000. b k + /// m g t kb mb gb tb will print the whole tree in that size. + #[arg(short, long, value_enum, value_name("FORMAT"), ignore_case(true))] + pub output_format: Option, + + /// Specify memory to use as stack size - use if you see: 'fatal runtime + /// error: stack overflow' (default low memory=1048576, high + /// memory=1073741824) + #[arg(short('S'), long)] + pub stack_size: Option, + + /// Input files or directories. + #[arg(value_name("PATH"), value_hint(ValueHint::AnyPath))] + pub params: Option>, + + /// Output the directory tree as json to the current directory + #[arg(short('j'), long)] + pub output_json: bool, + + /// +/-n matches files modified more/less than n days ago , and n matches + /// files modified exactly n days ago, days are rounded down.That is +n => + /// (−∞, curr−(n+1)), n => [curr−(n+1), curr−n), and -n => (𝑐𝑢𝑟𝑟−𝑛, +∞) + #[arg(short('M'), long, allow_hyphen_values(true))] + pub mtime: Option, + + /// just like -mtime, but based on file access time + #[arg(short('A'), long, allow_hyphen_values(true))] + pub atime: Option, + + /// just like -mtime, but based on file change time + #[arg(short('y'), long, allow_hyphen_values(true))] + pub ctime: Option, + + /// run dust on NUL-terminated file names specified in file; if argument is + /// -, then read names from standard input + #[arg(long, value_hint(ValueHint::AnyPath))] + pub files0_from: Option, + + /// Keep these directories collapsed + #[arg(long, value_hint(ValueHint::AnyPath))] + pub collapse: Option>, + + /// Directory 'size' is max filetime of child files instead of disk size. + /// while a/c/m for last accessed/changed/modified time + #[arg(short('m'), long, value_enum)] + pub filetime: Option, +} + +#[derive(Clone, Copy, Debug, ValueEnum)] +#[value(rename_all = "lower")] +pub enum OutputFormat { + /// SI prefix (powers of 1000) + SI, + + /// byte (B) + B, + + /// kibibyte (KiB) + #[value(name = "k", alias("kib"))] + KiB, + + /// mebibyte (MiB) + #[value(name = "m", alias("mib"))] + MiB, + + /// gibibyte (GiB) + #[value(name = "g", alias("gib"))] + GiB, + + /// tebibyte (TiB) + #[value(name = "t", alias("tib"))] + TiB, + + /// kilobyte (kB) + KB, + + /// megabyte (MB) + MB, + + /// gigabyte (GB) + GB, + + /// terabyte (TB) + TB, +} + +impl fmt::Display for OutputFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::SI => write!(f, "si"), + Self::B => write!(f, "b"), + Self::KiB => write!(f, "k"), + Self::MiB => write!(f, "m"), + Self::GiB => write!(f, "g"), + Self::TiB => write!(f, "t"), + Self::KB => write!(f, "kb"), + Self::MB => write!(f, "mb"), + Self::GB => write!(f, "gb"), + Self::TB => write!(f, "tb"), + } + } +} + +#[derive(Clone, Copy, Debug, ValueEnum)] +pub enum FileTime { + /// last accessed time + #[value(name = "a", alias("accessed"))] + Accessed, + + /// last changed time + #[value(name = "c", alias("changed"))] + Changed, + + /// last modified time + #[value(name = "m", alias("modified"))] + Modified, } diff --git a/src/config.rs b/src/config.rs index 55cd57a..7005423 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,12 +1,12 @@ use crate::node::FileTime; use chrono::{Local, TimeZone}; -use clap::ArgMatches; use config_file::FromConfigFile; use regex::Regex; use serde::Deserialize; use std::path::Path; use std::path::PathBuf; +use crate::cli::Cli; use crate::dir_walker::Operator; use crate::display::get_number_format; @@ -39,77 +39,68 @@ pub struct Config { } impl Config { - pub fn get_files_from(&self, options: &ArgMatches) -> Option { - let from_file = options.get_one::("files0_from"); + pub fn get_files_from(&self, options: &Cli) -> Option { + let from_file = &options.files0_from; match from_file { None => self.files0_from.as_ref().map(|x| x.to_string()), Some(x) => Some(x.to_string()), } } - pub fn get_no_colors(&self, options: &ArgMatches) -> bool { - Some(true) == self.no_colors || options.get_flag("no_colors") + pub fn get_no_colors(&self, options: &Cli) -> bool { + Some(true) == self.no_colors || options.no_colors } - pub fn get_force_colors(&self, options: &ArgMatches) -> bool { - Some(true) == self.force_colors || options.get_flag("force_colors") + pub fn get_force_colors(&self, options: &Cli) -> bool { + Some(true) == self.force_colors || options.force_colors } - pub fn get_disable_progress(&self, options: &ArgMatches) -> bool { - Some(true) == self.disable_progress || options.get_flag("disable_progress") + pub fn get_disable_progress(&self, options: &Cli) -> bool { + Some(true) == self.disable_progress || options.no_progress } - pub fn get_apparent_size(&self, options: &ArgMatches) -> bool { - Some(true) == self.display_apparent_size || options.get_flag("display_apparent_size") + pub fn get_apparent_size(&self, options: &Cli) -> bool { + Some(true) == self.display_apparent_size || options.apparent_size } - pub fn get_ignore_hidden(&self, options: &ArgMatches) -> bool { - Some(true) == self.ignore_hidden || options.get_flag("ignore_hidden") + pub fn get_ignore_hidden(&self, options: &Cli) -> bool { + Some(true) == self.ignore_hidden || options.ignore_hidden } - pub fn get_full_paths(&self, options: &ArgMatches) -> bool { - Some(true) == self.display_full_paths || options.get_flag("display_full_paths") + pub fn get_full_paths(&self, options: &Cli) -> bool { + Some(true) == self.display_full_paths || options.full_paths } - pub fn get_reverse(&self, options: &ArgMatches) -> bool { - Some(true) == self.reverse || options.get_flag("reverse") + pub fn get_reverse(&self, options: &Cli) -> bool { + Some(true) == self.reverse || options.reverse } - pub fn get_no_bars(&self, options: &ArgMatches) -> bool { - Some(true) == self.no_bars || options.get_flag("no_bars") + pub fn get_no_bars(&self, options: &Cli) -> bool { + Some(true) == self.no_bars || options.no_percent_bars } - pub fn get_output_format(&self, options: &ArgMatches) -> String { - let out_fmt = options.get_one::("output_format"); + pub fn get_output_format(&self, options: &Cli) -> String { + let out_fmt = options.output_format; (match out_fmt { None => match &self.output_format { None => "".to_string(), Some(x) => x.to_string(), }, - Some(x) => x.into(), + Some(x) => x.to_string(), }) .to_lowercase() } - pub fn get_filetime(&self, options: &ArgMatches) -> Option { - let out_fmt = options.get_one::("filetime"); - match out_fmt { - None => None, - Some(x) => match x.as_str() { - "m" | "modified" => Some(FileTime::Modified), - "a" | "accessed" => Some(FileTime::Accessed), - "c" | "changed" => Some(FileTime::Changed), - _ => unreachable!(), - }, - } + pub fn get_filetime(&self, options: &Cli) -> Option { + options.filetime.map(FileTime::from) } - pub fn get_skip_total(&self, options: &ArgMatches) -> bool { - Some(true) == self.skip_total || options.get_flag("skip_total") + pub fn get_skip_total(&self, options: &Cli) -> bool { + Some(true) == self.skip_total || options.skip_total } - pub fn get_screen_reader(&self, options: &ArgMatches) -> bool { - Some(true) == self.screen_reader || options.get_flag("screen_reader") + pub fn get_screen_reader(&self, options: &Cli) -> bool { + Some(true) == self.screen_reader || options.screen_reader } - pub fn get_depth(&self, options: &ArgMatches) -> usize { - if let Some(v) = options.get_one::("depth") { - return *v; + pub fn get_depth(&self, options: &Cli) -> usize { + if let Some(v) = options.depth { + return v; } self.depth.unwrap_or(usize::MAX) } - pub fn get_min_size(&self, options: &ArgMatches) -> Option { - let size_from_param = options.get_one::("min_size"); + pub fn get_min_size(&self, options: &Cli) -> Option { + let size_from_param = options.min_size.as_ref(); self._get_min_size(size_from_param) } fn _get_min_size(&self, min_size: Option<&String>) -> Option { @@ -123,58 +114,49 @@ impl Config { size_from_param } } - pub fn get_only_dir(&self, options: &ArgMatches) -> bool { - Some(true) == self.only_dir || options.get_flag("only_dir") + pub fn get_only_dir(&self, options: &Cli) -> bool { + Some(true) == self.only_dir || options.only_dir } - pub fn get_print_errors(&self, options: &ArgMatches) -> bool { - Some(true) == self.print_errors || options.get_flag("print_errors") + pub fn get_print_errors(&self, options: &Cli) -> bool { + Some(true) == self.print_errors || options.print_errors } - pub fn get_only_file(&self, options: &ArgMatches) -> bool { - Some(true) == self.only_file || options.get_flag("only_file") + pub fn get_only_file(&self, options: &Cli) -> bool { + Some(true) == self.only_file || options.only_file } - pub fn get_bars_on_right(&self, options: &ArgMatches) -> bool { - Some(true) == self.bars_on_right || options.get_flag("bars_on_right") + pub fn get_bars_on_right(&self, options: &Cli) -> bool { + Some(true) == self.bars_on_right || options.bars_on_right } - pub fn get_custom_stack_size(&self, options: &ArgMatches) -> Option { - let from_cmd_line = options.get_one::("stack_size"); + pub fn get_custom_stack_size(&self, options: &Cli) -> Option { + let from_cmd_line = options.stack_size; if from_cmd_line.is_none() { self.stack_size } else { - from_cmd_line.copied() + from_cmd_line } } - pub fn get_threads(&self, options: &ArgMatches) -> Option { - let from_cmd_line = options.get_one::("threads"); + pub fn get_threads(&self, options: &Cli) -> Option { + let from_cmd_line = options.threads; if from_cmd_line.is_none() { self.threads } else { - from_cmd_line.copied() + from_cmd_line } } - pub fn get_output_json(&self, options: &ArgMatches) -> bool { - Some(true) == self.output_json || options.get_flag("output_json") + pub fn get_output_json(&self, options: &Cli) -> bool { + Some(true) == self.output_json || options.output_json } - pub fn get_modified_time_operator(&self, options: &ArgMatches) -> Option<(Operator, i64)> { - get_filter_time_operator( - options.get_one::("mtime"), - get_current_date_epoch_seconds(), - ) + pub fn get_modified_time_operator(&self, options: &Cli) -> Option<(Operator, i64)> { + get_filter_time_operator(options.mtime.as_ref(), get_current_date_epoch_seconds()) } - pub fn get_accessed_time_operator(&self, options: &ArgMatches) -> Option<(Operator, i64)> { - get_filter_time_operator( - options.get_one::("atime"), - get_current_date_epoch_seconds(), - ) + pub fn get_accessed_time_operator(&self, options: &Cli) -> Option<(Operator, i64)> { + get_filter_time_operator(options.atime.as_ref(), get_current_date_epoch_seconds()) } - pub fn get_changed_time_operator(&self, options: &ArgMatches) -> Option<(Operator, i64)> { - get_filter_time_operator( - options.get_one::("ctime"), - get_current_date_epoch_seconds(), - ) + pub fn get_changed_time_operator(&self, options: &Cli) -> Option<(Operator, i64)> { + get_filter_time_operator(options.ctime.as_ref(), get_current_date_epoch_seconds()) } } @@ -250,10 +232,10 @@ fn get_config_locations(base: &Path) -> Vec { ] } -pub fn get_config(conf_path: Option) -> Config { +pub fn get_config(conf_path: Option<&String>) -> Config { match conf_path { Some(path_str) => { - let path = Path::new(&path_str); + let path = Path::new(path_str); if path.exists() { match Config::from_config_file(path) { Ok(config) => return config, @@ -287,8 +269,7 @@ mod tests { #[allow(unused_imports)] use super::*; use chrono::{Datelike, Timelike}; - use clap::builder::PossibleValue; - use clap::{Arg, ArgMatches, Command, value_parser}; + use clap::Parser; #[test] fn test_get_current_date_epoch_seconds() { @@ -357,15 +338,8 @@ mod tests { assert_eq!(c.get_depth(&args), 5); } - fn get_args(args: Vec<&str>) -> ArgMatches { - Command::new("Dust") - .arg( - Arg::new("depth") - .long("depth") - .num_args(1) - .value_parser(value_parser!(usize)), - ) - .get_matches_from(args) + fn get_args(args: Vec<&str>) -> Cli { + Cli::parse_from(args) } #[test] @@ -403,20 +377,7 @@ mod tests { assert_eq!(c.get_filetime(&args), Some(FileTime::Changed)); } - fn get_filetime_args(args: Vec<&str>) -> ArgMatches { - Command::new("Dust") - .arg( - Arg::new("filetime") - .short('m') - .long("filetime") - .num_args(1) - .value_parser([ - PossibleValue::new("a").alias("accessed"), - PossibleValue::new("c").alias("changed"), - PossibleValue::new("m").alias("modified"), - ]) - .help("Directory 'size' is max filetime of child files instead of disk size. while a/c/m for accessed/changed/modified time"), - ) - .get_matches_from(args) + fn get_filetime_args(args: Vec<&str>) -> Cli { + Cli::parse_from(args) } } diff --git a/src/main.rs b/src/main.rs index 594cad1..01d022f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,9 +10,9 @@ mod platform; mod progress; mod utils; -use crate::cli::build_cli; +use crate::cli::Cli; use crate::progress::RuntimeErrors; -use clap::parser::ValuesRef; +use clap::Parser; use dir_walker::WalkData; use display::InitialDisplayData; use filter::AggregateData; @@ -98,9 +98,10 @@ fn get_width_of_terminal() -> usize { .unwrap_or(DEFAULT_TERMINAL_WIDTH) } -fn get_regex_value(maybe_value: Option>) -> Vec { +fn get_regex_value(maybe_value: Option<&Vec>) -> Vec { maybe_value - .unwrap_or_default() + .unwrap_or(&Vec::new()) + .iter() .map(|reg| { Regex::new(reg).unwrap_or_else(|err| { eprintln!("Ignoring bad value for regex {err:?}"); @@ -111,8 +112,8 @@ fn get_regex_value(maybe_value: Option>) -> Vec { } fn main() { - let options = build_cli().get_matches(); - let config = get_config(options.get_one::("config").cloned()); + let options = Cli::parse(); + let config = get_config(options.config.as_ref()); let errors = RuntimeErrors::default(); let error_listen_for_ctrlc = Arc::new(Mutex::new(errors)); @@ -148,19 +149,19 @@ fn main() { } } } - None => match options.get_many::("params") { - Some(values) => values.cloned().collect(), + None => match options.params { + Some(ref values) => values.clone(), None => vec![".".to_owned()], }, }; - let summarize_file_types = options.get_flag("types"); + let summarize_file_types = options.file_types; - let filter_regexs = get_regex_value(options.get_many("filter")); - let invert_filter_regexs = get_regex_value(options.get_many("invert_filter")); + let filter_regexs = get_regex_value(options.filter.as_ref()); + let invert_filter_regexs = get_regex_value(options.invert_filter.as_ref()); - let terminal_width: usize = match options.get_one::("width") { - Some(&val) => val, + let terminal_width: usize = match options.terminal_width { + Some(val) => val, None => get_width_of_terminal(), }; @@ -169,8 +170,8 @@ fn main() { // If depth is set, then we set the default number_of_lines to be max // instead of screen height - let number_of_lines = match options.get_one::("number_of_lines") { - Some(&val) => val, + let number_of_lines = match options.number_of_lines { + Some(val) => val, None => { if depth != usize::MAX { usize::MAX @@ -185,17 +186,17 @@ fn main() { config.get_force_colors(&options), ); - let ignore_directories = match options.get_many::("ignore_directory") { - Some(values) => values - .map(|v| v.as_str()) + let ignore_directories = match options.ignore_directory { + Some(ref values) => values + .iter() .map(PathBuf::from) .map(canonicalize_absolute_path) .collect::>(), None => vec![], }; - let ignore_from_file_result = match options.get_one::("ignore_all_in_file") { - Some(val) => read_to_string(val) + let ignore_from_file_result = match options.ignore_all_in_file { + Some(ref val) => read_to_string(val) .unwrap() .lines() .map(Regex::new) @@ -212,10 +213,10 @@ fn main() { .chain(ignore_from_file) .collect::>(); - let by_filecount = options.get_flag("by_filecount"); + let by_filecount = options.filecount; let by_filetime = config.get_filetime(&options); - let limit_filesystem = options.get_flag("limit_filesystem"); - let follow_links = options.get_flag("dereference_links"); + let limit_filesystem = options.limit_filesystem; + let follow_links = options.dereference_links; let allowed_filesystems = limit_filesystem .then(|| get_filesystem_devices(&target_dirs, follow_links)) @@ -236,8 +237,8 @@ fn main() { indicator.spawn(output_format.clone()) } - let keep_collapsed: HashSet = match options.get_many::("collapse") { - Some(collapse) => { + let keep_collapsed: HashSet = match options.collapse { + Some(ref collapse) => { let mut combined_dirs = HashSet::new(); for collapse_dir in collapse { for target_dir in target_dirs.iter() { diff --git a/src/node.rs b/src/node.rs index f1ff255..7af8e0a 100644 --- a/src/node.rs +++ b/src/node.rs @@ -23,6 +23,16 @@ pub enum FileTime { Changed, } +impl From for FileTime { + fn from(time: crate::cli::FileTime) -> Self { + match time { + crate::cli::FileTime::Modified => Self::Modified, + crate::cli::FileTime::Accessed => Self::Accessed, + crate::cli::FileTime::Changed => Self::Changed, + } + } +} + #[allow(clippy::too_many_arguments)] pub fn build_node( dir: PathBuf,