From 43f276e0731b52ee9d07593dcdd6d702a10499ea Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Tue, 4 Jan 2022 00:35:49 -0700 Subject: [PATCH] Upgrade to clap 3.0 This wasn't backwards compatible so required some more substantial changes. --- Cargo.lock | 104 +++++++++++------- Cargo.toml | 7 +- build.rs | 11 +- src/app.rs | 250 ++++++++++++++++++++++--------------------- tests/testenv/mod.rs | 19 ++-- tests/tests.rs | 4 +- 6 files changed, 224 insertions(+), 171 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da514c7..94007af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,18 +85,28 @@ dependencies = [ [[package]] name = "clap" -version = "2.34.0" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "f6f34b09b9ee8c7c7b400fe2f8df39cafc9538b03d6ba7f4ae13e4cb90bfbb7d" dependencies = [ - "ansi_term", "atty", "bitflags", + "indexmap", + "lazy_static", + "os_str_bytes", "strsim", - "term_size", + "termcolor", + "terminal_size", "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]] @@ -155,6 +165,7 @@ dependencies = [ "atty", "chrono", "clap", + "clap_complete", "ctrlc", "diff", "dirs-next", @@ -231,6 +242,12 @@ dependencies = [ "regex", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -264,6 +281,16 @@ dependencies = [ "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]] name = "jemalloc-sys" version = "0.3.2" @@ -323,9 +350,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "memoffset" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] @@ -388,19 +415,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" [[package]] -name = "proc-macro2" -version = "1.0.32" +name = "os_str_bytes" +version = "6.0.0" 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 = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.10" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" dependencies = [ "proc-macro2", ] @@ -498,15 +534,15 @@ dependencies = [ [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.82" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" dependencies = [ "proc-macro2", "quote", @@ -524,10 +560,19 @@ dependencies = [ ] [[package]] -name = "term_size" -version = "0.3.2" +name = "termcolor" +version = "1.1.2" 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 = [ "libc", "winapi", @@ -548,12 +593,11 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.11.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" dependencies = [ - "term_size", - "unicode-width", + "terminal_size", ] [[package]] @@ -575,12 +619,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - [[package]] name = "unicode-xid" version = "0.2.2" @@ -597,12 +635,6 @@ dependencies = [ "log", ] -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 53007be..420c6ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,8 @@ name = "fd" path = "src/main.rs" [build-dependencies] -clap = "2.34.0" +clap = "3.0" +clap_complete = "3.0" version_check = "0.9" [dependencies] @@ -51,8 +52,8 @@ chrono = "0.4" once_cell = "1.9.0" [dependencies.clap] -version = "2.34.0" -features = ["suggestions", "color", "wrap_help"] +version = "3.0" +features = ["suggestions", "color", "wrap_help", "cargo"] [target.'cfg(unix)'.dependencies] users = "0.11.0" diff --git a/build.rs b/build.rs index d2d9566..cb390f7 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,8 @@ 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"); @@ -24,7 +26,8 @@ fn main() { fs::create_dir_all(&outdir).unwrap(); let mut app = build_app(); - app.gen_completions("fd", Shell::Bash, &outdir); - app.gen_completions("fd", Shell::Fish, &outdir); - app.gen_completions("fd", Shell::PowerShell, &outdir); + // NOTE: zsh completions are hand written in contrib/completion/_fd + for shell in [Bash, PowerShell, Fish, Elvish] { + generate_to(shell, &mut app, "fd", &outdir).unwrap(); + } } diff --git a/src/app.rs b/src/app.rs index dd4282b..bbf41b3 100644 --- a/src/app.rs +++ b/src/app.rs @@ -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> { - let clap_color_setting = if std::env::var_os("NO_COLOR").is_none() { - AppSettings::ColoredHelp +pub fn build_app() -> App<'static> { + let clap_color_choice = if std::env::var_os("NO_COLOR").is_none() { + ColorChoice::Auto } else { - AppSettings::ColorNever + ColorChoice::Never }; let mut app = App::new("fd") .version(crate_version!()) - .usage("fd [FLAGS/OPTIONS] [] [...]") - .setting(clap_color_setting) + .color(clap_color_choice) .setting(AppSettings::DeriveDisplayOrder) + .setting(AppSettings::DontCollapseArgsInUsage) .after_help( "Note: `fd -h` prints a short and concise overview while `fd --help` gives all \ details.", ) .arg( - Arg::with_name("hidden") + Arg::new("hidden") .long("hidden") - .short("H") + .short('H') .overrides_with("hidden") .help("Search hidden files and directories") .long_help( @@ -30,18 +30,18 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("no-hidden") + Arg::new("no-hidden") .long("no-hidden") .overrides_with("hidden") - .hidden(true) + .hide(true) .long_help( "Overrides --hidden.", ), ) .arg( - Arg::with_name("no-ignore") + Arg::new("no-ignore") .long("no-ignore") - .short("I") + .short('I') .overrides_with("no-ignore") .help("Do not respect .(git|fd)ignore files") .long_help( @@ -51,19 +51,19 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("ignore") + Arg::new("ignore") .long("ignore") .overrides_with("no-ignore") - .hidden(true) + .hide(true) .long_help( "Overrides --no-ignore.", ), ) .arg( - Arg::with_name("no-ignore-vcs") + Arg::new("no-ignore-vcs") .long("no-ignore-vcs") .overrides_with("no-ignore-vcs") - .hidden_short_help(true) + .hide_short_help(true) .help("Do not respect .gitignore files") .long_help( "Show search results from files and directories that would otherwise be \ @@ -71,19 +71,19 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("ignore-vcs") + Arg::new("ignore-vcs") .long("ignore-vcs") .overrides_with("no-ignore-vcs") - .hidden(true) + .hide(true) .long_help( "Overrides --no-ignore-vcs.", ), ) .arg( - Arg::with_name("no-ignore-parent") + Arg::new("no-ignore-parent") .long("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") .long_help( "Show search results from files and directories that would otherwise be \ @@ -91,19 +91,19 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("no-global-ignore-file") + Arg::new("no-global-ignore-file") .long("no-global-ignore-file") - .hidden(true) + .hide(true) .help("Do not respect the global ignore file") .long_help("Do not respect the global ignore file."), ) .arg( - Arg::with_name("rg-alias-hidden-ignore") - .short("u") + Arg::new("rg-alias-hidden-ignore") + .short('u') .long("unrestricted") .overrides_with_all(&["ignore", "no-hidden"]) - .multiple(true) - .hidden_short_help(true) + .multiple_occurrences(true) + .hide_short_help(true) .help("Alias for '--no-ignore', and '--hidden' when given twice") .long_help( "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::with_name("case-sensitive") + Arg::new("case-sensitive") .long("case-sensitive") - .short("s") + .short('s') .overrides_with_all(&["ignore-case", "case-sensitive"]) .help("Case-sensitive search (default: smart case)") .long_help( @@ -123,9 +123,9 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("ignore-case") + Arg::new("ignore-case") .long("ignore-case") - .short("i") + .short('i') .overrides_with_all(&["case-sensitive", "ignore-case"]) .help("Case-insensitive search (default: smart case)") .long_help( @@ -135,19 +135,19 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("glob") + Arg::new("glob") .long("glob") - .short("g") + .short('g') .conflicts_with("fixed-strings") .overrides_with("glob") .help("Glob-based search (default: regular expression)") .long_help("Perform a glob-based search instead of a regular expression search."), ) .arg( - Arg::with_name("regex") + Arg::new("regex") .long("regex") .overrides_with_all(&["glob", "regex"]) - .hidden_short_help(true) + .hide_short_help(true) .help("Regular-expression based search (default)") .long_help( "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::with_name("fixed-strings") + Arg::new("fixed-strings") .long("fixed-strings") - .short("F") + .short('F') .alias("literal") .overrides_with("fixed-strings") - .hidden_short_help(true) + .hide_short_help(true) .help("Treat pattern as literal string instead of regex") .long_help( "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::with_name("absolute-path") + Arg::new("absolute-path") .long("absolute-path") - .short("a") + .short('a') .overrides_with("absolute-path") .help("Show absolute instead of relative paths") .long_help( @@ -180,18 +180,18 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("relative-path") + Arg::new("relative-path") .long("relative-path") .overrides_with("absolute-path") - .hidden(true) + .hide(true) .long_help( "Overrides --absolute-path.", ), ) .arg( - Arg::with_name("list-details") + Arg::new("list-details") .long("list-details") - .short("l") + .short('l') .conflicts_with("absolute-path") .help("Use a long listing format with file metadata") .long_help( @@ -202,9 +202,9 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("follow") + Arg::new("follow") .long("follow") - .short("L") + .short('L') .alias("dereference") .overrides_with("follow") .help("Follow symbolic links") @@ -215,18 +215,18 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("no-follow") + Arg::new("no-follow") .long("no-follow") .overrides_with("follow") - .hidden(true) + .hide(true) .long_help( "Overrides --follow.", ), ) .arg( - Arg::with_name("full-path") + Arg::new("full-path") .long("full-path") - .short("p") + .short('p') .overrides_with("full-path") .help("Search full abs. path (default: filename only)") .long_help( @@ -237,12 +237,12 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("null_separator") + Arg::new("null_separator") .long("print0") - .short("0") + .short('0') .overrides_with("print0") .conflicts_with("list-details") - .hidden_short_help(true) + .hide_short_help(true) .help("Separate results by the null character") .long_help( "Separate search results by the null character (instead of newlines). \ @@ -250,9 +250,9 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("max-depth") + Arg::new("max-depth") .long("max-depth") - .short("d") + .short('d') .takes_value(true) .value_name("depth") .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 .arg( - Arg::with_name("rg-depth") + Arg::new("rg-depth") .long("maxdepth") - .hidden(true) + .hide(true) .takes_value(true) .help("Set maximum search depth (default: none)") ) .arg( - Arg::with_name("min-depth") + Arg::new("min-depth") .long("min-depth") .takes_value(true) .value_name("depth") - .hidden_short_help(true) + .hide_short_help(true) .help("Only show results starting at given depth") .long_help( "Only show search results starting at the given depth. \ @@ -282,11 +282,11 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("exact-depth") + Arg::new("exact-depth") .long("exact-depth") .takes_value(true) .value_name("depth") - .hidden_short_help(true) + .hide_short_help(true) .conflicts_with_all(&["max-depth", "min-depth"]) .help("Only show results at exact given depth") .long_help( @@ -295,19 +295,19 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("prune") + Arg::new("prune") .long("prune") .conflicts_with_all(&["size", "exact-depth"]) - .hidden_short_help(true) + .hide_short_help(true) .help("Do not traverse into matching directories") .long_help("Do not traverse into directories that match the search criteria. If \ you want to exclude specific directories, use the '--exclude=…' option.") ) .arg( - Arg::with_name("file-type") + Arg::new("file-type") .long("type") - .short("t") - .multiple(true) + .short('t') + .multiple_occurrences(true) .number_of_values(1) .takes_value(true) .value_name("filetype") @@ -366,10 +366,10 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("extension") + Arg::new("extension") .long("extension") - .short("e") - .multiple(true) + .short('e') + .multiple_occurrences(true) .number_of_values(1) .takes_value(true) .value_name("ext") @@ -382,9 +382,9 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("exec") + Arg::new("exec") .long("exec") - .short("x") + .short('x') .min_values(1) .allow_hyphen_values(true) .value_terminator(";") @@ -413,9 +413,9 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("exec-batch") + Arg::new("exec-batch") .long("exec-batch") - .short("X") + .short('X') .min_values(1) .allow_hyphen_values(true) .value_terminator(";") @@ -440,11 +440,11 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("batch-size") + Arg::new("batch-size") .long("batch-size") .takes_value(true) .value_name("size") - .hidden_short_help(true) + .hide_short_help(true) .requires("exec-batch") .help("Max number of arguments to run as a batch with -X") .long_help( @@ -455,13 +455,13 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("exclude") + Arg::new("exclude") .long("exclude") - .short("E") + .short('E') .takes_value(true) .value_name("pattern") .number_of_values(1) - .multiple(true) + .multiple_occurrences(true) .help("Exclude entries that match the given glob pattern") .long_help( "Exclude files/directories that match the given glob pattern. This \ @@ -473,13 +473,13 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("ignore-file") + Arg::new("ignore-file") .long("ignore-file") .takes_value(true) .value_name("path") .number_of_values(1) - .multiple(true) - .hidden_short_help(true) + .multiple_occurrences(true) + .hide_short_help(true) .help("Add custom ignore-file in '.gitignore' format") .long_help( "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::with_name("color") + Arg::new("color") .long("color") - .short("c") + .short('c') .takes_value(true) .value_name("when") .possible_values(&["never", "auto", "always"]) @@ -503,12 +503,12 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("threads") + Arg::new("threads") .long("threads") - .short("j") + .short('j') .takes_value(true) .value_name("num") - .hidden_short_help(true) + .hide_short_help(true) .help("Set number of threads") .long_help( "Set number of threads to use for searching & executing (default: number \ @@ -516,13 +516,13 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("size") + Arg::new("size") .long("size") - .short("S") + .short('S') .takes_value(true) .number_of_values(1) .allow_hyphen_values(true) - .multiple(true) + .multiple_occurrences(true) .help("Limit results based on the size of files") .long_help( "Limit results based on the size of files using the format <+->.\n \ @@ -544,10 +544,10 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("max-buffer-time") + Arg::new("max-buffer-time") .long("max-buffer-time") .takes_value(true) - .hidden(true) + .hide(true) .help("Milliseconds to buffer before streaming search results to console") .long_help( "Amount of time in milliseconds to buffer, before streaming the search \ @@ -555,7 +555,7 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("changed-within") + Arg::new("changed-within") .long("changed-within") .alias("change-newer-than") .alias("newer") @@ -575,7 +575,7 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("changed-before") + Arg::new("changed-before") .long("changed-before") .alias("change-older-than") .alias("older") @@ -594,7 +594,7 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("max-results") + Arg::new("max-results") .long("max-results") .takes_value(true) .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 // the files they saw in the previous search. .conflicts_with_all(&["exec", "exec-batch", "list-details"]) - .hidden_short_help(true) + .hide_short_help(true) .help("Limit number of search results") .long_help("Limit the number of search results to 'count' and quit immediately."), ) .arg( - Arg::with_name("max-one-result") - .short("1") - .hidden_short_help(true) + Arg::new("max-one-result") + .short('1') + .hide_short_help(true) .overrides_with("max-results") .conflicts_with_all(&["exec", "exec-batch", "list-details"]) .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'.") ) .arg( - Arg::with_name("quiet") + Arg::new("quiet") .long("quiet") - .short("q") + .short('q') .alias("has-results") - .hidden_short_help(true) + .hide_short_help(true) .conflicts_with_all(&["exec", "exec-batch", "list-details", "max-results"]) .help("Print nothing, exit code 0 if match found, 1 otherwise") .long_help( @@ -634,9 +634,9 @@ pub fn build_app() -> App<'static, 'static> { ) ) .arg( - Arg::with_name("show-errors") + Arg::new("show-errors") .long("show-errors") - .hidden_short_help(true) + .hide_short_help(true) .overrides_with("show-errors") .help("Show filesystem errors") .long_help( @@ -645,12 +645,13 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("base-directory") + Arg::new("base-directory") .long("base-directory") .takes_value(true) .value_name("path") .number_of_values(1) - .hidden_short_help(true) + .allow_invalid_utf8(true) + .hide_short_help(true) .help("Change current working directory") .long_help( "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::with_name("pattern").help( + Arg::new("pattern") + .allow_invalid_utf8(true) + .help( "the search pattern (a regular expression, unless '--glob' is used; optional)", ).long_help( "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').") ) .arg( - Arg::with_name("path-separator") + Arg::new("path-separator") .takes_value(true) .value_name("separator") .long("path-separator") - .hidden_short_help(true) + .hide_short_help(true) .help("Set path separator when printing file paths") .long_help( "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::with_name("path") - .multiple(true) + Arg::new("path") + .multiple_occurrences(true) + .allow_invalid_utf8(true) .help("the root directory for the filesystem search (optional)") .long_help( "The directory where the filesystem search is rooted (optional). If \ @@ -691,25 +695,26 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("search-path") + Arg::new("search-path") .long("search-path") .takes_value(true) .conflicts_with("path") - .multiple(true) - .hidden_short_help(true) + .multiple_occurrences(true) + .hide_short_help(true) .number_of_values(1) + .allow_invalid_utf8(true) .help("Provide paths to search as an alternative to the positional ") .long_help( "Provide paths to search as an alternative to the positional \ - argument. Changes the usage to `fd [FLAGS/OPTIONS] --search-path \ + argument. Changes the usage to `fd [OPTIONS] --search-path \ --search-path []`", ), ) .arg( - Arg::with_name("strip-cwd-prefix") + Arg::new("strip-cwd-prefix") .long("strip-cwd-prefix") .conflicts_with_all(&["path", "search-path"]) - .hidden_short_help(true) + .hide_short_help(true) .help("strip './' prefix from non-tty outputs") .long_help( "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) { app = app.arg( - Arg::with_name("owner") + Arg::new("owner") .long("owner") - .short("o") + .short('o') .takes_value(true) .value_name("user: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`. if cfg!(any(unix, windows)) { app = app.arg( - Arg::with_name("one-file-system") + Arg::new("one-file-system") .long("one-file-system") .aliases(&["mount", "xdev"]) - .hidden_short_help(true) + .hide_short_help(true) .help("Do not descend into a different file system") .long_help( "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 } + +#[test] +fn verify_app() { + build_app().debug_assert() +} diff --git a/tests/testenv/mod.rs b/tests/testenv/mod.rs index 510a7b2..c17ef4b 100644 --- a/tests/testenv/mod.rs +++ b/tests/testenv/mod.rs @@ -130,6 +130,17 @@ fn normalize_output(s: &str, trim_start: bool, normalize_line: bool) -> String { 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 { pub fn new(directories: &[&'static str], files: &[&'static str]) -> TestEnv { let temp_dir = create_working_directory(directories, files).expect("working directory"); @@ -287,12 +298,8 @@ impl TestEnv { if let Some(expected) = expected { // Normalize both expected and actual output. - let expected_error = normalize_output(expected, true, self.normalize_line); - let actual_err = normalize_output( - &String::from_utf8_lossy(&output.stderr), - false, - self.normalize_line, - ); + let expected_error = trim_lines(expected); + let actual_err = trim_lines(&String::from_utf8_lossy(&output.stderr)); // Compare actual output to expected output. if !actual_err.trim_start().starts_with(&expected_error) { diff --git a/tests/tests.rs b/tests/tests.rs index fb0535c..ec95a9c 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1420,12 +1420,12 @@ fn test_exec_batch() { te.assert_failure_with_error( &["foo", "--exec-batch", "echo", "{/}", ";", "-x", "echo"], - "error: The argument '--exec ' cannot be used with '--exec-batch '", + "error: The argument '--exec-batch ...' cannot be used with '--exec ...'", ); te.assert_failure_with_error( &["foo", "--exec-batch"], - "error: The argument '--exec-batch ' requires a value but none was supplied", + "error: The argument '--exec-batch ...' requires a value but none was supplied", ); te.assert_failure_with_error(