Merge pull request #929 from tmccombs/clap-upgrade

Upgrade to clap 3.0
This commit is contained in:
Thayne McCombs 2022-01-08 00:24:43 -07:00 committed by GitHub
commit 125cb81a5b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 228 additions and 175 deletions

View file

@ -1,7 +1,7 @@
name: CICD name: CICD
env: env:
MIN_SUPPORTED_RUST_VERSION: "1.53.0" MIN_SUPPORTED_RUST_VERSION: "1.54.0"
CICD_INTERMEDIATES_DIR: "_cicd-intermediates" CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
on: on:

104
Cargo.lock generated
View file

@ -85,18 +85,28 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.34.0" version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" checksum = "f6f34b09b9ee8c7c7b400fe2f8df39cafc9538b03d6ba7f4ae13e4cb90bfbb7d"
dependencies = [ dependencies = [
"ansi_term",
"atty", "atty",
"bitflags", "bitflags",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim", "strsim",
"term_size", "termcolor",
"terminal_size",
"textwrap", "textwrap",
"unicode-width", ]
"vec_map",
[[package]]
name = "clap_complete"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a394f7ec0715b42a4e52b294984c27c9a61f77c8d82f7774c5198350be143f19"
dependencies = [
"clap",
] ]
[[package]] [[package]]
@ -155,6 +165,7 @@ dependencies = [
"atty", "atty",
"chrono", "chrono",
"clap", "clap",
"clap_complete",
"ctrlc", "ctrlc",
"diff", "diff",
"dirs-next", "dirs-next",
@ -231,6 +242,12 @@ dependencies = [
"regex", "regex",
] ]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.19" version = "0.1.19"
@ -264,6 +281,16 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "indexmap"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]] [[package]]
name = "jemalloc-sys" name = "jemalloc-sys"
version = "0.3.2" version = "0.3.2"
@ -323,9 +350,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.6.4" version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
@ -388,19 +415,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]] [[package]]
name = "proc-macro2" name = "os_str_bytes"
version = "1.0.32" version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.10" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -498,15 +534,15 @@ dependencies = [
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.8.0" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.82" version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -524,10 +560,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "term_size" name = "termcolor"
version = "0.3.2" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [ dependencies = [
"libc", "libc",
"winapi", "winapi",
@ -548,12 +593,11 @@ dependencies = [
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.11.0" version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
dependencies = [ dependencies = [
"term_size", "terminal_size",
"unicode-width",
] ]
[[package]] [[package]]
@ -575,12 +619,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.2" version = "0.2.2"
@ -597,12 +635,6 @@ dependencies = [
"log", "log",
] ]
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"

View file

@ -30,7 +30,8 @@ name = "fd"
path = "src/main.rs" path = "src/main.rs"
[build-dependencies] [build-dependencies]
clap = "2.34.0" clap = "3.0"
clap_complete = "3.0"
version_check = "0.9" version_check = "0.9"
[dependencies] [dependencies]
@ -51,8 +52,8 @@ chrono = "0.4"
once_cell = "1.9.0" once_cell = "1.9.0"
[dependencies.clap] [dependencies.clap]
version = "2.34.0" version = "3.0"
features = ["suggestions", "color", "wrap_help"] features = ["suggestions", "color", "wrap_help", "cargo"]
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
users = "0.11.0" users = "0.11.0"

View file

@ -652,7 +652,7 @@ With Rust's package manager [cargo](https://github.com/rust-lang/cargo), you can
``` ```
cargo install fd-find cargo install fd-find
``` ```
Note that rust version *1.53.0* or later is required. Note that rust version *1.54.0* or later is required.
`make` is also needed for the build. `make` is also needed for the build.

View file

@ -1,11 +1,13 @@
use std::fs; use std::fs;
use clap::Shell; use clap_complete::{generate_to, Shell};
use Shell::*;
//use clap_complete::shells::Shel{Bash, Fish, PowerShell, Elvish};
include!("src/app.rs"); include!("src/app.rs");
fn main() { fn main() {
let min_version = "1.53"; let min_version = "1.54";
match version_check::is_min_version(min_version) { match version_check::is_min_version(min_version) {
Some(true) => {} Some(true) => {}
@ -24,7 +26,8 @@ fn main() {
fs::create_dir_all(&outdir).unwrap(); fs::create_dir_all(&outdir).unwrap();
let mut app = build_app(); let mut app = build_app();
app.gen_completions("fd", Shell::Bash, &outdir); // NOTE: zsh completions are hand written in contrib/completion/_fd
app.gen_completions("fd", Shell::Fish, &outdir); for shell in [Bash, PowerShell, Fish, Elvish] {
app.gen_completions("fd", Shell::PowerShell, &outdir); generate_to(shell, &mut app, "fd", &outdir).unwrap();
}
} }

View file

@ -1 +1 @@
msrv = "1.53.0" msrv = "1.54.0"

View file

@ -1,25 +1,25 @@
use clap::{crate_version, App, AppSettings, Arg}; use clap::{crate_version, App, AppSettings, Arg, ColorChoice};
pub fn build_app() -> App<'static, 'static> { pub fn build_app() -> App<'static> {
let clap_color_setting = if std::env::var_os("NO_COLOR").is_none() { let clap_color_choice = if std::env::var_os("NO_COLOR").is_none() {
AppSettings::ColoredHelp ColorChoice::Auto
} else { } else {
AppSettings::ColorNever ColorChoice::Never
}; };
let mut app = App::new("fd") let mut app = App::new("fd")
.version(crate_version!()) .version(crate_version!())
.usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>...]") .color(clap_color_choice)
.setting(clap_color_setting)
.setting(AppSettings::DeriveDisplayOrder) .setting(AppSettings::DeriveDisplayOrder)
.setting(AppSettings::DontCollapseArgsInUsage)
.after_help( .after_help(
"Note: `fd -h` prints a short and concise overview while `fd --help` gives all \ "Note: `fd -h` prints a short and concise overview while `fd --help` gives all \
details.", details.",
) )
.arg( .arg(
Arg::with_name("hidden") Arg::new("hidden")
.long("hidden") .long("hidden")
.short("H") .short('H')
.overrides_with("hidden") .overrides_with("hidden")
.help("Search hidden files and directories") .help("Search hidden files and directories")
.long_help( .long_help(
@ -30,18 +30,18 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("no-hidden") Arg::new("no-hidden")
.long("no-hidden") .long("no-hidden")
.overrides_with("hidden") .overrides_with("hidden")
.hidden(true) .hide(true)
.long_help( .long_help(
"Overrides --hidden.", "Overrides --hidden.",
), ),
) )
.arg( .arg(
Arg::with_name("no-ignore") Arg::new("no-ignore")
.long("no-ignore") .long("no-ignore")
.short("I") .short('I')
.overrides_with("no-ignore") .overrides_with("no-ignore")
.help("Do not respect .(git|fd)ignore files") .help("Do not respect .(git|fd)ignore files")
.long_help( .long_help(
@ -51,19 +51,19 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("ignore") Arg::new("ignore")
.long("ignore") .long("ignore")
.overrides_with("no-ignore") .overrides_with("no-ignore")
.hidden(true) .hide(true)
.long_help( .long_help(
"Overrides --no-ignore.", "Overrides --no-ignore.",
), ),
) )
.arg( .arg(
Arg::with_name("no-ignore-vcs") Arg::new("no-ignore-vcs")
.long("no-ignore-vcs") .long("no-ignore-vcs")
.overrides_with("no-ignore-vcs") .overrides_with("no-ignore-vcs")
.hidden_short_help(true) .hide_short_help(true)
.help("Do not respect .gitignore files") .help("Do not respect .gitignore files")
.long_help( .long_help(
"Show search results from files and directories that would otherwise be \ "Show search results from files and directories that would otherwise be \
@ -71,19 +71,19 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("ignore-vcs") Arg::new("ignore-vcs")
.long("ignore-vcs") .long("ignore-vcs")
.overrides_with("no-ignore-vcs") .overrides_with("no-ignore-vcs")
.hidden(true) .hide(true)
.long_help( .long_help(
"Overrides --no-ignore-vcs.", "Overrides --no-ignore-vcs.",
), ),
) )
.arg( .arg(
Arg::with_name("no-ignore-parent") Arg::new("no-ignore-parent")
.long("no-ignore-parent") .long("no-ignore-parent")
.overrides_with("no-ignore-parent") .overrides_with("no-ignore-parent")
.hidden_short_help(true) .hide_short_help(true)
.help("Do not respect .(git|fd)ignore files in parent directories") .help("Do not respect .(git|fd)ignore files in parent directories")
.long_help( .long_help(
"Show search results from files and directories that would otherwise be \ "Show search results from files and directories that would otherwise be \
@ -91,19 +91,19 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("no-global-ignore-file") Arg::new("no-global-ignore-file")
.long("no-global-ignore-file") .long("no-global-ignore-file")
.hidden(true) .hide(true)
.help("Do not respect the global ignore file") .help("Do not respect the global ignore file")
.long_help("Do not respect the global ignore file."), .long_help("Do not respect the global ignore file."),
) )
.arg( .arg(
Arg::with_name("rg-alias-hidden-ignore") Arg::new("rg-alias-hidden-ignore")
.short("u") .short('u')
.long("unrestricted") .long("unrestricted")
.overrides_with_all(&["ignore", "no-hidden"]) .overrides_with_all(&["ignore", "no-hidden"])
.multiple(true) .multiple_occurrences(true)
.hidden_short_help(true) .hide_short_help(true)
.help("Alias for '--no-ignore', and '--hidden' when given twice") .help("Alias for '--no-ignore', and '--hidden' when given twice")
.long_help( .long_help(
"Alias for '--no-ignore'. Can be repeated. '-uu' is an alias for \ "Alias for '--no-ignore'. Can be repeated. '-uu' is an alias for \
@ -111,9 +111,9 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("case-sensitive") Arg::new("case-sensitive")
.long("case-sensitive") .long("case-sensitive")
.short("s") .short('s')
.overrides_with_all(&["ignore-case", "case-sensitive"]) .overrides_with_all(&["ignore-case", "case-sensitive"])
.help("Case-sensitive search (default: smart case)") .help("Case-sensitive search (default: smart case)")
.long_help( .long_help(
@ -123,9 +123,9 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("ignore-case") Arg::new("ignore-case")
.long("ignore-case") .long("ignore-case")
.short("i") .short('i')
.overrides_with_all(&["case-sensitive", "ignore-case"]) .overrides_with_all(&["case-sensitive", "ignore-case"])
.help("Case-insensitive search (default: smart case)") .help("Case-insensitive search (default: smart case)")
.long_help( .long_help(
@ -135,19 +135,19 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("glob") Arg::new("glob")
.long("glob") .long("glob")
.short("g") .short('g')
.conflicts_with("fixed-strings") .conflicts_with("fixed-strings")
.overrides_with("glob") .overrides_with("glob")
.help("Glob-based search (default: regular expression)") .help("Glob-based search (default: regular expression)")
.long_help("Perform a glob-based search instead of a regular expression search."), .long_help("Perform a glob-based search instead of a regular expression search."),
) )
.arg( .arg(
Arg::with_name("regex") Arg::new("regex")
.long("regex") .long("regex")
.overrides_with_all(&["glob", "regex"]) .overrides_with_all(&["glob", "regex"])
.hidden_short_help(true) .hide_short_help(true)
.help("Regular-expression based search (default)") .help("Regular-expression based search (default)")
.long_help( .long_help(
"Perform a regular-expression based search (default). This can be used to \ "Perform a regular-expression based search (default). This can be used to \
@ -155,12 +155,12 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("fixed-strings") Arg::new("fixed-strings")
.long("fixed-strings") .long("fixed-strings")
.short("F") .short('F')
.alias("literal") .alias("literal")
.overrides_with("fixed-strings") .overrides_with("fixed-strings")
.hidden_short_help(true) .hide_short_help(true)
.help("Treat pattern as literal string instead of regex") .help("Treat pattern as literal string instead of regex")
.long_help( .long_help(
"Treat the pattern as a literal string instead of a regular expression. Note \ "Treat the pattern as a literal string instead of a regular expression. Note \
@ -169,9 +169,9 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("absolute-path") Arg::new("absolute-path")
.long("absolute-path") .long("absolute-path")
.short("a") .short('a')
.overrides_with("absolute-path") .overrides_with("absolute-path")
.help("Show absolute instead of relative paths") .help("Show absolute instead of relative paths")
.long_help( .long_help(
@ -180,18 +180,18 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("relative-path") Arg::new("relative-path")
.long("relative-path") .long("relative-path")
.overrides_with("absolute-path") .overrides_with("absolute-path")
.hidden(true) .hide(true)
.long_help( .long_help(
"Overrides --absolute-path.", "Overrides --absolute-path.",
), ),
) )
.arg( .arg(
Arg::with_name("list-details") Arg::new("list-details")
.long("list-details") .long("list-details")
.short("l") .short('l')
.conflicts_with("absolute-path") .conflicts_with("absolute-path")
.help("Use a long listing format with file metadata") .help("Use a long listing format with file metadata")
.long_help( .long_help(
@ -202,9 +202,9 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("follow") Arg::new("follow")
.long("follow") .long("follow")
.short("L") .short('L')
.alias("dereference") .alias("dereference")
.overrides_with("follow") .overrides_with("follow")
.help("Follow symbolic links") .help("Follow symbolic links")
@ -215,18 +215,18 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("no-follow") Arg::new("no-follow")
.long("no-follow") .long("no-follow")
.overrides_with("follow") .overrides_with("follow")
.hidden(true) .hide(true)
.long_help( .long_help(
"Overrides --follow.", "Overrides --follow.",
), ),
) )
.arg( .arg(
Arg::with_name("full-path") Arg::new("full-path")
.long("full-path") .long("full-path")
.short("p") .short('p')
.overrides_with("full-path") .overrides_with("full-path")
.help("Search full abs. path (default: filename only)") .help("Search full abs. path (default: filename only)")
.long_help( .long_help(
@ -237,12 +237,12 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("null_separator") Arg::new("null_separator")
.long("print0") .long("print0")
.short("0") .short('0')
.overrides_with("print0") .overrides_with("print0")
.conflicts_with("list-details") .conflicts_with("list-details")
.hidden_short_help(true) .hide_short_help(true)
.help("Separate results by the null character") .help("Separate results by the null character")
.long_help( .long_help(
"Separate search results by the null character (instead of newlines). \ "Separate search results by the null character (instead of newlines). \
@ -250,9 +250,9 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("max-depth") Arg::new("max-depth")
.long("max-depth") .long("max-depth")
.short("d") .short('d')
.takes_value(true) .takes_value(true)
.value_name("depth") .value_name("depth")
.help("Set maximum search depth (default: none)") .help("Set maximum search depth (default: none)")
@ -263,18 +263,18 @@ pub fn build_app() -> App<'static, 'static> {
) )
// support --maxdepth as well, for compatibility with rg // support --maxdepth as well, for compatibility with rg
.arg( .arg(
Arg::with_name("rg-depth") Arg::new("rg-depth")
.long("maxdepth") .long("maxdepth")
.hidden(true) .hide(true)
.takes_value(true) .takes_value(true)
.help("Set maximum search depth (default: none)") .help("Set maximum search depth (default: none)")
) )
.arg( .arg(
Arg::with_name("min-depth") Arg::new("min-depth")
.long("min-depth") .long("min-depth")
.takes_value(true) .takes_value(true)
.value_name("depth") .value_name("depth")
.hidden_short_help(true) .hide_short_help(true)
.help("Only show results starting at given depth") .help("Only show results starting at given depth")
.long_help( .long_help(
"Only show search results starting at the given depth. \ "Only show search results starting at the given depth. \
@ -282,11 +282,11 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("exact-depth") Arg::new("exact-depth")
.long("exact-depth") .long("exact-depth")
.takes_value(true) .takes_value(true)
.value_name("depth") .value_name("depth")
.hidden_short_help(true) .hide_short_help(true)
.conflicts_with_all(&["max-depth", "min-depth"]) .conflicts_with_all(&["max-depth", "min-depth"])
.help("Only show results at exact given depth") .help("Only show results at exact given depth")
.long_help( .long_help(
@ -295,19 +295,19 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("prune") Arg::new("prune")
.long("prune") .long("prune")
.conflicts_with_all(&["size", "exact-depth"]) .conflicts_with_all(&["size", "exact-depth"])
.hidden_short_help(true) .hide_short_help(true)
.help("Do not traverse into matching directories") .help("Do not traverse into matching directories")
.long_help("Do not traverse into directories that match the search criteria. If \ .long_help("Do not traverse into directories that match the search criteria. If \
you want to exclude specific directories, use the '--exclude=' option.") you want to exclude specific directories, use the '--exclude=' option.")
) )
.arg( .arg(
Arg::with_name("file-type") Arg::new("file-type")
.long("type") .long("type")
.short("t") .short('t')
.multiple(true) .multiple_occurrences(true)
.number_of_values(1) .number_of_values(1)
.takes_value(true) .takes_value(true)
.value_name("filetype") .value_name("filetype")
@ -366,10 +366,10 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("extension") Arg::new("extension")
.long("extension") .long("extension")
.short("e") .short('e')
.multiple(true) .multiple_occurrences(true)
.number_of_values(1) .number_of_values(1)
.takes_value(true) .takes_value(true)
.value_name("ext") .value_name("ext")
@ -382,9 +382,9 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("exec") Arg::new("exec")
.long("exec") .long("exec")
.short("x") .short('x')
.min_values(1) .min_values(1)
.allow_hyphen_values(true) .allow_hyphen_values(true)
.value_terminator(";") .value_terminator(";")
@ -413,9 +413,9 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("exec-batch") Arg::new("exec-batch")
.long("exec-batch") .long("exec-batch")
.short("X") .short('X')
.min_values(1) .min_values(1)
.allow_hyphen_values(true) .allow_hyphen_values(true)
.value_terminator(";") .value_terminator(";")
@ -440,11 +440,11 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("batch-size") Arg::new("batch-size")
.long("batch-size") .long("batch-size")
.takes_value(true) .takes_value(true)
.value_name("size") .value_name("size")
.hidden_short_help(true) .hide_short_help(true)
.requires("exec-batch") .requires("exec-batch")
.help("Max number of arguments to run as a batch with -X") .help("Max number of arguments to run as a batch with -X")
.long_help( .long_help(
@ -455,13 +455,13 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("exclude") Arg::new("exclude")
.long("exclude") .long("exclude")
.short("E") .short('E')
.takes_value(true) .takes_value(true)
.value_name("pattern") .value_name("pattern")
.number_of_values(1) .number_of_values(1)
.multiple(true) .multiple_occurrences(true)
.help("Exclude entries that match the given glob pattern") .help("Exclude entries that match the given glob pattern")
.long_help( .long_help(
"Exclude files/directories that match the given glob pattern. This \ "Exclude files/directories that match the given glob pattern. This \
@ -473,13 +473,13 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("ignore-file") Arg::new("ignore-file")
.long("ignore-file") .long("ignore-file")
.takes_value(true) .takes_value(true)
.value_name("path") .value_name("path")
.number_of_values(1) .number_of_values(1)
.multiple(true) .multiple_occurrences(true)
.hidden_short_help(true) .hide_short_help(true)
.help("Add custom ignore-file in '.gitignore' format") .help("Add custom ignore-file in '.gitignore' format")
.long_help( .long_help(
"Add a custom ignore-file in '.gitignore' format. These files have a low \ "Add a custom ignore-file in '.gitignore' format. These files have a low \
@ -487,9 +487,9 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("color") Arg::new("color")
.long("color") .long("color")
.short("c") .short('c')
.takes_value(true) .takes_value(true)
.value_name("when") .value_name("when")
.possible_values(&["never", "auto", "always"]) .possible_values(&["never", "auto", "always"])
@ -503,12 +503,12 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("threads") Arg::new("threads")
.long("threads") .long("threads")
.short("j") .short('j')
.takes_value(true) .takes_value(true)
.value_name("num") .value_name("num")
.hidden_short_help(true) .hide_short_help(true)
.help("Set number of threads") .help("Set number of threads")
.long_help( .long_help(
"Set number of threads to use for searching & executing (default: number \ "Set number of threads to use for searching & executing (default: number \
@ -516,13 +516,13 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("size") Arg::new("size")
.long("size") .long("size")
.short("S") .short('S')
.takes_value(true) .takes_value(true)
.number_of_values(1) .number_of_values(1)
.allow_hyphen_values(true) .allow_hyphen_values(true)
.multiple(true) .multiple_occurrences(true)
.help("Limit results based on the size of files") .help("Limit results based on the size of files")
.long_help( .long_help(
"Limit results based on the size of files using the format <+-><NUM><UNIT>.\n \ "Limit results based on the size of files using the format <+-><NUM><UNIT>.\n \
@ -544,10 +544,10 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("max-buffer-time") Arg::new("max-buffer-time")
.long("max-buffer-time") .long("max-buffer-time")
.takes_value(true) .takes_value(true)
.hidden(true) .hide(true)
.help("Milliseconds to buffer before streaming search results to console") .help("Milliseconds to buffer before streaming search results to console")
.long_help( .long_help(
"Amount of time in milliseconds to buffer, before streaming the search \ "Amount of time in milliseconds to buffer, before streaming the search \
@ -555,7 +555,7 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("changed-within") Arg::new("changed-within")
.long("changed-within") .long("changed-within")
.alias("change-newer-than") .alias("change-newer-than")
.alias("newer") .alias("newer")
@ -575,7 +575,7 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("changed-before") Arg::new("changed-before")
.long("changed-before") .long("changed-before")
.alias("change-older-than") .alias("change-older-than")
.alias("older") .alias("older")
@ -594,7 +594,7 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("max-results") Arg::new("max-results")
.long("max-results") .long("max-results")
.takes_value(true) .takes_value(true)
.value_name("count") .value_name("count")
@ -604,14 +604,14 @@ pub fn build_app() -> App<'static, 'static> {
// same search with `--exec rm` attached and get a reliable removal of // same search with `--exec rm` attached and get a reliable removal of
// the files they saw in the previous search. // the files they saw in the previous search.
.conflicts_with_all(&["exec", "exec-batch", "list-details"]) .conflicts_with_all(&["exec", "exec-batch", "list-details"])
.hidden_short_help(true) .hide_short_help(true)
.help("Limit number of search results") .help("Limit number of search results")
.long_help("Limit the number of search results to 'count' and quit immediately."), .long_help("Limit the number of search results to 'count' and quit immediately."),
) )
.arg( .arg(
Arg::with_name("max-one-result") Arg::new("max-one-result")
.short("1") .short('1')
.hidden_short_help(true) .hide_short_help(true)
.overrides_with("max-results") .overrides_with("max-results")
.conflicts_with_all(&["exec", "exec-batch", "list-details"]) .conflicts_with_all(&["exec", "exec-batch", "list-details"])
.help("Limit search to a single result") .help("Limit search to a single result")
@ -619,11 +619,11 @@ pub fn build_app() -> App<'static, 'static> {
This is an alias for '--max-results=1'.") This is an alias for '--max-results=1'.")
) )
.arg( .arg(
Arg::with_name("quiet") Arg::new("quiet")
.long("quiet") .long("quiet")
.short("q") .short('q')
.alias("has-results") .alias("has-results")
.hidden_short_help(true) .hide_short_help(true)
.conflicts_with_all(&["exec", "exec-batch", "list-details", "max-results"]) .conflicts_with_all(&["exec", "exec-batch", "list-details", "max-results"])
.help("Print nothing, exit code 0 if match found, 1 otherwise") .help("Print nothing, exit code 0 if match found, 1 otherwise")
.long_help( .long_help(
@ -634,9 +634,9 @@ pub fn build_app() -> App<'static, 'static> {
) )
) )
.arg( .arg(
Arg::with_name("show-errors") Arg::new("show-errors")
.long("show-errors") .long("show-errors")
.hidden_short_help(true) .hide_short_help(true)
.overrides_with("show-errors") .overrides_with("show-errors")
.help("Show filesystem errors") .help("Show filesystem errors")
.long_help( .long_help(
@ -645,12 +645,13 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("base-directory") Arg::new("base-directory")
.long("base-directory") .long("base-directory")
.takes_value(true) .takes_value(true)
.value_name("path") .value_name("path")
.number_of_values(1) .number_of_values(1)
.hidden_short_help(true) .allow_invalid_utf8(true)
.hide_short_help(true)
.help("Change current working directory") .help("Change current working directory")
.long_help( .long_help(
"Change the current working directory of fd to the provided path. This \ "Change the current working directory of fd to the provided path. This \
@ -661,7 +662,9 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("pattern").help( Arg::new("pattern")
.allow_invalid_utf8(true)
.help(
"the search pattern (a regular expression, unless '--glob' is used; optional)", "the search pattern (a regular expression, unless '--glob' is used; optional)",
).long_help( ).long_help(
"the search pattern which is either a regular expression (default) or a glob \ "the search pattern which is either a regular expression (default) or a glob \
@ -670,11 +673,11 @@ pub fn build_app() -> App<'static, 'static> {
pass '--' first, or it will be considered as a flag (fd -- '-foo').") pass '--' first, or it will be considered as a flag (fd -- '-foo').")
) )
.arg( .arg(
Arg::with_name("path-separator") Arg::new("path-separator")
.takes_value(true) .takes_value(true)
.value_name("separator") .value_name("separator")
.long("path-separator") .long("path-separator")
.hidden_short_help(true) .hide_short_help(true)
.help("Set path separator when printing file paths") .help("Set path separator when printing file paths")
.long_help( .long_help(
"Set the path separator to use when printing file paths. The default is \ "Set the path separator to use when printing file paths. The default is \
@ -682,8 +685,9 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("path") Arg::new("path")
.multiple(true) .multiple_occurrences(true)
.allow_invalid_utf8(true)
.help("the root directory for the filesystem search (optional)") .help("the root directory for the filesystem search (optional)")
.long_help( .long_help(
"The directory where the filesystem search is rooted (optional). If \ "The directory where the filesystem search is rooted (optional). If \
@ -691,25 +695,26 @@ pub fn build_app() -> App<'static, 'static> {
), ),
) )
.arg( .arg(
Arg::with_name("search-path") Arg::new("search-path")
.long("search-path") .long("search-path")
.takes_value(true) .takes_value(true)
.conflicts_with("path") .conflicts_with("path")
.multiple(true) .multiple_occurrences(true)
.hidden_short_help(true) .hide_short_help(true)
.number_of_values(1) .number_of_values(1)
.allow_invalid_utf8(true)
.help("Provide paths to search as an alternative to the positional <path>") .help("Provide paths to search as an alternative to the positional <path>")
.long_help( .long_help(
"Provide paths to search as an alternative to the positional <path> \ "Provide paths to search as an alternative to the positional <path> \
argument. Changes the usage to `fd [FLAGS/OPTIONS] --search-path <path> \ argument. Changes the usage to `fd [OPTIONS] --search-path <path> \
--search-path <path2> [<pattern>]`", --search-path <path2> [<pattern>]`",
), ),
) )
.arg( .arg(
Arg::with_name("strip-cwd-prefix") Arg::new("strip-cwd-prefix")
.long("strip-cwd-prefix") .long("strip-cwd-prefix")
.conflicts_with_all(&["path", "search-path"]) .conflicts_with_all(&["path", "search-path"])
.hidden_short_help(true) .hide_short_help(true)
.help("strip './' prefix from non-tty outputs") .help("strip './' prefix from non-tty outputs")
.long_help( .long_help(
"By default, relative paths are prefixed with './' when the output goes to a non \ "By default, relative paths are prefixed with './' when the output goes to a non \
@ -719,9 +724,9 @@ pub fn build_app() -> App<'static, 'static> {
if cfg!(unix) { if cfg!(unix) {
app = app.arg( app = app.arg(
Arg::with_name("owner") Arg::new("owner")
.long("owner") .long("owner")
.short("o") .short('o')
.takes_value(true) .takes_value(true)
.value_name("user:group") .value_name("user:group")
.help("Filter by owning user and/or group") .help("Filter by owning user and/or group")
@ -742,10 +747,10 @@ pub fn build_app() -> App<'static, 'static> {
// Provide aliases `mount` and `xdev` for people coming from `find`. // Provide aliases `mount` and `xdev` for people coming from `find`.
if cfg!(any(unix, windows)) { if cfg!(any(unix, windows)) {
app = app.arg( app = app.arg(
Arg::with_name("one-file-system") Arg::new("one-file-system")
.long("one-file-system") .long("one-file-system")
.aliases(&["mount", "xdev"]) .aliases(&["mount", "xdev"])
.hidden_short_help(true) .hide_short_help(true)
.help("Do not descend into a different file system") .help("Do not descend into a different file system")
.long_help( .long_help(
"By default, fd will traverse the file system tree as far as other options \ "By default, fd will traverse the file system tree as far as other options \
@ -758,3 +763,8 @@ pub fn build_app() -> App<'static, 'static> {
app app
} }
#[test]
fn verify_app() {
build_app().debug_assert()
}

View file

@ -130,6 +130,17 @@ fn normalize_output(s: &str, trim_start: bool, normalize_line: bool) -> String {
lines.join("\n") lines.join("\n")
} }
/// Trim whitespace from the beginning of each line.
fn trim_lines(s: &str) -> String {
s.lines()
.map(|line| line.trim_start())
.fold(String::new(), |mut str, line| {
str.push_str(line);
str.push('\n');
str
})
}
impl TestEnv { impl TestEnv {
pub fn new(directories: &[&'static str], files: &[&'static str]) -> TestEnv { pub fn new(directories: &[&'static str], files: &[&'static str]) -> TestEnv {
let temp_dir = create_working_directory(directories, files).expect("working directory"); let temp_dir = create_working_directory(directories, files).expect("working directory");
@ -287,12 +298,8 @@ impl TestEnv {
if let Some(expected) = expected { if let Some(expected) = expected {
// Normalize both expected and actual output. // Normalize both expected and actual output.
let expected_error = normalize_output(expected, true, self.normalize_line); let expected_error = trim_lines(expected);
let actual_err = normalize_output( let actual_err = trim_lines(&String::from_utf8_lossy(&output.stderr));
&String::from_utf8_lossy(&output.stderr),
false,
self.normalize_line,
);
// Compare actual output to expected output. // Compare actual output to expected output.
if !actual_err.trim_start().starts_with(&expected_error) { if !actual_err.trim_start().starts_with(&expected_error) {

View file

@ -1420,12 +1420,12 @@ fn test_exec_batch() {
te.assert_failure_with_error( te.assert_failure_with_error(
&["foo", "--exec-batch", "echo", "{/}", ";", "-x", "echo"], &["foo", "--exec-batch", "echo", "{/}", ";", "-x", "echo"],
"error: The argument '--exec <cmd>' cannot be used with '--exec-batch <cmd>'", "error: The argument '--exec-batch <cmd>...' cannot be used with '--exec <cmd>...'",
); );
te.assert_failure_with_error( te.assert_failure_with_error(
&["foo", "--exec-batch"], &["foo", "--exec-batch"],
"error: The argument '--exec-batch <cmd>' requires a value but none was supplied", "error: The argument '--exec-batch <cmd>...' requires a value but none was supplied",
); );
te.assert_failure_with_error( te.assert_failure_with_error(