diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index d12326fe5edbecf94abb3ab0d1de3de467110e19..5b7e83ac6844932ca5d227f2f30756d5108ee9f4 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -1,26 +1,49 @@ name: Security audit + +permissions: + contents: read + on: pull_request: paths: - '**/Cargo.toml' - '**/Cargo.lock' push: - paths: - - '**/Cargo.toml' - - '**/Cargo.lock' - schedule: - - cron: '3 3 3 * *' -permissions: - contents: read + branches: + - main + +env: + RUST_BACKTRACE: 1 + CARGO_TERM_COLOR: always + CLICOLOR: 1 + jobs: security_audit: permissions: issues: write # to create issues (actions-rs/audit-check) checks: write # to create check (actions-rs/audit-check) runs-on: ubuntu-latest + # Prevent sudden announcement of a new advisory from failing ci: + continue-on-error: true steps: - name: Checkout repository uses: actions/checkout@v3 - uses: actions-rs/audit-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} + + cargo_deny: + permissions: + issues: write # to create issues (actions-rs/audit-check) + checks: write # to create check (actions-rs/audit-check) + runs-on: ubuntu-latest + strategy: + matrix: + checks: + - bans licenses sources + steps: + - uses: actions/checkout@v3 + - uses: EmbarkStudios/cargo-deny-action@v1 + with: + command: check ${{ matrix.checks }} + rust-version: stable diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63eec09e0982591ba8d99ec594898f2e74706157..5d88fb8b029766762394e2e78c1f607ec8b4866b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: permissions: contents: none name: CI - needs: [test, check, docs, rustfmt, clippy] + needs: [test, check, docs, rustfmt, clippy, cffconvert] runs-on: ubuntu-latest steps: - name: Done @@ -187,3 +187,15 @@ jobs: run: make clippy-full - name: Lint (release) run: make clippy-release + cffconvert: + name: cffconvert + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + persist-credentials: false + - name: CFF validation + uses: citation-file-format/cffconvert-github-action@2.0.0 + with: + args: --validate diff --git a/BUILD.gn b/BUILD.gn index 8f3b110b1fac9b22b6dba44b84971f2fa2525911..207b380fca290156441421b784c32f47e904dd88 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -20,7 +20,7 @@ ohos_cargo_crate("lib") { sources = [ "src/lib.rs" ] edition = "2021" - cargo_pkg_version = "4.1.4" + cargo_pkg_version = "4.1.13" cargo_pkg_name = "clap" cargo_pkg_description = "A simple to use, efficient, and full-featured Command Line Argument Parser" deps = [ @@ -49,7 +49,7 @@ ohos_cargo_crate("stdio_fixture") { sources = [ "src/bin/stdio-fixture.rs" ] edition = "2021" - cargo_pkg_version = "4.1.4" + cargo_pkg_version = "4.1.13" cargo_pkg_name = "clap" cargo_pkg_description = "A simple to use, efficient, and full-featured Command Line Argument Parser" deps = [ diff --git a/CHANGELOG.md b/CHANGELOG.md index bdcf3ad26058945698f969b7ed5deb02fb54cfdc..913d9440aef68ba48d5efaf869b029e16d4f5b3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,65 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +## [4.1.13] - 2023-03-18 + +### Performance + +- Reduce repeated alloc calls when building a `Command` +- Reduce duplicate dependencies for faster builds + +## [4.1.12] - 2023-03-18 + +### Internal + +- *(derive)* Update to `syn` v2 + +### Performance + +- *(derive)* Faster build times by dropping `proc-macro-error` dependency + +## [4.1.11] - 2023-03-17 + +### Internal + +- Update `bitflags` + +## [4.1.10] - 2023-03-17 + +### Fixes + +- *(help)* On Windows, avoid underlined text artifacts + +## [4.1.9] - 2023-03-16 + +### Fixes + +- *(assert)* Improve the assert when using the wrong action with `get_count` / `get_flag` + +## [4.1.8] - 2023-02-27 + +### Fixes + +- *(derive)* Don't `deny` lints on the users behalf + +## [4.1.7] - 2023-02-27 + +### Fixes + +- *(derive)* Hide some nightly clippy warnings + +## [4.1.6] - 2023-02-15 + +### Fixes + +- *(help)* Don't show long help for `--help` just because hidden possible values include a description + +## [4.1.5] - 2023-02-15 + +### Fixes + +- *(help)* Don't show long help for `--help` just because a hidden arg has a possible value with a description + ## [4.1.4] - 2023-01-24 ### Fixes @@ -4119,7 +4178,16 @@ Minimum version of Rust is now v1.13.0 (Stable) * **arg** allow lifetimes other than 'static in arguments ([9e8c1fb9](https://github.com/clap-rs/clap/commit/9e8c1fb9406f8448873ca58bab07fe905f1551e5)) -[Unreleased]: https://github.com/clap-rs/clap/compare/v4.1.4...HEAD +[Unreleased]: https://github.com/clap-rs/clap/compare/v4.1.13...HEAD +[4.1.13]: https://github.com/clap-rs/clap/compare/v4.1.12...v4.1.13 +[4.1.12]: https://github.com/clap-rs/clap/compare/v4.1.11...v4.1.12 +[4.1.11]: https://github.com/clap-rs/clap/compare/v4.1.10...v4.1.11 +[4.1.10]: https://github.com/clap-rs/clap/compare/v4.1.9...v4.1.10 +[4.1.9]: https://github.com/clap-rs/clap/compare/v4.1.8...v4.1.9 +[4.1.8]: https://github.com/clap-rs/clap/compare/v4.1.7...v4.1.8 +[4.1.7]: https://github.com/clap-rs/clap/compare/v4.1.6...v4.1.7 +[4.1.6]: https://github.com/clap-rs/clap/compare/v4.1.5...v4.1.6 +[4.1.5]: https://github.com/clap-rs/clap/compare/v4.1.4...v4.1.5 [4.1.4]: https://github.com/clap-rs/clap/compare/v4.1.3...v4.1.4 [4.1.3]: https://github.com/clap-rs/clap/compare/v4.1.2...v4.1.3 [4.1.2]: https://github.com/clap-rs/clap/compare/v4.1.1...v4.1.2 diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000000000000000000000000000000000000..888945012ce49ed9e67a0e21c9a5981913e300c8 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,35 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Parser settings. +cff-version: 1.2.0 +message: Please cite this crate using these information. + +# Version information. +date-released: 2023-03-18 +version: 4.1.13 + +# Project information. +abstract: A full featured, fast Command Line Argument Parser for Rust +authors: + - alias: kbknapp + family-names: Knapp + given-names: Kevin B. + - name: The Clap Community +license: + - Apache-2.0 + - MIT +repository-artifact: https://crates.io/crates/clap +repository-code: https://github.com/clap-rs/clap +title: clap +url: https://docs.rs/clap diff --git a/Cargo.lock b/Cargo.lock index 5cbab71b17413bcbea6e63502cfcb68fd5e3dbf0..8032cdf403351c13d2b536399488789f2cf2ad6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.17.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ "gimli", ] @@ -23,6 +23,46 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anstream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0982309face56a044e935a18bbffcddeb1ce72e69a3ecc3bafb56d4e959f37" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-wincon", + "concolor-override", + "concolor-query", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453bc2a7b261f8c4d1ce5b2c6c222d648d00988d30315e4911fbddc4ddf8983c" + +[[package]] +name = "anstyle-parse" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-wincon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" +dependencies = [ + "anstyle", + "windows-sys 0.45.0", +] + [[package]] name = "atty" version = "0.2.14" @@ -42,9 +82,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ "addr2line", "cc", @@ -55,6 +95,15 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "basic-toml" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e819b667739967cd44d308b8c7b71305d8bb0729ac44a248aa08f33d01950b4" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -126,12 +175,12 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.4" +version = "4.1.13" dependencies = [ "backtrace", "bitflags", "clap_derive", - "clap_lex 0.3.1", + "clap_lex 0.3.3", "humantime", "is-terminal", "once_cell", @@ -153,17 +202,17 @@ dependencies = [ name = "clap_bench" version = "0.0.0" dependencies = [ - "clap 4.1.4", + "clap 4.1.13", "criterion", "lazy_static", ] [[package]] name = "clap_complete" -version = "4.1.1" +version = "4.1.5" dependencies = [ - "clap 4.1.4", - "clap_lex 0.3.1", + "clap 4.1.13", + "clap_lex 0.3.3", "is_executable", "os_str_bytes", "pathdiff", @@ -175,22 +224,21 @@ dependencies = [ [[package]] name = "clap_complete_fig" -version = "4.1.0" +version = "4.1.2" dependencies = [ - "clap 4.1.4", + "clap 4.1.13", "clap_complete", "snapbox", ] [[package]] name = "clap_derive" -version = "4.1.0" +version = "4.1.12" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.8", ] [[package]] @@ -204,36 +252,34 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.1" +version = "0.3.3" dependencies = [ "os_str_bytes", ] [[package]] name = "clap_mangen" -version = "0.2.7" +version = "0.2.10" dependencies = [ - "clap 4.1.4", + "clap 4.1.13", "roff", "snapbox", ] [[package]] -name = "concolor" -version = "0.0.11" +name = "concolor-override" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "318d6c16e73b3a900eb212ad6a82fc7d298c5ab8184c7a9998646455bc474a16" -dependencies = [ - "bitflags", - "concolor-query", - "is-terminal", -] +checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" [[package]] name = "concolor-query" -version = "0.1.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a90734b3d5dcf656e7624cca6bce9c3a90ee11f900e80141a7427ccfb3d317" +checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" +dependencies = [ + "windows-sys 0.45.0", +] [[package]] name = "criterion" @@ -355,9 +401,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.2" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "glob" @@ -394,12 +440,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "humantime" @@ -434,19 +477,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] name = "is-terminal" -version = "0.4.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -526,22 +569,13 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.3" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ "adler", ] -[[package]] -name = "nom8" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" -dependencies = [ - "memchr", -] - [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -569,9 +603,9 @@ dependencies = [ [[package]] name = "object" -version = "0.29.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] @@ -638,44 +672,20 @@ dependencies = [ "plotters-backend", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -742,14 +752,14 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "ryu" @@ -774,22 +784,22 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.147" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.8", ] [[package]] @@ -803,6 +813,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + [[package]] name = "shlex" version = "1.1.0" @@ -817,26 +836,30 @@ checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803" [[package]] name = "snapbox" -version = "0.4.4" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34eced5a65e76d5a00047986a83c65f80dc666faa27b5138f331659e2ca6bcf5" +checksum = "9615402f9cff539301119bdf2c2f328739cf2b45c2116666618fb6ac399f75bb" dependencies = [ - "concolor", + "anstream", + "anstyle", + "escargot", "libc", "normalize-line-endings", "os_pipe", "similar", "snapbox-macros", "wait-timeout", - "windows-sys", - "yansi", + "windows-sys 0.45.0", ] [[package]] name = "snapbox-macros" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "485e65c1203eb37244465e857d15a26d3a85a5410648ccb53b18bd44cb3a7336" +checksum = "f8e40c667388ed1cb5060f545d0013bf0a23efdfa6c5c3e9ef592de391cd860f" +dependencies = [ + "anstream", +] [[package]] name = "static_assertions" @@ -852,9 +875,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.103" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" dependencies = [ "proc-macro2", "quote", @@ -877,7 +911,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907" dependencies = [ "rustix", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -896,59 +930,49 @@ dependencies = [ "serde_json", ] -[[package]] -name = "toml" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" -dependencies = [ - "serde", -] - [[package]] name = "toml_datetime" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808b51e57d0ef8f71115d8f3a01e7d3750d01c79cac4b3eda910f4389fdf92fd" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.17.1" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34cc558345efd7e88b9eda9626df2138b80bb46a7606f695e751c892bc7dac6" +checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" dependencies = [ "indexmap", - "itertools", - "nom8", "serde", + "serde_spanned", "toml_datetime", + "winnow", ] [[package]] name = "trybuild" -version = "1.0.73" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed01de3de062db82c0920b5cabe804f88d599a3f217932292597c678c903754d" +checksum = "a44da5a6f2164c8e14d3bbc0657d69c5966af9f5f6930d4f600b1f5c4a673413" dependencies = [ + "basic-toml", "glob", "once_cell", "serde", "serde_derive", "serde_json", "termcolor", - "toml", ] [[package]] name = "trycmd" -version = "0.14.9" +version = "0.14.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a050cd97463d2f8df73e65b122dadb7f04ea31f0bd53a1306ea915cbb156849" +checksum = "32564b3f936a9ebedf5cc07dcf1e7e661204766d35f92c03bf347b099d84e783" dependencies = [ - "escargot", "glob", "humantime", "humantime-serde", @@ -1011,9 +1035,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-width" @@ -1027,6 +1051,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "version_check" version = "0.9.4" @@ -1074,7 +1104,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -1096,7 +1126,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1163,50 +1193,77 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] -name = "yansi" -version = "0.5.1" +name = "winnow" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "faf09497b8f8b5ac5d3bb4d05c0a99be20f26fd3d5f2db7b0716e946d5103658" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index 7fc4c57de4d274291cb1c4afc8b54bb7c28447ad..efce9d36544399507bed5e82432c1bb4e4f638c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ include = [ [package] name = "clap" -version = "4.1.4" +version = "4.1.13" description = "A simple to use, efficient, and full-featured Command Line Argument Parser" repository = "https://github.com/clap-rs/clap" categories = ["command-line-interface"] @@ -57,6 +57,8 @@ pre-release-replacements = [ {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/clap-rs/clap/compare/{{tag_name}}...HEAD", exactly=1}, + {file="CITATION.cff", search="^date-released: ....-..-..", replace="date-released: {{date}}"}, + {file="CITATION.cff", search="^version: .+\\..+\\..+", replace="version: {{version}}"}, ] [features] @@ -97,25 +99,25 @@ unstable-v5 = ["clap_derive?/unstable-v5", "deprecated"] bench = false [dependencies] -clap_derive = { path = "./clap_derive", version = "=4.1.0", optional = true } +clap_derive = { path = "./clap_derive", version = "=4.1.12", optional = true } clap_lex = { path = "./clap_lex", version = "0.3.0" } -bitflags = "1.2" -unicase = { version = "2.6", optional = true } -strsim = { version = "0.10", optional = true } +bitflags = "1.2.0" +unicase = { version = "2.6.0", optional = true } +strsim = { version = "0.10.0", optional = true } is-terminal = { version = "0.4.1", optional = true } termcolor = { version = "1.1.1", optional = true } terminal_size = { version = "0.2.1", optional = true } -backtrace = { version = "0.3", optional = true } +backtrace = { version = "0.3.67", optional = true } unicode-width = { version = "0.1.9", optional = true } once_cell = { version = "1.12.0", optional = true } [dev-dependencies] -trybuild = "1.0.73" -rustversion = "1" +trybuild = "1.0.77" +rustversion = "1.0.12" # Cutting out `filesystem` feature -trycmd = { version = "0.14.9", default-features = false, features = ["color-auto", "diff", "examples"] } -humantime = "2" -snapbox = "0.4" +trycmd = { version = "0.14.15", default-features = false, features = ["color-auto", "diff", "examples"] } +humantime = "2.1.0" +snapbox = "0.4.10" shlex = "1.1.0" static_assertions = "1.1.0" unic-emoji-char = "0.9.0" diff --git a/README.OpenSource b/README.OpenSource index a099ac5a239ace78a15c066f96da792e7a148f3c..e0abba69876825cc77a60b145c0a4db0a3ca7a37 100644 --- a/README.OpenSource +++ b/README.OpenSource @@ -3,7 +3,7 @@ "Name": "clap", "License": "Apache license 2.0, MIT", "License File": "LICENSE-MIT, LICENSE-APACHE", - "Version Number": "v4.1.4", + "Version Number": "v4.1.13", "Owner": "fangting12@huawei.com", "Upstream URL": "https://github.com/clap-rs/clap", "Description": "A command-line argument parsing library for Rust that supports subcommands and complex argument parsing." diff --git a/clap_bench/Cargo.toml b/clap_bench/Cargo.toml index d53d23a36a1829f516134929b2f1305b5349af3d..6ea656c92a9ab0fa9daf370c71fb3e272693242b 100644 --- a/clap_bench/Cargo.toml +++ b/clap_bench/Cargo.toml @@ -14,7 +14,7 @@ release = false [dev-dependencies] clap = { path = "../", version = "4.0.0", default-features = false, features = ["std", "help"] } criterion = "0.4.0" -lazy_static = "1" +lazy_static = "1.4.0" [[bench]] harness = false diff --git a/clap_complete/CHANGELOG.md b/clap_complete/CHANGELOG.md index 55d489584c88ed54bfa9713b18817c746e04f5e7..14a6504a6e3f6cd5fbe8a546ab766d52adc4e9b2 100644 --- a/clap_complete/CHANGELOG.md +++ b/clap_complete/CHANGELOG.md @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +## [4.1.5] - 2023-03-16 + +## [4.1.4] - 2023-02-27 + +### Features + +- *(zsh)* Allow sourcing completion + +## [4.1.3] - 2023-02-23 + +### Fixes + +- *(zsh)* Improve handling of multi-valued arguments + +## [4.1.2] - 2023-02-15 + ## [4.1.1] - 2023-01-23 ### Fixes @@ -109,7 +125,11 @@ MSRV changed to 1.64.0 ## [3.0.1] - 2022-01-03 -[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v4.1.1...HEAD +[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v4.1.5...HEAD +[4.1.5]: https://github.com/clap-rs/clap/compare/clap_complete-v4.1.4...clap_complete-v4.1.5 +[4.1.4]: https://github.com/clap-rs/clap/compare/clap_complete-v4.1.3...clap_complete-v4.1.4 +[4.1.3]: https://github.com/clap-rs/clap/compare/clap_complete-v4.1.2...clap_complete-v4.1.3 +[4.1.2]: https://github.com/clap-rs/clap/compare/clap_complete-v4.1.1...clap_complete-v4.1.2 [4.1.1]: https://github.com/clap-rs/clap/compare/clap_complete-v4.1.0...clap_complete-v4.1.1 [4.1.0]: https://github.com/clap-rs/clap/compare/clap_complete-v4.0.7...clap_complete-v4.1.0 [4.0.7]: https://github.com/clap-rs/clap/compare/clap_complete-v4.0.6...clap_complete-v4.0.7 diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml index 94a18d324367a3d5ed1cef5c107a0cc4857c951f..a8c41616bb49e730a399416bdab448b605679ddd 100644 --- a/clap_complete/Cargo.toml +++ b/clap_complete/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clap_complete" -version = "4.1.1" +version = "4.1.5" description = "Generate shell completion scripts for your clap::Command" repository = "https://github.com/clap-rs/clap/tree/master/clap_complete" categories = ["command-line-interface"] @@ -35,15 +35,15 @@ bench = false clap = { path = "../", version = "4.1.0", default-features = false, features = ["std"] } clap_lex = { path = "../clap_lex", version = "0.3.0", optional = true } is_executable = { version = "1.0.1", optional = true } -os_str_bytes = { version = "6.0", default-features = false, features = ["raw_os_str"], optional = true } +os_str_bytes = { version = "6.0.0", default-features = false, features = ["raw_os_str"], optional = true } pathdiff = { version = "0.2.1", optional = true } shlex = { version = "1.1.0", optional = true } unicode-xid = { version = "0.2.2", optional = true } [dev-dependencies] -snapbox = { version = "0.4", features = ["diff"] } +snapbox = { version = "0.4.10", features = ["diff"] } # Cutting out `filesystem` feature -trycmd = { version = "0.14.6", default-features = false, features = ["color-auto", "diff", "examples"] } +trycmd = { version = "0.14.15", default-features = false, features = ["color-auto", "diff", "examples"] } clap = { path = "../", version = "4.0.0", default-features = false, features = ["std", "derive", "help"] } [[example]] diff --git a/clap_complete/README.md b/clap_complete/README.md index e500809f78470a44e3e188ca90c855a04cfcb3a2..75233b61f4226e45e6003a998ba80f9f01b08dd9 100644 --- a/clap_complete/README.md +++ b/clap_complete/README.md @@ -5,16 +5,16 @@ [![Crates.io](https://img.shields.io/crates/v/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete) [![Crates.io](https://img.shields.io/crates/d/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete) -[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.1.1/LICENSE-APACHE) -[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.1.1/LICENSE-MIT) +[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.1.5/LICENSE-APACHE) +[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.1.5/LICENSE-MIT) Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT). 1. [About](#about) 2. [API Reference](https://docs.rs/clap_complete) 3. [Questions & Discussions](https://github.com/clap-rs/clap/discussions) -4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v4.1.1/clap_complete/CONTRIBUTING.md) -5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v4.1.1/README.md#sponsors) +4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v4.1.5/clap_complete/CONTRIBUTING.md) +5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v4.1.5/README.md#sponsors) ## About diff --git a/clap_complete/examples/completion-derive.rs b/clap_complete/examples/completion-derive.rs index 8eeebf89d469ce04f8ea062d58d308b2c6f7acad..6b90f0a58adf20c4f07ba2b4afefd05437518547 100644 --- a/clap_complete/examples/completion-derive.rs +++ b/clap_complete/examples/completion-derive.rs @@ -24,7 +24,7 @@ struct Opt { // If provided, outputs the completion file for given shell #[arg(long = "generate", value_enum)] generator: Option, - #[clap(subcommand)] + #[command(subcommand)] command: Option, } @@ -75,9 +75,9 @@ fn main() { if let Some(generator) = opt.generator { let mut cmd = Opt::command(); - eprintln!("Generating completion file for {:?}...", generator); + eprintln!("Generating completion file for {generator:?}..."); print_completions(generator, &mut cmd); } else { - println!("{:#?}", opt); + println!("{opt:#?}"); } } diff --git a/clap_complete/examples/completion.rs b/clap_complete/examples/completion.rs index 0455d4f9c1f9bf33b0dc284f37c8a2cbcbf0ab35..70487b3b91fb09585b7b483d5f990343364b65b7 100644 --- a/clap_complete/examples/completion.rs +++ b/clap_complete/examples/completion.rs @@ -103,7 +103,7 @@ fn main() { if let Some(generator) = matches.get_one::("generator") { let mut cmd = build_cli(); - eprintln!("Generating completion file for {}...", generator); + eprintln!("Generating completion file for {generator}..."); print_completions(*generator, &mut cmd); } } diff --git a/clap_complete/src/shells/bash.rs b/clap_complete/src/shells/bash.rs index df9557611ec8b6c04c6b1a013e7f9d0bf9746c62..b66634098f6eecbfc77795a3978ec79b9593b00a 100644 --- a/clap_complete/src/shells/bash.rs +++ b/clap_complete/src/shells/bash.rs @@ -10,7 +10,7 @@ pub struct Bash; impl Generator for Bash { fn file_name(&self, name: &str) -> String { - format!("{}.bash", name) + format!("{name}.bash") } fn generate(&self, cmd: &Command, buf: &mut dyn Write) { @@ -114,9 +114,6 @@ fn all_subcommands(cmd: &Command) -> String { "{parent_fn_name},{name}) cmd=\"{fn_name}\" ;;", - parent_fn_name = parent_fn_name, - name = name, - fn_name = fn_name, )); } @@ -221,10 +218,10 @@ fn all_options_for_path(cmd: &Command, path: &str) -> String { let mut opts = String::new(); for short in utils::shorts_and_visible_aliases(p) { - write!(&mut opts, "-{} ", short).unwrap(); + write!(&mut opts, "-{short} ").unwrap(); } for long in utils::longs_and_visible_aliases(p) { - write!(&mut opts, "--{} ", long).unwrap(); + write!(&mut opts, "--{long} ").unwrap(); } for pos in p.get_positionals() { if let Some(vals) = utils::possible_values(pos) { @@ -232,11 +229,11 @@ fn all_options_for_path(cmd: &Command, path: &str) -> String { write!(&mut opts, "{} ", value.get_name()).unwrap(); } } else { - write!(&mut opts, "{} ", pos).unwrap(); + write!(&mut opts, "{pos} ").unwrap(); } } for (sc, _) in utils::subcommands(p) { - write!(&mut opts, "{} ", sc).unwrap(); + write!(&mut opts, "{sc} ").unwrap(); } opts.pop(); diff --git a/clap_complete/src/shells/elvish.rs b/clap_complete/src/shells/elvish.rs index 07da28348021d4413d8e00729e6535af15775177..48a0f852bbf36d79d0110e49ec879bda9fc66079 100644 --- a/clap_complete/src/shells/elvish.rs +++ b/clap_complete/src/shells/elvish.rs @@ -12,7 +12,7 @@ pub struct Elvish; impl Generator for Elvish { fn file_name(&self, name: &str) -> String { - format!("{}.elv", name) + format!("{name}.elv") } fn generate(&self, cmd: &Command, buf: &mut dyn Write) { @@ -46,8 +46,6 @@ set edit:completion:arg-completer[{bin_name}] = {{|@words| $completions[$command] }} "#, - bin_name = bin_name, - subcommands_cases = subcommands_cases ); w!(buf, result.as_bytes()); @@ -83,7 +81,7 @@ fn generate_inner(p: &Command, previous_command_name: &str) -> String { let tooltip = get_tooltip(option.get_help(), shorts[0]); for short in shorts { completions.push_str(&preamble); - completions.push_str(format!("-{} '{}'", short, tooltip).as_str()); + completions.push_str(format!("-{short} '{tooltip}'").as_str()); } } @@ -91,7 +89,7 @@ fn generate_inner(p: &Command, previous_command_name: &str) -> String { let tooltip = get_tooltip(option.get_help(), longs[0]); for long in longs { completions.push_str(&preamble); - completions.push_str(format!("--{} '{}'", long, tooltip).as_str()); + completions.push_str(format!("--{long} '{tooltip}'").as_str()); } } } @@ -101,7 +99,7 @@ fn generate_inner(p: &Command, previous_command_name: &str) -> String { let tooltip = get_tooltip(flag.get_help(), shorts[0]); for short in shorts { completions.push_str(&preamble); - completions.push_str(format!("-{} '{}'", short, tooltip).as_str()); + completions.push_str(format!("-{short} '{tooltip}'").as_str()); } } @@ -109,7 +107,7 @@ fn generate_inner(p: &Command, previous_command_name: &str) -> String { let tooltip = get_tooltip(flag.get_help(), longs[0]); for long in longs { completions.push_str(&preamble); - completions.push_str(format!("--{} '{}'", long, tooltip).as_str()); + completions.push_str(format!("--{long} '{tooltip}'").as_str()); } } } @@ -119,7 +117,7 @@ fn generate_inner(p: &Command, previous_command_name: &str) -> String { let tooltip = get_tooltip(subcommand.get_about(), data); completions.push_str(&preamble); - completions.push_str(format!("{} '{}'", data, tooltip).as_str()); + completions.push_str(format!("{data} '{tooltip}'").as_str()); } let mut subcommands_cases = format!( diff --git a/clap_complete/src/shells/fish.rs b/clap_complete/src/shells/fish.rs index fd2f3a4f80abb9752ba865e782e9c23a82629bff..77a72f7ebde826b28aa5a9b0098689cd356da4c4 100644 --- a/clap_complete/src/shells/fish.rs +++ b/clap_complete/src/shells/fish.rs @@ -12,7 +12,7 @@ pub struct Fish; impl Generator for Fish { fn file_name(&self, name: &str) -> String { - format!("{}.fish", name) + format!("{name}.fish") } fn generate(&self, cmd: &Command, buf: &mut dyn Write) { @@ -56,7 +56,7 @@ fn gen_fish_inner( // -n "__fish_use_subcommand" # complete for command "myprog" // -n "__fish_seen_subcommand_from subcmd1" # complete for command "myprog subcmd1" - let mut basic_template = format!("complete -c {}", root_command); + let mut basic_template = format!("complete -c {root_command}"); if parent_commands.is_empty() { if cmd.has_subcommands() { @@ -68,10 +68,10 @@ fn gen_fish_inner( " -n \"{}\"", parent_commands .iter() - .map(|command| format!("__fish_seen_subcommand_from {}", command)) + .map(|command| format!("__fish_seen_subcommand_from {command}")) .chain( cmd.get_subcommands() - .map(|command| format!("not __fish_seen_subcommand_from {}", command)) + .map(|command| format!("not __fish_seen_subcommand_from {command}")) ) .collect::>() .join("; and ") @@ -87,7 +87,7 @@ fn gen_fish_inner( if let Some(shorts) = option.get_short_and_visible_aliases() { for short in shorts { - template.push_str(format!(" -s {}", short).as_str()); + template.push_str(format!(" -s {short}").as_str()); } } @@ -113,7 +113,7 @@ fn gen_fish_inner( if let Some(shorts) = flag.get_short_and_visible_aliases() { for short in shorts { - template.push_str(format!(" -s {}", short).as_str()); + template.push_str(format!(" -s {short}").as_str()); } } diff --git a/clap_complete/src/shells/powershell.rs b/clap_complete/src/shells/powershell.rs index 0d3a2a55f734a4d105791c1cc023c347641002ec..92467d53c8490b873177dba1e70c7cbdb3f24e54 100644 --- a/clap_complete/src/shells/powershell.rs +++ b/clap_complete/src/shells/powershell.rs @@ -12,7 +12,7 @@ pub struct PowerShell; impl Generator for PowerShell { fn file_name(&self, name: &str) -> String { - format!("_{}.ps1", name) + format!("_{name}.ps1") } fn generate(&self, cmd: &Command, buf: &mut dyn Write) { diff --git a/clap_complete/src/shells/shell.rs b/clap_complete/src/shells/shell.rs index 4ae4a79ce9ffeecdf1a0ef2ec523738342682f66..52cb2e9b741ee5e39e4ec9a1400c91cbbcc97e0a 100644 --- a/clap_complete/src/shells/shell.rs +++ b/clap_complete/src/shells/shell.rs @@ -42,7 +42,7 @@ impl FromStr for Shell { return Ok(*variant); } } - Err(format!("invalid variant: {}", s)) + Err(format!("invalid variant: {s}")) } } diff --git a/clap_complete/src/shells/zsh.rs b/clap_complete/src/shells/zsh.rs index 15a9ed126dc97d2a2ec22f47d554f62c9a4c3b78..a41eb8856e2b2d73693a0cd606d41c61098a8c47 100644 --- a/clap_complete/src/shells/zsh.rs +++ b/clap_complete/src/shells/zsh.rs @@ -11,7 +11,7 @@ pub struct Zsh; impl Generator for Zsh { fn file_name(&self, name: &str) -> String { - format!("_{}", name) + format!("_{name}") } fn generate(&self, cmd: &Command, buf: &mut dyn Write) { @@ -43,7 +43,11 @@ _{name}() {{ {subcommand_details} -_{name} \"$@\" +if [ \"$funcstack[1]\" = \"_{name}\" ]; then + _{name} \"$@\" +else + compdef _{name} {name} +fi ", name = bin_name, initial_args = get_args_of(cmd, None), @@ -111,8 +115,8 @@ _{bin_name_underscore}_commands() {{ all_subcommands.sort(); all_subcommands.dedup(); - for &(_, ref bin_name) in &all_subcommands { - debug!("subcommand_details:iter: bin_name={}", bin_name); + for (_, ref bin_name) in &all_subcommands { + debug!("subcommand_details:iter: bin_name={bin_name}"); ret.push(format!( "\ @@ -223,14 +227,12 @@ fn get_subcommands_of(parent: &Command) -> String { let subcommand_names = utils::subcommands(parent); let mut all_subcommands = vec![]; - for &(ref name, ref bin_name) in &subcommand_names { + for (ref name, ref bin_name) in &subcommand_names { debug!( - "get_subcommands_of:iter: parent={}, name={}, bin_name={}", + "get_subcommands_of:iter: parent={}, name={name}, bin_name={bin_name}", parent.get_name(), - name, - bin_name, ); - let mut segments = vec![format!("({})", name)]; + let mut segments = vec![format!("({name})")]; let subcommand_args = get_args_of( parser_of(parent, bin_name).expect(INTERNAL_ERROR_MSG), Some(parent), @@ -458,8 +460,8 @@ fn write_opts_of(p: &Command, p_global: Option<&Command>) -> String { Some(val) => val[0].to_string(), }; let vc = match value_completion(o) { - Some(val) => format!(":{}:{}", vn, val), - None => format!(":{}: ", vn), + Some(val) => format!(":{vn}:{val}"), + None => format!(":{vn}: "), }; let vc = vc.repeat(o.get_num_args().expect("built").min_values()); @@ -502,11 +504,11 @@ fn arg_conflicts(cmd: &Command, arg: &Arg, app_global: Option<&Command>) -> Stri fn push_conflicts(conflicts: &[&Arg], res: &mut Vec) { for conflict in conflicts { if let Some(s) = conflict.get_short() { - res.push(format!("-{}", s)); + res.push(format!("-{s}")); } if let Some(l) = conflict.get_long() { - res.push(format!("--{}", l)); + res.push(format!("--{l}")); } } } @@ -568,13 +570,7 @@ fn write_flags_of(p: &Command, p_global: Option<&Command>) -> String { if let Some(short_aliases) = f.get_visible_short_aliases() { for alias in short_aliases { - let s = format!( - "'{conflicts}{multiple}-{arg}[{help}]' \\", - multiple = multiple, - conflicts = conflicts, - arg = alias, - help = help - ); + let s = format!("'{conflicts}{multiple}-{alias}[{help}]' \\",); debug!("write_flags_of:iter: Wrote...{}", &*s); @@ -622,18 +618,46 @@ fn write_positionals_of(p: &Command) -> String { let mut ret = vec![]; + // Completions for commands that end with two Vec arguments require special care. + // - You can have two Vec args separated with a custom value terminator. + // - You can have two Vec args with the second one set to last (raw sets last) + // which will require a '--' separator to be used before the second argument + // on the command-line. + // + // We use the '-S' _arguments option to disable completion after '--'. Thus, the + // completion for the second argument in scenario (B) does not need to be emitted + // because it is implicitly handled by the '-S' option. + // We only need to emit the first catch-all. + // + // Have we already emitted a catch-all multi-valued positional argument + // without a custom value terminator? + let mut catch_all_emitted = false; + for arg in p.get_positionals() { debug!("write_positionals_of:iter: arg={}", arg.get_id()); let num_args = arg.get_num_args().expect("built"); + let is_multi_valued = num_args.max_values() > 1; + + if catch_all_emitted && (arg.is_last_set() || is_multi_valued) { + // This is the final argument and it also takes multiple arguments. + // We've already emitted a catch-all positional argument so we don't need + // to emit anything for this argument because it is implicitly handled by + // the use of the '-S' _arguments option. + continue; + } + let cardinality_value; - let cardinality = if num_args.max_values() > 1 { + let cardinality = if is_multi_valued { match arg.get_value_terminator() { Some(terminator) => { cardinality_value = format!("*{}:", escape_value(terminator)); cardinality_value.as_str() } - None => "*:", + None => { + catch_all_emitted = true; + "*:" + } } } else if !arg.is_required_set() { ":" diff --git a/clap_complete/tests/bash.rs b/clap_complete/tests/bash.rs index d01651f26cd018142198b38490333374025e7f74..a331da84a5b044702fd4ebdf8f97414350a5fd4e 100644 --- a/clap_complete/tests/bash.rs +++ b/clap_complete/tests/bash.rs @@ -111,3 +111,15 @@ fn register_minimal() { .action_env("SNAPSHOTS") .matches_path("tests/snapshots/register_minimal.bash", buf); } + +#[test] +fn two_multi_valued_arguments() { + let name = "my-app"; + let cmd = common::two_multi_valued_arguments_command(name); + common::assert_matches_path( + "tests/snapshots/two_multi_valued_arguments.bash", + clap_complete::shells::Bash, + cmd, + name, + ); +} diff --git a/clap_complete/tests/common.rs b/clap_complete/tests/common.rs index 3f8cb437764d2bc3e4ba7a3f12836451c7083867..eef2e71a0bbbef0663110d34136d21f3ea2702fb 100644 --- a/clap_complete/tests/common.rs +++ b/clap_complete/tests/common.rs @@ -261,6 +261,20 @@ pub fn value_terminator_command(name: &'static str) -> clap::Command { ) } +pub fn two_multi_valued_arguments_command(name: &'static str) -> clap::Command { + clap::Command::new(name) + .arg( + clap::Arg::new("first") + .help("first multi-valued argument") + .num_args(1..), + ) + .arg( + clap::Arg::new("second") + .help("second multi-valued argument") + .raw(true), + ) +} + pub fn assert_matches_path( expected_path: impl AsRef, gen: impl clap_complete::Generator, diff --git a/clap_complete/tests/elvish.rs b/clap_complete/tests/elvish.rs index 26075a67ac19da3fd14eae4da0835235d3a78914..c2a82fc97c6f3c6315ace34aaf544343de28ddf1 100644 --- a/clap_complete/tests/elvish.rs +++ b/clap_complete/tests/elvish.rs @@ -95,3 +95,15 @@ fn value_terminator() { name, ); } + +#[test] +fn two_multi_valued_arguments() { + let name = "my-app"; + let cmd = common::two_multi_valued_arguments_command(name); + common::assert_matches_path( + "tests/snapshots/two_multi_valued_arguments.elvish", + clap_complete::shells::Elvish, + cmd, + name, + ); +} diff --git a/clap_complete/tests/fish.rs b/clap_complete/tests/fish.rs index 9393ed699aaf49ec12b24e88481f9712da5c8f60..b5bfae1508775e81c5d1f2d9bac6abb7b02122fd 100644 --- a/clap_complete/tests/fish.rs +++ b/clap_complete/tests/fish.rs @@ -95,3 +95,15 @@ fn value_terminator() { name, ); } + +#[test] +fn two_multi_valued_arguments() { + let name = "my-app"; + let cmd = common::two_multi_valued_arguments_command(name); + common::assert_matches_path( + "tests/snapshots/two_multi_valued_arguments.fish", + clap_complete::shells::Fish, + cmd, + name, + ); +} diff --git a/clap_complete/tests/powershell.rs b/clap_complete/tests/powershell.rs index 1f18618b7d22b326b3f6540ce0e4772086af00d7..2de4a05efd625e68aa2fe7a3594896f73044a7d0 100644 --- a/clap_complete/tests/powershell.rs +++ b/clap_complete/tests/powershell.rs @@ -95,3 +95,15 @@ fn value_terminator() { name, ); } + +#[test] +fn two_multi_valued_arguments() { + let name = "my-app"; + let cmd = common::two_multi_valued_arguments_command(name); + common::assert_matches_path( + "tests/snapshots/two_multi_valued_arguments.ps1", + clap_complete::shells::PowerShell, + cmd, + name, + ); +} diff --git a/clap_complete/tests/snapshots/aliases.zsh b/clap_complete/tests/snapshots/aliases.zsh index 403bbfe864dcd8994cb8ca7bf088d431d4f8ac1f..63764e56191d14eef487dd667ae8f170bb456c12 100644 --- a/clap_complete/tests/snapshots/aliases.zsh +++ b/clap_complete/tests/snapshots/aliases.zsh @@ -37,4 +37,8 @@ _my-app_commands() { _describe -t commands 'my-app commands' commands "$@" } -_my-app "$@" +if [ "$funcstack[1]" = "_my-app" ]; then + _my-app "$@" +else + compdef _my-app my-app +fi diff --git a/clap_complete/tests/snapshots/basic.zsh b/clap_complete/tests/snapshots/basic.zsh index 217ed938643ff4c1aa7794e1d645e23339497ec0..408bb7e4b8d4ed33d04c69c71a13aa038e050f22 100644 --- a/clap_complete/tests/snapshots/basic.zsh +++ b/clap_complete/tests/snapshots/basic.zsh @@ -97,4 +97,8 @@ _my-app__test_commands() { _describe -t commands 'my-app test commands' commands "$@" } -_my-app "$@" +if [ "$funcstack[1]" = "_my-app" ]; then + _my-app "$@" +else + compdef _my-app my-app +fi diff --git a/clap_complete/tests/snapshots/feature_sample.zsh b/clap_complete/tests/snapshots/feature_sample.zsh index 3b4a7b64b534e33e0b489d2e92b7396dfcdb8da2..72b85e11fff3fe9af5a7bac5a75f872a6eba93c3 100644 --- a/clap_complete/tests/snapshots/feature_sample.zsh +++ b/clap_complete/tests/snapshots/feature_sample.zsh @@ -104,4 +104,8 @@ _my-app__test_commands() { _describe -t commands 'my-app test commands' commands "$@" } -_my-app "$@" +if [ "$funcstack[1]" = "_my-app" ]; then + _my-app "$@" +else + compdef _my-app my-app +fi diff --git a/clap_complete/tests/snapshots/quoting.zsh b/clap_complete/tests/snapshots/quoting.zsh index 4afe4438a726583270c24be26f76b9be4932c3f9..95ed7eb697d04a136980080fd40732e5c9076c99 100644 --- a/clap_complete/tests/snapshots/quoting.zsh +++ b/clap_complete/tests/snapshots/quoting.zsh @@ -211,4 +211,8 @@ _my-app__help__help_commands() { _describe -t commands 'my-app help help commands' commands "$@" } -_my-app "$@" +if [ "$funcstack[1]" = "_my-app" ]; then + _my-app "$@" +else + compdef _my-app my-app +fi diff --git a/clap_complete/tests/snapshots/special_commands.zsh b/clap_complete/tests/snapshots/special_commands.zsh index 42023be705c6cb64ab2a154acea9a47a363c8b45..67d04bdae4b901f5624b556e5bf7f2ca69605cc9 100644 --- a/clap_complete/tests/snapshots/special_commands.zsh +++ b/clap_complete/tests/snapshots/special_commands.zsh @@ -178,4 +178,8 @@ _my-app__test_commands() { _describe -t commands 'my-app test commands' commands "$@" } -_my-app "$@" +if [ "$funcstack[1]" = "_my-app" ]; then + _my-app "$@" +else + compdef _my-app my-app +fi diff --git a/clap_complete/tests/snapshots/sub_subcommands.zsh b/clap_complete/tests/snapshots/sub_subcommands.zsh index 7a14feb555985c9b191a1171367345d50f75a9b8..720e72f12784bdcc159620e96eaf51ea601574d8 100644 --- a/clap_complete/tests/snapshots/sub_subcommands.zsh +++ b/clap_complete/tests/snapshots/sub_subcommands.zsh @@ -224,4 +224,8 @@ _my-app__test_commands() { _describe -t commands 'my-app test commands' commands "$@" } -_my-app "$@" +if [ "$funcstack[1]" = "_my-app" ]; then + _my-app "$@" +else + compdef _my-app my-app +fi diff --git a/clap_complete/tests/snapshots/two_multi_valued_arguments.bash b/clap_complete/tests/snapshots/two_multi_valued_arguments.bash new file mode 100644 index 0000000000000000000000000000000000000000..4e6e33c4558e8fcf4b21c6f231733e95e27c7215 --- /dev/null +++ b/clap_complete/tests/snapshots/two_multi_valued_arguments.bash @@ -0,0 +1,38 @@ +_my-app() { + local i cur prev opts cmd + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + cmd="" + opts="" + + for i in ${COMP_WORDS[@]} + do + case "${cmd},${i}" in + ",$1") + cmd="my__app" + ;; + *) + ;; + esac + done + + case "${cmd}" in + my__app) + opts="-h --help [first]... [second]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + esac +} + +complete -F _my-app -o bashdefault -o default my-app diff --git a/clap_complete/tests/snapshots/two_multi_valued_arguments.elvish b/clap_complete/tests/snapshots/two_multi_valued_arguments.elvish new file mode 100644 index 0000000000000000000000000000000000000000..89b26e0005a03e6897e2dabe4adaf0457f65a596 --- /dev/null +++ b/clap_complete/tests/snapshots/two_multi_valued_arguments.elvish @@ -0,0 +1,26 @@ + +use builtin; +use str; + +set edit:completion:arg-completer[my-app] = {|@words| + fn spaces {|n| + builtin:repeat $n ' ' | str:join '' + } + fn cand {|text desc| + edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc + } + var command = 'my-app' + for word $words[1..-1] { + if (str:has-prefix $word '-') { + break + } + set command = $command';'$word + } + var completions = [ + &'my-app'= { + cand -h 'Print help' + cand --help 'Print help' + } + ] + $completions[$command] +} diff --git a/clap_complete/tests/snapshots/two_multi_valued_arguments.fish b/clap_complete/tests/snapshots/two_multi_valued_arguments.fish new file mode 100644 index 0000000000000000000000000000000000000000..a33c671acf51aa624924ff5dbf2970bd2ac4ba49 --- /dev/null +++ b/clap_complete/tests/snapshots/two_multi_valued_arguments.fish @@ -0,0 +1 @@ +complete -c my-app -s h -l help -d 'Print help' diff --git a/clap_complete/tests/snapshots/two_multi_valued_arguments.ps1 b/clap_complete/tests/snapshots/two_multi_valued_arguments.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..30262afe031daaf910c85e7efdb694ef67e9f941 --- /dev/null +++ b/clap_complete/tests/snapshots/two_multi_valued_arguments.ps1 @@ -0,0 +1,32 @@ + +using namespace System.Management.Automation +using namespace System.Management.Automation.Language + +Register-ArgumentCompleter -Native -CommandName 'my-app' -ScriptBlock { + param($wordToComplete, $commandAst, $cursorPosition) + + $commandElements = $commandAst.CommandElements + $command = @( + 'my-app' + for ($i = 1; $i -lt $commandElements.Count; $i++) { + $element = $commandElements[$i] + if ($element -isnot [StringConstantExpressionAst] -or + $element.StringConstantType -ne [StringConstantType]::BareWord -or + $element.Value.StartsWith('-') -or + $element.Value -eq $wordToComplete) { + break + } + $element.Value + }) -join ';' + + $completions = @(switch ($command) { + 'my-app' { + [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help') + [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help') + break + } + }) + + $completions.Where{ $_.CompletionText -like "$wordToComplete*" } | + Sort-Object -Property ListItemText +} diff --git a/clap_complete/tests/snapshots/two_multi_valued_arguments.zsh b/clap_complete/tests/snapshots/two_multi_valued_arguments.zsh new file mode 100644 index 0000000000000000000000000000000000000000..a20088ea2a097f64fd43923d9b8d2b291bc02616 --- /dev/null +++ b/clap_complete/tests/snapshots/two_multi_valued_arguments.zsh @@ -0,0 +1,34 @@ +#compdef my-app + +autoload -U is-at-least + +_my-app() { + typeset -A opt_args + typeset -a _arguments_options + local ret=1 + + if is-at-least 5.2; then + _arguments_options=(-s -S -C) + else + _arguments_options=(-s -C) + fi + + local context curcontext="$curcontext" state line + _arguments "${_arguments_options[@]}" \ +'-h[Print help]' \ +'--help[Print help]' \ +'*::first -- first multi-valued argument:' \ +&& ret=0 +} + +(( $+functions[_my-app_commands] )) || +_my-app_commands() { + local commands; commands=() + _describe -t commands 'my-app commands' commands "$@" +} + +if [ "$funcstack[1]" = "_my-app" ]; then + _my-app "$@" +else + compdef _my-app my-app +fi diff --git a/clap_complete/tests/snapshots/value_hint.zsh b/clap_complete/tests/snapshots/value_hint.zsh index 15892dd7f087e4fd7ee92415d52f5482a53a1ced..c3a97f754b59a89cd5b914f0be40511e34baafec 100644 --- a/clap_complete/tests/snapshots/value_hint.zsh +++ b/clap_complete/tests/snapshots/value_hint.zsh @@ -47,4 +47,8 @@ _my-app_commands() { _describe -t commands 'my-app commands' commands "$@" } -_my-app "$@" +if [ "$funcstack[1]" = "_my-app" ]; then + _my-app "$@" +else + compdef _my-app my-app +fi diff --git a/clap_complete/tests/snapshots/value_terminator.zsh b/clap_complete/tests/snapshots/value_terminator.zsh index 656d8ea182ed4a52d46652e61cc4b885b9d7449c..fe819420f82501ee48ec5660d405a93f2cd369e1 100644 --- a/clap_complete/tests/snapshots/value_terminator.zsh +++ b/clap_complete/tests/snapshots/value_terminator.zsh @@ -27,4 +27,8 @@ _my-app_commands() { _describe -t commands 'my-app commands' commands "$@" } -_my-app "$@" +if [ "$funcstack[1]" = "_my-app" ]; then + _my-app "$@" +else + compdef _my-app my-app +fi diff --git a/clap_complete/tests/zsh.rs b/clap_complete/tests/zsh.rs index 57040bde465899790df6b675bb6bc9acbb8e3318..f90a7b80ce0a9c742131799f20aa79da8390473a 100644 --- a/clap_complete/tests/zsh.rs +++ b/clap_complete/tests/zsh.rs @@ -95,3 +95,15 @@ fn value_terminator() { name, ); } + +#[test] +fn two_multi_valued_arguments() { + let name = "my-app"; + let cmd = common::two_multi_valued_arguments_command(name); + common::assert_matches_path( + "tests/snapshots/two_multi_valued_arguments.zsh", + clap_complete::shells::Zsh, + cmd, + name, + ); +} diff --git a/clap_complete_fig/CHANGELOG.md b/clap_complete_fig/CHANGELOG.md index 5b331afc96bf6d0099941332cd662ec521afcf38..6685284918bcc1761fb2538206f36c143c109635 100644 --- a/clap_complete_fig/CHANGELOG.md +++ b/clap_complete_fig/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +## [4.1.2] - 2023-03-16 + +## [4.1.1] - 2023-02-15 + ## [4.1.0] - 2023-01-13 ### Compatibility @@ -63,7 +67,9 @@ MSRV changed to 1.64.0 ## [3.0.1] - 2022-01-03 -[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete_fig-v4.1.0...HEAD +[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete_fig-v4.1.2...HEAD +[4.1.2]: https://github.com/clap-rs/clap/compare/clap_complete_fig-v4.1.1...clap_complete_fig-v4.1.2 +[4.1.1]: https://github.com/clap-rs/clap/compare/clap_complete_fig-v4.1.0...clap_complete_fig-v4.1.1 [4.1.0]: https://github.com/clap-rs/clap/compare/clap_complete_fig-v4.0.2...clap_complete_fig-v4.1.0 [4.0.2]: https://github.com/clap-rs/clap/compare/clap_complete_fig-v4.0.1...clap_complete_fig-v4.0.2 [4.0.1]: https://github.com/clap-rs/clap/compare/clap_complete_fig-v4.0.0...clap_complete_fig-v4.0.1 diff --git a/clap_complete_fig/Cargo.toml b/clap_complete_fig/Cargo.toml index be90f65e9abc54fcb38a25b09f695879a5585124..de9659eb697a3cf46f5489851e2fad11d807047f 100644 --- a/clap_complete_fig/Cargo.toml +++ b/clap_complete_fig/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clap_complete_fig" -version = "4.1.0" +version = "4.1.2" description = "A generator library used with clap for Fig completion scripts" repository = "https://github.com/clap-rs/clap/tree/master/clap_complete_fig" categories = ["command-line-interface"] @@ -35,5 +35,5 @@ clap = { path = "../", version = "4.0.0", default-features = false, features = [ clap_complete = { path = "../clap_complete", version = "4.0.0" } [dev-dependencies] -snapbox = { version = "0.4", features = ["diff"] } +snapbox = { version = "0.4.10", features = ["diff"] } clap = { path = "../", version = "4.0.0", default-features = false, features = ["std", "help"] } diff --git a/clap_complete_fig/src/fig.rs b/clap_complete_fig/src/fig.rs index 6a81afaf6f5bdf5581605a77b74ffa8871bc6f57..07fb8a136c83b416a04b1d3688771523154b6036 100644 --- a/clap_complete_fig/src/fig.rs +++ b/clap_complete_fig/src/fig.rs @@ -11,7 +11,7 @@ pub struct Fig; impl Generator for Fig { fn file_name(&self, name: &str) -> String { - format!("{}.ts", name) + format!("{name}.ts") } fn generate(&self, cmd: &Command, buf: &mut dyn std::io::Write) { @@ -267,11 +267,11 @@ fn gen_options(cmd: &Command, indent: usize) -> String { let mut flags = vec![]; if let Some(shorts) = flag.get_short_and_visible_aliases() { - flags.extend(shorts.iter().map(|s| format!("-{}", s))); + flags.extend(shorts.iter().map(|s| format!("-{s}"))); } if let Some(longs) = flag.get_long_and_visible_aliases() { - flags.extend(longs.iter().map(|s| format!("--{}", s))); + flags.extend(longs.iter().map(|s| format!("--{s}"))); } if flags.len() > 1 { diff --git a/clap_derive/BUILD.gn b/clap_derive/BUILD.gn index f9328ca7d307fd33d43008f417b8e52a1c620cac..c9e7955fd37dd15ef4305cf7ada9297659c872fb 100644 --- a/clap_derive/BUILD.gn +++ b/clap_derive/BUILD.gn @@ -14,20 +14,20 @@ import("//build/ohos.gni") ohos_cargo_crate("lib") { - crate_name = "clap_derive" - crate_type = "proc-macro" - crate_root = "src/lib.rs" + crate_name = "clap_derive" + crate_type = "proc-macro" + crate_root = "src/lib.rs" - sources = ["src/lib.rs"] - edition = "2021" - cargo_pkg_version = "4.1.0" - cargo_pkg_name = "clap_derive" - cargo_pkg_description = "Parse command line argument by defining a struct, derive crate." - deps = [ - "//third_party/rust/crates/heck:lib", - "//third_party/rust/crates/proc-macro2:lib", - "//third_party/rust/crates/proc-macro-error:lib", - "//third_party/rust/crates/quote:lib", - "//third_party/rust/crates/syn:lib", - ] + sources = [ "src/lib.rs" ] + edition = "2021" + cargo_pkg_version = "4.1.12" + cargo_pkg_name = "clap_derive" + cargo_pkg_description = + "Parse command line argument by defining a struct, derive crate." + deps = [ + "//third_party/rust/crates/heck:lib", + "//third_party/rust/crates/proc-macro2:lib", + "//third_party/rust/crates/quote:lib", + "//third_party/rust/crates/syn:lib", + ] } diff --git a/clap_derive/Cargo.toml b/clap_derive/Cargo.toml index 7f721079ea82ce144267a92e2d732bbf0bda4d2f..782859a24e9bb3b2e6e9c8d7c65652739db9f6a4 100644 --- a/clap_derive/Cargo.toml +++ b/clap_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clap_derive" -version = "4.1.0" +version = "4.1.12" description = "Parse command line argument by defining a struct, derive crate." repository = "https://github.com/clap-rs/clap/tree/master/clap_derive" categories = ["command-line-interface", "development-tools::procedural-macro-helpers"] @@ -29,11 +29,10 @@ proc-macro = true bench = false [dependencies] -syn = { version = "1.0.74", features = ["full"] } +syn = { version = "2.0.8", features = ["full"] } quote = "1.0.9" proc-macro2 = "1.0.42" heck = "0.4.0" -proc-macro-error = "1" [features] default = [] diff --git a/clap_derive/src/attr.rs b/clap_derive/src/attr.rs index e282a8f94eddec3b362a171a2659dbe4a292c46c..3bc9ac0140d0e4f33f199f85077ab4a3c8d44fe1 100644 --- a/clap_derive/src/attr.rs +++ b/clap_derive/src/attr.rs @@ -1,8 +1,6 @@ use std::iter::FromIterator; use proc_macro2::TokenStream; -use proc_macro_error::abort; -use proc_macro_error::ResultExt; use quote::quote; use quote::ToTokens; use syn::spanned::Spanned; @@ -24,49 +22,44 @@ pub struct ClapAttr { } impl ClapAttr { - pub fn parse_all(all_attrs: &[Attribute]) -> Vec { - all_attrs - .iter() - .filter_map(|attr| { - let kind = if attr.path.is_ident("clap") { - Some(Sp::new(AttrKind::Clap, attr.path.span())) - } else if attr.path.is_ident("structopt") { - Some(Sp::new(AttrKind::StructOpt, attr.path.span())) - } else if attr.path.is_ident("command") { - Some(Sp::new(AttrKind::Command, attr.path.span())) - } else if attr.path.is_ident("group") { - Some(Sp::new(AttrKind::Group, attr.path.span())) - } else if attr.path.is_ident("arg") { - Some(Sp::new(AttrKind::Arg, attr.path.span())) - } else if attr.path.is_ident("value") { - Some(Sp::new(AttrKind::Value, attr.path.span())) - } else { - None - }; - kind.map(|k| (k, attr)) - }) - .flat_map(|(k, attr)| { - attr.parse_args_with(Punctuated::::parse_terminated) - .unwrap_or_abort() - .into_iter() - .map(move |mut a| { - a.kind = k; - a - }) - }) - .collect() + pub fn parse_all(all_attrs: &[Attribute]) -> Result, syn::Error> { + let mut parsed = Vec::new(); + for attr in all_attrs { + let kind = if attr.path().is_ident("clap") { + Sp::new(AttrKind::Clap, attr.path().span()) + } else if attr.path().is_ident("structopt") { + Sp::new(AttrKind::StructOpt, attr.path().span()) + } else if attr.path().is_ident("command") { + Sp::new(AttrKind::Command, attr.path().span()) + } else if attr.path().is_ident("group") { + Sp::new(AttrKind::Group, attr.path().span()) + } else if attr.path().is_ident("arg") { + Sp::new(AttrKind::Arg, attr.path().span()) + } else if attr.path().is_ident("value") { + Sp::new(AttrKind::Value, attr.path().span()) + } else { + continue; + }; + for mut attr in + attr.parse_args_with(Punctuated::::parse_terminated)? + { + attr.kind = kind; + parsed.push(attr); + } + } + Ok(parsed) } - pub fn value_or_abort(&self) -> &AttrValue { + pub fn value_or_abort(&self) -> Result<&AttrValue, syn::Error> { self.value .as_ref() - .unwrap_or_else(|| abort!(self.name, "attribute `{}` requires a value", self.name)) + .ok_or_else(|| format_err!(self.name, "attribute `{}` requires a value", self.name)) } - pub fn lit_str_or_abort(&self) -> &LitStr { - let value = self.value_or_abort(); + pub fn lit_str_or_abort(&self) -> Result<&LitStr, syn::Error> { + let value = self.value_or_abort()?; match value { - AttrValue::LitStr(tokens) => tokens, + AttrValue::LitStr(tokens) => Ok(tokens), AttrValue::Expr(_) | AttrValue::Call(_) => { abort!( self.name, @@ -133,7 +126,7 @@ impl Parse for ClapAttr { let nested; parenthesized!(nested in input); - let method_args: Punctuated<_, Token![,]> = nested.parse_terminated(Expr::parse)?; + let method_args: Punctuated<_, _> = nested.parse_terminated(Expr::parse, Token![,])?; Some(AttrValue::Call(Vec::from_iter(method_args))) } else { None diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs index b5d51bfca397d4058d0a88cff0f40b0ea08b315e..ab2ce319270373e3be4122a9de7f9ec731d0b182 100644 --- a/clap_derive/src/derives/args.rs +++ b/clap_derive/src/derives/args.rs @@ -13,7 +13,6 @@ // MIT/Apache 2.0 license. use proc_macro2::{Ident, Span, TokenStream}; -use proc_macro_error::{abort, abort_call_site}; use quote::{format_ident, quote, quote_spanned}; use syn::ext::IdentExt; use syn::{ @@ -21,30 +20,27 @@ use syn::{ Fields, Generics, }; -use crate::dummies; use crate::item::{Item, Kind, Name}; use crate::utils::{inner_type, sub_type, Sp, Ty}; -pub fn derive_args(input: &DeriveInput) -> TokenStream { +pub fn derive_args(input: &DeriveInput) -> Result { let ident = &input.ident; - dummies::args(ident); - match input.data { Data::Struct(DataStruct { fields: Fields::Named(ref fields), .. }) => { let name = Name::Derived(ident.clone()); - let item = Item::from_args_struct(input, name); + let item = Item::from_args_struct(input, name)?; let fields = fields .named .iter() .map(|field| { - let item = Item::from_args_field(field, item.casing(), item.env_casing()); - (field, item) + let item = Item::from_args_field(field, item.casing(), item.env_casing())?; + Ok((field, item)) }) - .collect::>(); + .collect::, syn::Error>>()?; gen_for_struct(&item, ident, &input.generics, &fields) } Data::Struct(DataStruct { @@ -52,15 +48,15 @@ pub fn derive_args(input: &DeriveInput) -> TokenStream { .. }) => { let name = Name::Derived(ident.clone()); - let item = Item::from_args_struct(input, name); + let item = Item::from_args_struct(input, name)?; let fields = Punctuated::::new(); let fields = fields .iter() .map(|field| { - let item = Item::from_args_field(field, item.casing(), item.env_casing()); - (field, item) + let item = Item::from_args_field(field, item.casing(), item.env_casing())?; + Ok((field, item)) }) - .collect::>(); + .collect::, syn::Error>>()?; gen_for_struct(&item, ident, &input.generics, &fields) } _ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"), @@ -72,7 +68,7 @@ pub fn gen_for_struct( item_name: &Ident, generics: &Generics, fields: &[(&Field, Item)], -) -> TokenStream { +) -> Result { if !matches!(&*item.kind(), Kind::Command(_)) { abort! { item.kind().span(), "`{}` cannot be used with `command`", @@ -82,13 +78,13 @@ pub fn gen_for_struct( let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let constructor = gen_constructor(fields); - let updater = gen_updater(fields, true); + let constructor = gen_constructor(fields)?; + let updater = gen_updater(fields, true)?; let raw_deprecated = raw_deprecated(); let app_var = Ident::new("__clap_app", Span::call_site()); - let augmentation = gen_augment(fields, &app_var, item, false); - let augmentation_update = gen_augment(fields, &app_var, item, true); + let augmentation = gen_augment(fields, &app_var, item, false)?; + let augmentation_update = gen_augment(fields, &app_var, item, true)?; let group_id = if item.skip_group() { quote!(None) @@ -97,7 +93,7 @@ pub fn gen_for_struct( quote!(Some(clap::Id::from(#group_id))) }; - quote! { + Ok(quote! { #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] #[allow( clippy::style, @@ -109,8 +105,8 @@ pub fn gen_for_struct( clippy::nursery, clippy::cargo, clippy::suspicious_else_formatting, + clippy::almost_swapped, )] - #[deny(clippy::correctness)] impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause { fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result { Self::from_arg_matches_mut(&mut __clap_arg_matches.clone()) @@ -144,8 +140,8 @@ pub fn gen_for_struct( clippy::nursery, clippy::cargo, clippy::suspicious_else_formatting, + clippy::almost_swapped, )] - #[deny(clippy::correctness)] impl #impl_generics clap::Args for #item_name #ty_generics #where_clause { fn group_id() -> Option { #group_id @@ -157,7 +153,7 @@ pub fn gen_for_struct( #augmentation_update } } - } + }) } /// Generate a block of code to add arguments/subcommands corresponding to @@ -167,11 +163,12 @@ pub fn gen_augment( app_var: &Ident, parent_item: &Item, override_required: bool, -) -> TokenStream { +) -> Result { let mut subcommand_specified = false; - let args = fields.iter().filter_map(|(field, item)| { + let mut args = Vec::new(); + for (field, item) in fields { let kind = item.kind(); - match &*kind { + let genned = match &*kind { Kind::Command(_) | Kind::Value | Kind::Skip(_, _) @@ -179,7 +176,10 @@ pub fn gen_augment( | Kind::ExternalSubcommand => None, Kind::Subcommand(ty) => { if subcommand_specified { - abort!(field.span(), "`#[command(subcommand)]` can only be used once per container"); + abort!( + field.span(), + "`#[command(subcommand)]` can only be used once per container" + ); } subcommand_specified = true; @@ -354,8 +354,9 @@ pub fn gen_augment( }); }) } - } - }); + }; + args.push(genned); + } let deprecations = if !override_required { parent_item.deprecations() @@ -408,7 +409,7 @@ pub fn gen_augment( ) ) }; - quote! {{ + Ok(quote! {{ #deprecations let #app_var = #app_var #initial_app_methods @@ -416,15 +417,15 @@ pub fn gen_augment( ; #( #args )* #app_var #final_app_methods - }} + }}) } -pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream { +pub fn gen_constructor(fields: &[(&Field, Item)]) -> Result { let fields = fields.iter().map(|(field, item)| { let field_name = field.ident.as_ref().unwrap(); let kind = item.kind(); let arg_matches = format_ident!("__clap_arg_matches"); - match &*kind { + let genned = match &*kind { Kind::Command(_) | Kind::Value | Kind::ExternalSubcommand => { @@ -519,18 +520,20 @@ pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream { }, Kind::Arg(ty) | Kind::FromGlobal(ty) => { - gen_parsers(item, ty, field_name, field, None) + gen_parsers(item, ty, field_name, field, None)? } - } - }); + }; + Ok(genned) + }).collect::, syn::Error>>()?; - quote! {{ + Ok(quote! {{ #( #fields ),* - }} + }}) } -pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream { - let fields = fields.iter().map(|(field, item)| { +pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> Result { + let mut genned_fields = Vec::new(); + for (field, item) in fields { let field_name = field.ident.as_ref().unwrap(); let kind = item.kind(); @@ -544,10 +547,8 @@ pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream { }; let arg_matches = format_ident!("__clap_arg_matches"); - match &*kind { - Kind::Command(_) - | Kind::Value - | Kind::ExternalSubcommand => { + let genned = match &*kind { + Kind::Command(_) | Kind::Value | Kind::ExternalSubcommand => { abort! { kind.span(), "`{}` cannot be used with `arg`", kind.name(), @@ -617,17 +618,20 @@ pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream { #updater } } - }, + } Kind::Skip(_, _) => quote!(), - Kind::Arg(ty) | Kind::FromGlobal(ty) => gen_parsers(item, ty, field_name, field, Some(&access)), - } - }); - - quote! { - #( #fields )* + Kind::Arg(ty) | Kind::FromGlobal(ty) => { + gen_parsers(item, ty, field_name, field, Some(&access))? + } + }; + genned_fields.push(genned); } + + Ok(quote! { + #( #genned_fields )* + }) } fn gen_parsers( @@ -636,7 +640,7 @@ fn gen_parsers( field_name: &Ident, field: &Field, update: Option<&TokenStream>, -) -> TokenStream { +) -> Result { let span = ty.span(); let convert_type = inner_type(&field.ty); let id = item.id(); @@ -709,7 +713,7 @@ fn gen_parsers( } }; - if let Some(access) = update { + let genned = if let Some(access) = update { quote_spanned! { field.span()=> if #arg_matches.contains_id(#id) { #access @@ -718,7 +722,8 @@ fn gen_parsers( } } else { quote_spanned!(field.span()=> #field_name: #field_value ) - } + }; + Ok(genned) } #[cfg(feature = "raw-deprecated")] diff --git a/clap_derive/src/derives/into_app.rs b/clap_derive/src/derives/into_app.rs index a45667ab29630875c6934467ac7a02867713f933..72f081fd8d1c2a453b73094a11772d552f006cc0 100644 --- a/clap_derive/src/derives/into_app.rs +++ b/clap_derive/src/derives/into_app.rs @@ -18,7 +18,11 @@ use syn::{Generics, Ident}; use crate::item::Item; -pub fn gen_for_struct(item: &Item, item_name: &Ident, generics: &Generics) -> TokenStream { +pub fn gen_for_struct( + item: &Item, + item_name: &Ident, + generics: &Generics, +) -> Result { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let name = item.cased_name(); @@ -36,8 +40,8 @@ pub fn gen_for_struct(item: &Item, item_name: &Ident, generics: &Generics) -> To clippy::nursery, clippy::cargo, clippy::suspicious_else_formatting, + clippy::almost_swapped, )] - #[deny(clippy::correctness)] impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause { fn command<'b>() -> clap::Command { let #app_var = clap::Command::new(#name); @@ -51,16 +55,20 @@ pub fn gen_for_struct(item: &Item, item_name: &Ident, generics: &Generics) -> To } }; - tokens + Ok(tokens) } -pub fn gen_for_enum(item: &Item, item_name: &Ident, generics: &Generics) -> TokenStream { +pub fn gen_for_enum( + item: &Item, + item_name: &Ident, + generics: &Generics, +) -> Result { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let name = item.cased_name(); let app_var = Ident::new("__clap_app", Span::call_site()); - quote! { + Ok(quote! { #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] #[allow( clippy::style, @@ -72,8 +80,8 @@ pub fn gen_for_enum(item: &Item, item_name: &Ident, generics: &Generics) -> Toke clippy::nursery, clippy::cargo, clippy::suspicious_else_formatting, + clippy::almost_swapped, )] - #[deny(clippy::correctness)] impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause { fn command<'b>() -> clap::Command { let #app_var = clap::Command::new(#name) @@ -89,5 +97,5 @@ pub fn gen_for_enum(item: &Item, item_name: &Ident, generics: &Generics) -> Toke .arg_required_else_help(false) } } - } + }) } diff --git a/clap_derive/src/derives/parser.rs b/clap_derive/src/derives/parser.rs index 617857cacaa4632d6ec0f16bb390d24262910853..39b0b8a774696f66acf584354b513a88e485c187 100644 --- a/clap_derive/src/derives/parser.rs +++ b/clap_derive/src/derives/parser.rs @@ -13,7 +13,6 @@ // MIT/Apache 2.0 license. use proc_macro2::TokenStream; -use proc_macro_error::abort_call_site; use quote::quote; use syn::Ident; use syn::Variant; @@ -23,11 +22,10 @@ use syn::{ }; use crate::derives::{args, into_app, subcommand}; -use crate::dummies; use crate::item::Item; use crate::item::Name; -pub fn derive_parser(input: &DeriveInput) -> TokenStream { +pub fn derive_parser(input: &DeriveInput) -> Result { let ident = &input.ident; let pkg_name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default(); @@ -36,52 +34,46 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream { fields: Fields::Named(ref fields), .. }) => { - dummies::parser_struct(ident); - let name = Name::Assigned(quote!(#pkg_name)); - let item = Item::from_args_struct(input, name); + let item = Item::from_args_struct(input, name)?; let fields = fields .named .iter() .map(|field| { - let item = Item::from_args_field(field, item.casing(), item.env_casing()); - (field, item) + let item = Item::from_args_field(field, item.casing(), item.env_casing())?; + Ok((field, item)) }) - .collect::>(); + .collect::, syn::Error>>()?; gen_for_struct(&item, ident, &input.generics, &fields) } Data::Struct(DataStruct { fields: Fields::Unit, .. }) => { - dummies::parser_struct(ident); - let name = Name::Assigned(quote!(#pkg_name)); - let item = Item::from_args_struct(input, name); + let item = Item::from_args_struct(input, name)?; let fields = Punctuated::::new(); let fields = fields .iter() .map(|field| { - let item = Item::from_args_field(field, item.casing(), item.env_casing()); - (field, item) + let item = Item::from_args_field(field, item.casing(), item.env_casing())?; + Ok((field, item)) }) - .collect::>(); + .collect::, syn::Error>>()?; gen_for_struct(&item, ident, &input.generics, &fields) } Data::Enum(ref e) => { - dummies::parser_enum(ident); - let name = Name::Assigned(quote!(#pkg_name)); - let item = Item::from_subcommand_enum(input, name); + let item = Item::from_subcommand_enum(input, name)?; let variants = e .variants .iter() .map(|variant| { let item = - Item::from_subcommand_variant(variant, item.casing(), item.env_casing()); - (variant, item) + Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?; + Ok((variant, item)) }) - .collect::>(); + .collect::, syn::Error>>()?; gen_for_enum(&item, ident, &input.generics, &variants) } _ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"), @@ -93,18 +85,18 @@ fn gen_for_struct( item_name: &Ident, generics: &Generics, fields: &[(&Field, Item)], -) -> TokenStream { +) -> Result { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let into_app = into_app::gen_for_struct(item, item_name, generics); - let args = args::gen_for_struct(item, item_name, generics, fields); + let into_app = into_app::gen_for_struct(item, item_name, generics)?; + let args = args::gen_for_struct(item, item_name, generics, fields)?; - quote! { + Ok(quote! { impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {} #into_app #args - } + }) } fn gen_for_enum( @@ -112,16 +104,16 @@ fn gen_for_enum( item_name: &Ident, generics: &Generics, variants: &[(&Variant, Item)], -) -> TokenStream { +) -> Result { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let into_app = into_app::gen_for_enum(item, item_name, generics); - let subcommand = subcommand::gen_for_enum(item, item_name, generics, variants); + let into_app = into_app::gen_for_enum(item, item_name, generics)?; + let subcommand = subcommand::gen_for_enum(item, item_name, generics, variants)?; - quote! { + Ok(quote! { impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {} #into_app #subcommand - } + }) } diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs index 8a44e0c42196fd82282eaf97b4e319ba66e460ae..403fe4557a1c318363d5d6014530a5a9ce4e98c2 100644 --- a/clap_derive/src/derives/subcommand.rs +++ b/clap_derive/src/derives/subcommand.rs @@ -13,33 +13,29 @@ // MIT/Apache 2.0 license. use proc_macro2::{Ident, Span, TokenStream}; -use proc_macro_error::{abort, abort_call_site}; use quote::{format_ident, quote, quote_spanned}; use syn::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Variant}; use crate::derives::args; -use crate::dummies; use crate::item::{Item, Kind, Name}; use crate::utils::{is_simple_ty, subty_if_name}; -pub fn derive_subcommand(input: &DeriveInput) -> TokenStream { +pub fn derive_subcommand(input: &DeriveInput) -> Result { let ident = &input.ident; - dummies::subcommand(ident); - match input.data { Data::Enum(ref e) => { let name = Name::Derived(ident.clone()); - let item = Item::from_subcommand_enum(input, name); + let item = Item::from_subcommand_enum(input, name)?; let variants = e .variants .iter() .map(|variant| { let item = - Item::from_subcommand_variant(variant, item.casing(), item.env_casing()); - (variant, item) + Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?; + Ok((variant, item)) }) - .collect::>(); + .collect::, syn::Error>>()?; gen_for_enum(&item, ident, &input.generics, &variants) } _ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"), @@ -51,7 +47,7 @@ pub fn gen_for_enum( item_name: &Ident, generics: &Generics, variants: &[(&Variant, Item)], -) -> TokenStream { +) -> Result { if !matches!(&*item.kind(), Kind::Command(_)) { abort! { item.kind().span(), "`{}` cannot be used with `command`", @@ -61,14 +57,14 @@ pub fn gen_for_enum( let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let from_arg_matches = gen_from_arg_matches(variants); - let update_from_arg_matches = gen_update_from_arg_matches(variants); + let from_arg_matches = gen_from_arg_matches(variants)?; + let update_from_arg_matches = gen_update_from_arg_matches(variants)?; - let augmentation = gen_augment(variants, item, false); - let augmentation_update = gen_augment(variants, item, true); - let has_subcommand = gen_has_subcommand(variants); + let augmentation = gen_augment(variants, item, false)?; + let augmentation_update = gen_augment(variants, item, true)?; + let has_subcommand = gen_has_subcommand(variants)?; - quote! { + Ok(quote! { #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] #[allow( clippy::style, @@ -80,8 +76,8 @@ pub fn gen_for_enum( clippy::nursery, clippy::cargo, clippy::suspicious_else_formatting, + clippy::almost_swapped, )] - #[deny(clippy::correctness)] impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause { fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result { Self::from_arg_matches_mut(&mut __clap_arg_matches.clone()) @@ -106,8 +102,8 @@ pub fn gen_for_enum( clippy::nursery, clippy::cargo, clippy::suspicious_else_formatting, + clippy::almost_swapped, )] - #[deny(clippy::correctness)] impl #impl_generics clap::Subcommand for #item_name #ty_generics #where_clause { fn augment_subcommands <'b>(__clap_app: clap::Command) -> clap::Command { #augmentation @@ -119,225 +115,222 @@ pub fn gen_for_enum( #has_subcommand } } - } + }) } fn gen_augment( variants: &[(&Variant, Item)], parent_item: &Item, override_required: bool, -) -> TokenStream { +) -> Result { use syn::Fields::*; let app_var = Ident::new("__clap_app", Span::call_site()); - let subcommands: Vec<_> = variants - .iter() - .filter_map(|(variant, item)| { - let kind = item.kind(); + let mut subcommands = Vec::new(); + for (variant, item) in variants { + let kind = item.kind(); - match &*kind { - Kind::Skip(_, _) | - Kind::Arg(_) | - Kind::FromGlobal(_) | - Kind::Value => None, + let genned = match &*kind { + Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None, - Kind::ExternalSubcommand => { - let ty = match variant.fields { - Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, + Kind::ExternalSubcommand => { + let ty = match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, - _ => abort!( - variant, - "The enum variant marked with `external_subcommand` must be \ + _ => abort!( + variant, + "The enum variant marked with `external_subcommand` must be \ a single-typed tuple, and the type must be either `Vec` \ or `Vec`." - ), - }; + ), + }; + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let subty = subty_if_name(ty, "Vec").ok_or_else(|| { + format_err!( + ty.span(), + "The type must be `Vec<_>` \ + to be used with `external_subcommand`." + ) + })?; + let subcommand = quote_spanned! { kind.span()=> + #deprecations + let #app_var = #app_var + .external_subcommand_value_parser(clap::value_parser!(#subty)); + }; + Some(subcommand) + } + + Kind::Flatten(_) => match variant.fields { + Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { + let ty = &unnamed[0]; let deprecations = if !override_required { item.deprecations() } else { quote!() }; - let subty = subty_if_name(ty, "Vec").unwrap_or_else(|| { - abort!( - ty.span(), - "The type must be `Vec<_>` \ - to be used with `external_subcommand`." - ) - }); - let subcommand = quote_spanned! { kind.span()=> - #deprecations - let #app_var = #app_var - .external_subcommand_value_parser(clap::value_parser!(#subty)); + let next_help_heading = item.next_help_heading(); + let next_display_order = item.next_display_order(); + let subcommand = if override_required { + quote! { + #deprecations + let #app_var = #app_var + #next_help_heading + #next_display_order; + let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var); + } + } else { + quote! { + #deprecations + let #app_var = #app_var + #next_help_heading + #next_display_order; + let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var); + } }; Some(subcommand) } + _ => abort!( + variant, + "`flatten` is usable only with single-typed tuple variants" + ), + }, - Kind::Flatten(_) => match variant.fields { + Kind::Subcommand(_) => { + let subcommand_var = Ident::new("__clap_subcommand", Span::call_site()); + let arg_block = match variant.fields { + Named(_) => { + abort!(variant, "non single-typed tuple enums are not supported") + } + Unit => quote!( #subcommand_var ), Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { let ty = &unnamed[0]; - let deprecations = if !override_required { - item.deprecations() - } else { - quote!() - }; - let next_help_heading = item.next_help_heading(); - let next_display_order = item.next_display_order(); - let subcommand = if override_required { - quote! { - #deprecations - let #app_var = #app_var - #next_help_heading - #next_display_order; - let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var); + if override_required { + quote_spanned! { ty.span()=> + { + <#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_var) + } } } else { - quote! { - #deprecations - let #app_var = #app_var - #next_help_heading - #next_display_order; - let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var); - } - }; - Some(subcommand) - } - _ => abort!( - variant, - "`flatten` is usable only with single-typed tuple variants" - ), - }, - - Kind::Subcommand(_) => { - let subcommand_var = Ident::new("__clap_subcommand", Span::call_site()); - let arg_block = match variant.fields { - Named(_) => { - abort!(variant, "non single-typed tuple enums are not supported") - } - Unit => quote!( #subcommand_var ), - Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { - let ty = &unnamed[0]; - if override_required { - quote_spanned! { ty.span()=> - { - <#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_var) - } - } - } else { - quote_spanned! { ty.span()=> - { - <#ty as clap::Subcommand>::augment_subcommands(#subcommand_var) - } + quote_spanned! { ty.span()=> + { + <#ty as clap::Subcommand>::augment_subcommands(#subcommand_var) } } } - Unnamed(..) => { - abort!(variant, "non single-typed tuple enums are not supported") - } - }; + } + Unnamed(..) => { + abort!(variant, "non single-typed tuple enums are not supported") + } + }; - let name = item.cased_name(); - let deprecations = if !override_required { - item.deprecations() - } else { - quote!() - }; - let initial_app_methods = item.initial_top_level_methods(); - let final_from_attrs = item.final_top_level_methods(); - let override_methods = if override_required { - quote_spanned! { kind.span()=> - .subcommand_required(false) - .arg_required_else_help(false) - } - } else { - quote!() - }; - let subcommand = quote! { - let #app_var = #app_var.subcommand({ - #deprecations; - let #subcommand_var = clap::Command::new(#name); - let #subcommand_var = #subcommand_var - .subcommand_required(true) - .arg_required_else_help(true); + let name = item.cased_name(); + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let initial_app_methods = item.initial_top_level_methods(); + let final_from_attrs = item.final_top_level_methods(); + let override_methods = if override_required { + quote_spanned! { kind.span()=> + .subcommand_required(false) + .arg_required_else_help(false) + } + } else { + quote!() + }; + let subcommand = quote! { + let #app_var = #app_var.subcommand({ + #deprecations; + let #subcommand_var = clap::Command::new(#name); + let #subcommand_var = #subcommand_var + .subcommand_required(true) + .arg_required_else_help(true); + let #subcommand_var = #subcommand_var #initial_app_methods; + let #subcommand_var = #arg_block; + #subcommand_var #final_from_attrs #override_methods + }); + }; + Some(subcommand) + } + + Kind::Command(_) => { + let subcommand_var = Ident::new("__clap_subcommand", Span::call_site()); + let sub_augment = match variant.fields { + Named(ref fields) => { + // Defer to `gen_augment` for adding cmd methods + let fields = fields + .named + .iter() + .map(|field| { + let item = + Item::from_args_field(field, item.casing(), item.env_casing())?; + Ok((field, item)) + }) + .collect::, syn::Error>>()?; + args::gen_augment(&fields, &subcommand_var, item, override_required)? + } + Unit => { + let arg_block = quote!( #subcommand_var ); + let initial_app_methods = item.initial_top_level_methods(); + let final_from_attrs = item.final_top_level_methods(); + quote! { let #subcommand_var = #subcommand_var #initial_app_methods; let #subcommand_var = #arg_block; - #subcommand_var #final_from_attrs #override_methods - }); - }; - Some(subcommand) - } - - Kind::Command(_) => { - let subcommand_var = Ident::new("__clap_subcommand", Span::call_site()); - let sub_augment = match variant.fields { - Named(ref fields) => { - // Defer to `gen_augment` for adding cmd methods - let fields = fields - .named - .iter() - .map(|field| { - let item = Item::from_args_field(field, item.casing(), item.env_casing()); - (field, item) - }) - .collect::>(); - args::gen_augment(&fields, &subcommand_var, item, override_required) + #subcommand_var #final_from_attrs } - Unit => { - let arg_block = quote!( #subcommand_var ); - let initial_app_methods = item.initial_top_level_methods(); - let final_from_attrs = item.final_top_level_methods(); - quote! { - let #subcommand_var = #subcommand_var #initial_app_methods; - let #subcommand_var = #arg_block; - #subcommand_var #final_from_attrs - } - }, - Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { - let ty = &unnamed[0]; - let arg_block = if override_required { - quote_spanned! { ty.span()=> - { - <#ty as clap::Args>::augment_args_for_update(#subcommand_var) - } + } + Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { + let ty = &unnamed[0]; + let arg_block = if override_required { + quote_spanned! { ty.span()=> + { + <#ty as clap::Args>::augment_args_for_update(#subcommand_var) } - } else { - quote_spanned! { ty.span()=> - { - <#ty as clap::Args>::augment_args(#subcommand_var) - } + } + } else { + quote_spanned! { ty.span()=> + { + <#ty as clap::Args>::augment_args(#subcommand_var) } - }; - let initial_app_methods = item.initial_top_level_methods(); - let final_from_attrs = item.final_top_level_methods(); - quote! { - let #subcommand_var = #subcommand_var #initial_app_methods; - let #subcommand_var = #arg_block; - #subcommand_var #final_from_attrs } + }; + let initial_app_methods = item.initial_top_level_methods(); + let final_from_attrs = item.final_top_level_methods(); + quote! { + let #subcommand_var = #subcommand_var #initial_app_methods; + let #subcommand_var = #arg_block; + #subcommand_var #final_from_attrs } - Unnamed(..) => { - abort!(variant, "non single-typed tuple enums are not supported") - } - }; + } + Unnamed(..) => { + abort!(variant, "non single-typed tuple enums are not supported") + } + }; - let deprecations = if !override_required { - item.deprecations() - } else { - quote!() - }; - let name = item.cased_name(); - let subcommand = quote! { - let #app_var = #app_var.subcommand({ - #deprecations - let #subcommand_var = clap::Command::new(#name); - #sub_augment - }); - }; - Some(subcommand) - } + let deprecations = if !override_required { + item.deprecations() + } else { + quote!() + }; + let name = item.cased_name(); + let subcommand = quote! { + let #app_var = #app_var.subcommand({ + #deprecations + let #subcommand_var = clap::Command::new(#name); + #sub_augment + }); + }; + Some(subcommand) } - }) - .collect(); + }; + subcommands.push(genned); + } let deprecations = if !override_required { parent_item.deprecations() @@ -346,15 +339,15 @@ fn gen_augment( }; let initial_app_methods = parent_item.initial_top_level_methods(); let final_app_methods = parent_item.final_top_level_methods(); - quote! { + Ok(quote! { #deprecations; let #app_var = #app_var #initial_app_methods; #( #subcommands )*; #app_var #final_app_methods - } + }) } -fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream { +fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> Result { use syn::Fields::*; let mut ext_subcmd = false; @@ -391,19 +384,20 @@ fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream { .map(|(variant, _attrs)| match variant.fields { Unnamed(ref fields) if fields.unnamed.len() == 1 => { let ty = &fields.unnamed[0]; - quote! { + Ok(quote! { if <#ty as clap::Subcommand>::has_subcommand(__clap_name) { return true; } - } + }) } _ => abort!( variant, "`flatten` is usable only with single-typed tuple variants" ), - }); + }) + .collect::, syn::Error>>()?; - if ext_subcmd { + let genned = if ext_subcmd { quote! { true } } else { quote! { @@ -413,77 +407,79 @@ fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream { false } - } + }; + Ok(genned) } -fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { +fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> Result { use syn::Fields::*; - let mut ext_subcmd = None; - let subcommand_name_var = format_ident!("__clap_name"); let sub_arg_matches_var = format_ident!("__clap_arg_matches"); - let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants - .iter() - .filter_map(|(variant, item)| { - let kind = item.kind(); - match &*kind { - Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None, - Kind::ExternalSubcommand => { - if ext_subcmd.is_some() { - abort!( - item.kind().span(), - "Only one variant can be marked with `external_subcommand`, \ + let mut ext_subcmd = None; + let mut flatten_variants = Vec::new(); + let mut unflatten_variants = Vec::new(); + for (variant, item) in variants { + let kind = item.kind(); + match &*kind { + Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => {} + + Kind::ExternalSubcommand => { + if ext_subcmd.is_some() { + abort!( + item.kind().span(), + "Only one variant can be marked with `external_subcommand`, \ this is the second" - ); - } + ); + } - let ty = match variant.fields { - Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, + let ty = match variant.fields { + Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, - _ => abort!( - variant, - "The enum variant marked with `external_subcommand` must be \ + _ => abort!( + variant, + "The enum variant marked with `external_subcommand` must be \ a single-typed tuple, and the type must be either `Vec` \ or `Vec`." - ), - }; - - let (span, str_ty) = match subty_if_name(ty, "Vec") { - Some(subty) => { - if is_simple_ty(subty, "String") { - (subty.span(), quote!(::std::string::String)) - } else if is_simple_ty(subty, "OsString") { - (subty.span(), quote!(::std::ffi::OsString)) - } else { - abort!( - ty.span(), - "The type must be either `Vec` or `Vec` \ + ), + }; + + let (span, str_ty) = match subty_if_name(ty, "Vec") { + Some(subty) => { + if is_simple_ty(subty, "String") { + (subty.span(), quote!(::std::string::String)) + } else if is_simple_ty(subty, "OsString") { + (subty.span(), quote!(::std::ffi::OsString)) + } else { + abort!( + ty.span(), + "The type must be either `Vec` or `Vec` \ to be used with `external_subcommand`." - ); - } + ); } + } - None => abort!( - ty.span(), - "The type must be either `Vec` or `Vec` \ + None => abort!( + ty.span(), + "The type must be either `Vec` or `Vec` \ to be used with `external_subcommand`." - ), - }; + ), + }; - ext_subcmd = Some((span, &variant.ident, str_ty)); - None + ext_subcmd = Some((span, &variant.ident, str_ty)); + } + Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => { + if matches!(&*item.kind(), Kind::Flatten(_)) { + flatten_variants.push((variant, item)); + } else { + unflatten_variants.push((variant, item)); } - Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)), } - }) - .partition(|(_, item)| { - let kind = item.kind(); - matches!(&*kind, Kind::Flatten(_)) - }); + } + } - let subcommands = variants.iter().map(|(variant, item)| { + let subcommands = unflatten_variants.iter().map(|(variant, item)| { let sub_name = item.cased_name(); let variant_name = &variant.ident; let constructor_block = match variant.fields { @@ -492,11 +488,11 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { .named .iter() .map(|field| { - let item = Item::from_args_field(field, item.casing(), item.env_casing()); - (field, item) + let item = Item::from_args_field(field, item.casing(), item.env_casing())?; + Ok((field, item)) }) - .collect::>(); - args::gen_constructor(&fields) + .collect::, syn::Error>>()?; + args::gen_constructor(&fields)? }, Unit => quote!(), Unnamed(ref fields) if fields.unnamed.len() == 1 => { @@ -506,18 +502,18 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident), }; - quote! { + Ok(quote! { if #subcommand_name_var == #sub_name && !#sub_arg_matches_var.contains_id("") { return ::std::result::Result::Ok(Self :: #variant_name #constructor_block) } - } - }); + }) + }).collect::, syn::Error>>()?; let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| { let variant_name = &variant.ident; match variant.fields { Unnamed(ref fields) if fields.unnamed.len() == 1 => { let ty = &fields.unnamed[0]; - quote! { + Ok(quote! { if __clap_arg_matches .subcommand_name() .map(|__clap_name| <#ty as clap::Subcommand>::has_subcommand(__clap_name)) @@ -526,14 +522,14 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { let __clap_res = <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?; return ::std::result::Result::Ok(Self :: #variant_name (__clap_res)); } - } + }) } _ => abort!( variant, "`flatten` is usable only with single-typed tuple variants" ), } - }); + }).collect::, syn::Error>>()?; let wildcard = match ext_subcmd { Some((span, var_name, str_ty)) => quote_spanned! { span=> @@ -555,7 +551,7 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { }; let raw_deprecated = args::raw_deprecated(); - quote! { + Ok(quote! { fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result { #raw_deprecated @@ -570,10 +566,10 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { ::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::MissingSubcommand, "A subcommand is required but one was not provided.")) } } - } + }) } -fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { +fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> Result { use syn::Fields::*; let (flatten, variants): (Vec<_>, Vec<_>) = variants @@ -607,11 +603,11 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { .named .iter() .map(|field| { - let item = Item::from_args_field(field, item.casing(), item.env_casing()); - (field, item) + let item = Item::from_args_field(field, item.casing(), item.env_casing())?; + Ok((field, item)) }) - .collect::>(); - let update = args::gen_updater(&fields, false); + .collect::, syn::Error>>()?; + let update = args::gen_updater(&fields, false)?; (quote!( { #( #field_names, )* }), quote!( { #update } )) } Unit => (quote!(), quote!({})), @@ -630,38 +626,38 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { } }; - quote! { + Ok(quote! { Self :: #variant_name #pattern if #sub_name == __clap_name => { let (_, mut __clap_arg_sub_matches) = __clap_arg_matches.remove_subcommand().unwrap(); let __clap_arg_matches = &mut __clap_arg_sub_matches; #updater } - } - }); + }) + }).collect::, _>>()?; let child_subcommands = flatten.iter().map(|(variant, _attrs)| { let variant_name = &variant.ident; match variant.fields { Unnamed(ref fields) if fields.unnamed.len() == 1 => { let ty = &fields.unnamed[0]; - quote! { + Ok(quote! { if <#ty as clap::Subcommand>::has_subcommand(__clap_name) { if let Self :: #variant_name (child) = s { <#ty as clap::FromArgMatches>::update_from_arg_matches_mut(child, __clap_arg_matches)?; return ::std::result::Result::Ok(()); } } - } + }) } _ => abort!( variant, "`flatten` is usable only with single-typed tuple variants" ), } - }); + }).collect::, _>>()?; let raw_deprecated = args::raw_deprecated(); - quote! { + Ok(quote! { fn update_from_arg_matches_mut<'b>( &mut self, __clap_arg_matches: &mut clap::ArgMatches, @@ -679,5 +675,5 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream { } ::std::result::Result::Ok(()) } - } + }) } diff --git a/clap_derive/src/derives/value_enum.rs b/clap_derive/src/derives/value_enum.rs index a1411d02ab0b720e441684624b10fcfde51acb37..6f107c01c374e03a29847a8d9b520caab65c220a 100644 --- a/clap_derive/src/derives/value_enum.rs +++ b/clap_derive/src/derives/value_enum.rs @@ -9,39 +9,36 @@ // except according to those terms. use proc_macro2::TokenStream; -use proc_macro_error::{abort, abort_call_site}; use quote::quote; use quote::quote_spanned; use syn::{spanned::Spanned, Data, DeriveInput, Fields, Ident, Variant}; -use crate::dummies; use crate::item::{Item, Kind, Name}; -pub fn derive_value_enum(input: &DeriveInput) -> TokenStream { +pub fn derive_value_enum(input: &DeriveInput) -> Result { let ident = &input.ident; - dummies::value_enum(ident); - match input.data { Data::Enum(ref e) => { let name = Name::Derived(ident.clone()); - let item = Item::from_value_enum(input, name); - let variants = e - .variants - .iter() - .map(|variant| { - let item = - Item::from_value_enum_variant(variant, item.casing(), item.env_casing()); - (variant, item) - }) - .collect::>(); + let item = Item::from_value_enum(input, name)?; + let mut variants = Vec::new(); + for variant in &e.variants { + let item = + Item::from_value_enum_variant(variant, item.casing(), item.env_casing())?; + variants.push((variant, item)); + } gen_for_enum(&item, ident, &variants) } _ => abort_call_site!("`#[derive(ValueEnum)]` only supports enums"), } } -pub fn gen_for_enum(item: &Item, item_name: &Ident, variants: &[(&Variant, Item)]) -> TokenStream { +pub fn gen_for_enum( + item: &Item, + item_name: &Ident, + variants: &[(&Variant, Item)], +) -> Result { if !matches!(&*item.kind(), Kind::Value) { abort! { item.kind().span(), "`{}` cannot be used with `value`", @@ -49,11 +46,11 @@ pub fn gen_for_enum(item: &Item, item_name: &Ident, variants: &[(&Variant, Item) } } - let lits = lits(variants); + let lits = lits(variants)?; let value_variants = gen_value_variants(&lits); let to_possible_value = gen_to_possible_value(item, &lits); - quote! { + Ok(quote! { #[allow(dead_code, unreachable_code, unused_variables, unused_braces)] #[allow( clippy::style, @@ -65,39 +62,37 @@ pub fn gen_for_enum(item: &Item, item_name: &Ident, variants: &[(&Variant, Item) clippy::nursery, clippy::cargo, clippy::suspicious_else_formatting, + clippy::almost_swapped, )] - #[deny(clippy::correctness)] impl clap::ValueEnum for #item_name { #value_variants #to_possible_value } - } + }) } -fn lits(variants: &[(&Variant, Item)]) -> Vec<(TokenStream, Ident)> { - variants - .iter() - .filter_map(|(variant, item)| { - if let Kind::Skip(_, _) = &*item.kind() { - None - } else { - if !matches!(variant.fields, Fields::Unit) { - abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped"); - } - let fields = item.field_methods(); - let deprecations = item.deprecations(); - let name = item.cased_name(); - Some(( - quote_spanned! { variant.span()=> { - #deprecations - clap::builder::PossibleValue::new(#name) - #fields - }}, - variant.ident.clone(), - )) - } - }) - .collect::>() +fn lits(variants: &[(&Variant, Item)]) -> Result, syn::Error> { + let mut genned = Vec::new(); + for (variant, item) in variants { + if let Kind::Skip(_, _) = &*item.kind() { + continue; + } + if !matches!(variant.fields, Fields::Unit) { + abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped"); + } + let fields = item.field_methods(); + let deprecations = item.deprecations(); + let name = item.cased_name(); + genned.push(( + quote_spanned! { variant.span()=> { + #deprecations + clap::builder::PossibleValue::new(#name) + #fields + }}, + variant.ident.clone(), + )); + } + Ok(genned) } fn gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream { diff --git a/clap_derive/src/dummies.rs b/clap_derive/src/dummies.rs index 1f54e1dbcbbf21d6b8e06b5cb980d8b86f366605..b10bedc64a4e40e4df9f54fb2642f2f21f98b25c 100644 --- a/clap_derive/src/dummies.rs +++ b/clap_derive/src/dummies.rs @@ -1,23 +1,20 @@ //! Dummy implementations that we emit along with an error. use proc_macro2::Ident; -use proc_macro_error::append_dummy; use quote::quote; -pub fn parser_struct(name: &Ident) { - into_app(name); - args(name); - append_dummy(quote!( impl clap::Parser for #name {} )); +#[must_use] +pub fn parser(name: &Ident) -> proc_macro2::TokenStream { + let into_app = into_app(name); + quote!( + impl clap::Parser for #name {} + #into_app + ) } -pub fn parser_enum(name: &Ident) { - into_app(name); - subcommand(name); - append_dummy(quote!( impl clap::Parser for #name {} )); -} - -pub fn into_app(name: &Ident) { - append_dummy(quote! { +#[must_use] +pub fn into_app(name: &Ident) -> proc_macro2::TokenStream { + quote! { impl clap::CommandFactory for #name { fn command<'b>() -> clap::Command { unimplemented!() @@ -26,11 +23,12 @@ pub fn into_app(name: &Ident) { unimplemented!() } } - }); + } } -pub fn from_arg_matches(name: &Ident) { - append_dummy(quote! { +#[must_use] +pub fn from_arg_matches(name: &Ident) -> proc_macro2::TokenStream { + quote! { impl clap::FromArgMatches for #name { fn from_arg_matches(_m: &clap::ArgMatches) -> ::std::result::Result { unimplemented!() @@ -39,12 +37,13 @@ pub fn from_arg_matches(name: &Ident) { unimplemented!() } } - }); + } } -pub fn subcommand(name: &Ident) { - from_arg_matches(name); - append_dummy(quote! { +#[must_use] +pub fn subcommand(name: &Ident) -> proc_macro2::TokenStream { + let from_arg_matches = from_arg_matches(name); + quote! { impl clap::Subcommand for #name { fn augment_subcommands(_cmd: clap::Command) -> clap::Command { unimplemented!() @@ -56,12 +55,14 @@ pub fn subcommand(name: &Ident) { unimplemented!() } } - }); + #from_arg_matches + } } -pub fn args(name: &Ident) { - from_arg_matches(name); - append_dummy(quote! { +#[must_use] +pub fn args(name: &Ident) -> proc_macro2::TokenStream { + let from_arg_matches = from_arg_matches(name); + quote! { impl clap::Args for #name { fn augment_args(_cmd: clap::Command) -> clap::Command { unimplemented!() @@ -70,11 +71,13 @@ pub fn args(name: &Ident) { unimplemented!() } } - }); + #from_arg_matches + } } -pub fn value_enum(name: &Ident) { - append_dummy(quote! { +#[must_use] +pub fn value_enum(name: &Ident) -> proc_macro2::TokenStream { + quote! { impl clap::ValueEnum for #name { fn value_variants<'a>() -> &'a [Self]{ unimplemented!() @@ -86,5 +89,5 @@ pub fn value_enum(name: &Ident) { unimplemented!() } } - }) + } } diff --git a/clap_derive/src/item.rs b/clap_derive/src/item.rs index 5e8272ac0c1e04939c216d179f6592cd68cd8103..b7814f7de9ddb56870818f174b390526b296c747 100644 --- a/clap_derive/src/item.rs +++ b/clap_derive/src/item.rs @@ -16,7 +16,6 @@ use std::env; use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; use proc_macro2::{self, Span, TokenStream}; -use proc_macro_error::abort; use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::DeriveInput; use syn::{self, ext::IdentExt, spanned::Spanned, Attribute, Field, Ident, LitStr, Type, Variant}; @@ -53,7 +52,7 @@ pub struct Item { } impl Item { - pub fn from_args_struct(input: &DeriveInput, name: Name) -> Self { + pub fn from_args_struct(input: &DeriveInput, name: Name) -> Result { let ident = input.ident.clone(); let span = input.ident.span(); let attrs = &input.attrs; @@ -62,15 +61,15 @@ impl Item { let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span); let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); - let parsed_attrs = ClapAttr::parse_all(attrs); - res.infer_kind(&parsed_attrs); - res.push_attrs(&parsed_attrs); + let parsed_attrs = ClapAttr::parse_all(attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; res.push_doc_comment(attrs, "about", Some("long_about")); - res + Ok(res) } - pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Self { + pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Result { let ident = input.ident.clone(); let span = input.ident.span(); let attrs = &input.attrs; @@ -79,15 +78,15 @@ impl Item { let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span); let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); - let parsed_attrs = ClapAttr::parse_all(attrs); - res.infer_kind(&parsed_attrs); - res.push_attrs(&parsed_attrs); + let parsed_attrs = ClapAttr::parse_all(attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; res.push_doc_comment(attrs, "about", Some("long_about")); - res + Ok(res) } - pub fn from_value_enum(input: &DeriveInput, name: Name) -> Self { + pub fn from_value_enum(input: &DeriveInput, name: Name) -> Result { let ident = input.ident.clone(); let span = input.ident.span(); let attrs = &input.attrs; @@ -96,9 +95,9 @@ impl Item { let kind = Sp::new(Kind::Value, span); let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); - let parsed_attrs = ClapAttr::parse_all(attrs); - res.infer_kind(&parsed_attrs); - res.push_attrs(&parsed_attrs); + let parsed_attrs = ClapAttr::parse_all(attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; // Ignoring `push_doc_comment` as there is no top-level clap builder to add documentation // to @@ -110,14 +109,14 @@ impl Item { ); } - res + Ok(res) } pub fn from_subcommand_variant( variant: &Variant, struct_casing: Sp, env_casing: Sp, - ) -> Self { + ) -> Result { let name = variant.ident.clone(); let ident = variant.ident.clone(); let span = variant.span(); @@ -138,9 +137,9 @@ impl Item { env_casing, kind, ); - let parsed_attrs = ClapAttr::parse_all(&variant.attrs); - res.infer_kind(&parsed_attrs); - res.push_attrs(&parsed_attrs); + let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; if matches!(&*res.kind, Kind::Command(_) | Kind::Subcommand(_)) { res.push_doc_comment(&variant.attrs, "about", Some("long_about")); } @@ -164,14 +163,14 @@ impl Item { | Kind::Arg(_) => (), } - res + Ok(res) } pub fn from_value_enum_variant( variant: &Variant, argument_casing: Sp, env_casing: Sp, - ) -> Self { + ) -> Result { let ident = variant.ident.clone(); let span = variant.span(); let kind = Sp::new(Kind::Value, span); @@ -183,21 +182,21 @@ impl Item { env_casing, kind, ); - let parsed_attrs = ClapAttr::parse_all(&variant.attrs); - res.infer_kind(&parsed_attrs); - res.push_attrs(&parsed_attrs); + let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; if matches!(&*res.kind, Kind::Value) { res.push_doc_comment(&variant.attrs, "help", None); } - res + Ok(res) } pub fn from_args_field( field: &Field, struct_casing: Sp, env_casing: Sp, - ) -> Self { + ) -> Result { let name = field.ident.clone().unwrap(); let ident = field.ident.clone().unwrap(); let span = field.span(); @@ -211,9 +210,9 @@ impl Item { env_casing, kind, ); - let parsed_attrs = ClapAttr::parse_all(&field.attrs); - res.infer_kind(&parsed_attrs); - res.push_attrs(&parsed_attrs); + let parsed_attrs = ClapAttr::parse_all(&field.attrs)?; + res.infer_kind(&parsed_attrs)?; + res.push_attrs(&parsed_attrs)?; if matches!(&*res.kind, Kind::Arg(_)) { res.push_doc_comment(&field.attrs, "help", Some("long_help")); } @@ -244,7 +243,7 @@ impl Item { | Kind::ExternalSubcommand => {} } - res + Ok(res) } fn new( @@ -329,7 +328,7 @@ impl Item { } } - fn infer_kind(&mut self, attrs: &[ClapAttr]) { + fn infer_kind(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> { for attr in attrs { if let Some(AttrValue::Call(_)) = &attr.value { continue; @@ -339,7 +338,7 @@ impl Item { let kind = match &attr.magic { Some(MagicAttrName::FromGlobal) => { if attr.value.is_some() { - let expr = attr.value_or_abort(); + let expr = attr.value_or_abort()?; abort!(expr, "attribute `{}` does not accept a value", attr.name); } let ty = self @@ -352,7 +351,7 @@ impl Item { } Some(MagicAttrName::Subcommand) if attr.value.is_none() => { if attr.value.is_some() { - let expr = attr.value_or_abort(); + let expr = attr.value_or_abort()?; abort!(expr, "attribute `{}` does not accept a value", attr.name); } let ty = self @@ -365,7 +364,7 @@ impl Item { } Some(MagicAttrName::ExternalSubcommand) if attr.value.is_none() => { if attr.value.is_some() { - let expr = attr.value_or_abort(); + let expr = attr.value_or_abort()?; abort!(expr, "attribute `{}` does not accept a value", attr.name); } let kind = Sp::new(Kind::ExternalSubcommand, attr.name.clone().span()); @@ -373,7 +372,7 @@ impl Item { } Some(MagicAttrName::Flatten) if attr.value.is_none() => { if attr.value.is_some() { - let expr = attr.value_or_abort(); + let expr = attr.value_or_abort()?; abort!(expr, "attribute `{}` does not accept a value", attr.name); } let ty = self @@ -396,12 +395,14 @@ impl Item { }; if let Some(kind) = kind { - self.set_kind(kind); + self.set_kind(kind)?; } } + + Ok(()) } - fn push_attrs(&mut self, attrs: &[ClapAttr]) { + fn push_attrs(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> { for attr in attrs { let actual_attr_kind = *attr.kind.get(); let expected_attr_kind = self.kind.attr_kind(); @@ -437,7 +438,7 @@ impl Item { match &attr.magic { Some(MagicAttrName::Short) if attr.value.is_none() => { - assert_attr_kind(attr, &[AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Arg])?; self.push_method( *attr.kind.get(), @@ -447,13 +448,13 @@ impl Item { } Some(MagicAttrName::Long) if attr.value.is_none() => { - assert_attr_kind(attr, &[AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Arg])?; self.push_method(*attr.kind.get(), attr.name.clone(), self.name.clone().translate(*self.casing)); } Some(MagicAttrName::ValueParser) if attr.value.is_none() => { - assert_attr_kind(attr, &[AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Arg])?; self.deprecations.push(Deprecation { span: attr.name.span(), @@ -465,7 +466,7 @@ impl Item { } Some(MagicAttrName::Action) if attr.value.is_none() => { - assert_attr_kind(attr, &[AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Arg])?; self.deprecations.push(Deprecation { span: attr.name.span(), @@ -477,7 +478,7 @@ impl Item { } Some(MagicAttrName::Env) if attr.value.is_none() => { - assert_attr_kind(attr, &[AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Arg])?; self.push_method( *attr.kind.get(), @@ -487,7 +488,7 @@ impl Item { } Some(MagicAttrName::ValueEnum) if attr.value.is_none() => { - assert_attr_kind(attr, &[AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Arg])?; self.is_enum = true } @@ -497,45 +498,45 @@ impl Item { } Some(MagicAttrName::About) if attr.value.is_none() => { - assert_attr_kind(attr, &[AttrKind::Command]); + assert_attr_kind(attr, &[AttrKind::Command])?; if let Some(method) = - Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION") + Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION")? { self.methods.push(method); } } Some(MagicAttrName::LongAbout) if attr.value.is_none() => { - assert_attr_kind(attr, &[AttrKind::Command]); + assert_attr_kind(attr, &[AttrKind::Command])?; self.force_long_help = true; } Some(MagicAttrName::LongHelp) if attr.value.is_none() => { - assert_attr_kind(attr, &[AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Arg])?; self.force_long_help = true; } Some(MagicAttrName::Author) if attr.value.is_none() => { - assert_attr_kind(attr, &[AttrKind::Command]); + assert_attr_kind(attr, &[AttrKind::Command])?; - if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS") { + if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS")? { self.methods.push(method); } } Some(MagicAttrName::Version) if attr.value.is_none() => { - assert_attr_kind(attr, &[AttrKind::Command]); + assert_attr_kind(attr, &[AttrKind::Command])?; - if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION") { + if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION")? { self.methods.push(method); } } Some(MagicAttrName::DefaultValueT) => { - assert_attr_kind(attr, &[AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Arg])?; let ty = if let Some(ty) = self.ty.as_ref() { ty @@ -543,7 +544,7 @@ impl Item { abort!( attr.name.clone(), "#[arg(default_value_t)] (without an argument) can be used \ - only on field level"; + only on field level\n\n= note: {note}\n\n", note = "see \ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") @@ -583,7 +584,7 @@ impl Item { } Some(MagicAttrName::DefaultValuesT) => { - assert_attr_kind(attr, &[AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Arg])?; let ty = if let Some(ty) = self.ty.as_ref() { ty @@ -591,18 +592,18 @@ impl Item { abort!( attr.name.clone(), "#[arg(default_values_t)] (without an argument) can be used \ - only on field level"; + only on field level\n\n= note: {note}\n\n", note = "see \ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") }; - let expr = attr.value_or_abort(); + let expr = attr.value_or_abort()?; let container_type = Ty::from_syn_ty(ty); if *container_type != Ty::Vec { abort!( attr.name.clone(), - "#[arg(default_values_t)] can be used only on Vec types"; + "#[arg(default_values_t)] can be used only on Vec types\n\n= note: {note}\n\n", note = "see \ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") @@ -667,7 +668,7 @@ impl Item { } Some(MagicAttrName::DefaultValueOsT) => { - assert_attr_kind(attr, &[AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Arg])?; let ty = if let Some(ty) = self.ty.as_ref() { ty @@ -675,7 +676,7 @@ impl Item { abort!( attr.name.clone(), "#[arg(default_value_os_t)] (without an argument) can be used \ - only on field level"; + only on field level\n\n= note: {note}\n\n", note = "see \ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") @@ -715,7 +716,7 @@ impl Item { } Some(MagicAttrName::DefaultValuesOsT) => { - assert_attr_kind(attr, &[AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Arg])?; let ty = if let Some(ty) = self.ty.as_ref() { ty @@ -723,18 +724,18 @@ impl Item { abort!( attr.name.clone(), "#[arg(default_values_os_t)] (without an argument) can be used \ - only on field level"; + only on field level\n\n= note: {note}\n\n", note = "see \ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") }; - let expr = attr.value_or_abort(); + let expr = attr.value_or_abort()?; let container_type = Ty::from_syn_ty(ty); if *container_type != Ty::Vec { abort!( attr.name.clone(), - "#[arg(default_values_os_t)] can be used only on Vec types"; + "#[arg(default_values_os_t)] can be used only on Vec types\n\n= note: {note}\n\n", note = "see \ https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes") @@ -799,29 +800,29 @@ impl Item { } Some(MagicAttrName::NextDisplayOrder) => { - assert_attr_kind(attr, &[AttrKind::Command]); + assert_attr_kind(attr, &[AttrKind::Command])?; - let expr = attr.value_or_abort(); + let expr = attr.value_or_abort()?; self.next_display_order = Some(Method::new(attr.name.clone(), quote!(#expr))); } Some(MagicAttrName::NextHelpHeading) => { - assert_attr_kind(attr, &[AttrKind::Command]); + assert_attr_kind(attr, &[AttrKind::Command])?; - let expr = attr.value_or_abort(); + let expr = attr.value_or_abort()?; self.next_help_heading = Some(Method::new(attr.name.clone(), quote!(#expr))); } Some(MagicAttrName::RenameAll) => { - let lit = attr.lit_str_or_abort(); - self.casing = CasingStyle::from_lit(lit); + let lit = attr.lit_str_or_abort()?; + self.casing = CasingStyle::from_lit(lit)?; } Some(MagicAttrName::RenameAllEnv) => { - assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg]); + assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg])?; - let lit = attr.lit_str_or_abort(); - self.env_casing = CasingStyle::from_lit(lit); + let lit = attr.lit_str_or_abort()?; + self.env_casing = CasingStyle::from_lit(lit)?; } Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => { @@ -839,20 +840,20 @@ impl Item { | Some(MagicAttrName::Author) | Some(MagicAttrName::Version) => { - let expr = attr.value_or_abort(); + let expr = attr.value_or_abort()?; self.push_method(*attr.kind.get(), attr.name.clone(), expr); } // Magic only for the default, otherwise just forward to the builder Some(MagicAttrName::ValueParser) | Some(MagicAttrName::Action) => { - let expr = attr.value_or_abort(); + let expr = attr.value_or_abort()?; self.push_method(*attr.kind.get(), attr.name.clone(), expr); } // Directives that never receive a value Some(MagicAttrName::ValueEnum) | Some(MagicAttrName::VerbatimDocComment) => { - let expr = attr.value_or_abort(); + let expr = attr.value_or_abort()?; abort!(expr, "attribute `{}` does not accept a value", attr.name); } @@ -883,6 +884,8 @@ impl Item { ); } } + + Ok(()) } fn push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>) { @@ -912,7 +915,7 @@ impl Item { } } - fn set_kind(&mut self, kind: Sp) { + fn set_kind(&mut self, kind: Sp) -> Result<(), syn::Error> { match (self.kind.get(), kind.get()) { (Kind::Arg(_), Kind::FromGlobal(_)) | (Kind::Arg(_), Kind::Subcommand(_)) @@ -932,6 +935,7 @@ impl Item { abort!(kind.span(), "`{}` cannot be used with `{}`", new, old); } } + Ok(()) } pub fn find_default_method(&self) -> Option<&Method> { @@ -1211,19 +1215,21 @@ impl Method { Method { name, args } } - fn from_env(ident: Ident, env_var: &str) -> Option { + fn from_env(ident: Ident, env_var: &str) -> Result, syn::Error> { let mut lit = match env::var(env_var) { Ok(val) => { if val.is_empty() { - return None; + return Ok(None); } LitStr::new(&val, ident.span()) } Err(_) => { - abort!(ident, - "cannot derive `{}` from Cargo.toml", ident; - note = "`{}` environment variable is not set", env_var; - help = "use `{} = \"...\"` to set {} manually", ident, ident; + abort!( + ident, + "cannot derive `{}` from Cargo.toml\n\n= note: {note}\n\n= help: {help}\n\n", + ident, + note = format_args!("`{}` environment variable is not set", env_var), + help = format_args!("use `{} = \"...\"` to set {} manually", ident, ident) ); } }; @@ -1233,7 +1239,7 @@ impl Method { lit = LitStr::new(&edited, lit.span()); } - Some(Method::new(ident, quote!(#lit))) + Ok(Some(Method::new(ident, quote!(#lit)))) } pub(crate) fn args(&self) -> &TokenStream { @@ -1299,7 +1305,7 @@ impl ToTokens for Deprecation { } } -fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) { +fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) -> Result<(), syn::Error> { if *attr.kind.get() == AttrKind::Clap || *attr.kind.get() == AttrKind::StructOpt { // deprecated } else if !possible_kind.contains(attr.kind.get()) { @@ -1315,6 +1321,7 @@ fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) { options.join(", ") ); } + Ok(()) } /// replace all `:` with `, ` when not inside the `<>` @@ -1364,13 +1371,13 @@ pub enum CasingStyle { } impl CasingStyle { - fn from_lit(name: &LitStr) -> Sp { + fn from_lit(name: &LitStr) -> Result, syn::Error> { use self::CasingStyle::*; let normalized = name.value().to_upper_camel_case().to_lowercase(); let cs = |kind| Sp::new(kind, name.span()); - match normalized.as_ref() { + let s = match normalized.as_ref() { "camel" | "camelcase" => cs(Camel), "kebab" | "kebabcase" => cs(Kebab), "pascal" | "pascalcase" => cs(Pascal), @@ -1380,7 +1387,8 @@ impl CasingStyle { "upper" | "uppercase" => cs(Upper), "verbatim" | "verbatimcase" => cs(Verbatim), s => abort!(name, "unsupported casing: `{}`", s), - } + }; + Ok(s) } } diff --git a/clap_derive/src/lib.rs b/clap_derive/src/lib.rs index 84f2f0ded9fa12d34a2f0cd2e66ab913225866e9..d3cae9b65d367827441a2c8a2590b34ec42f5151 100644 --- a/clap_derive/src/lib.rs +++ b/clap_derive/src/lib.rs @@ -19,8 +19,11 @@ extern crate proc_macro; use proc_macro::TokenStream; -use proc_macro_error::proc_macro_error; use syn::{parse_macro_input, DeriveInput}; +use syn::{Data, DataStruct, Fields}; + +#[macro_use] +mod macros; mod attr; mod derives; @@ -30,10 +33,14 @@ mod utils; /// Generates the `ValueEnum` impl. #[proc_macro_derive(ValueEnum, attributes(clap, value))] -#[proc_macro_error] pub fn value_enum(input: TokenStream) -> TokenStream { let input: DeriveInput = parse_macro_input!(input); - derives::derive_value_enum(&input).into() + derives::derive_value_enum(&input) + .unwrap_or_else(|err| { + let dummy = dummies::value_enum(&input.ident); + to_compile_error(err, dummy) + }) + .into() } /// Generates the `Parser` implementation. @@ -43,24 +50,67 @@ pub fn value_enum(input: TokenStream) -> TokenStream { /// implementing a conversion code to instantiate an instance of the user /// context struct. #[proc_macro_derive(Parser, attributes(clap, structopt, command, arg, group))] -#[proc_macro_error] pub fn parser(input: TokenStream) -> TokenStream { let input: DeriveInput = parse_macro_input!(input); - derives::derive_parser(&input).into() + derives::derive_parser(&input) + .unwrap_or_else(|err| { + let specific_dummy = match input.data { + Data::Struct(DataStruct { + fields: Fields::Named(ref _fields), + .. + }) => Some(dummies::args(&input.ident)), + Data::Struct(DataStruct { + fields: Fields::Unit, + .. + }) => Some(dummies::args(&input.ident)), + Data::Enum(_) => Some(dummies::subcommand(&input.ident)), + _ => None, + }; + let dummy = specific_dummy + .map(|specific_dummy| { + let parser_dummy = dummies::parser(&input.ident); + quote::quote! { + #parser_dummy + #specific_dummy + } + }) + .unwrap_or_else(|| quote::quote!()); + to_compile_error(err, dummy) + }) + .into() } /// Generates the `Subcommand` impl. #[proc_macro_derive(Subcommand, attributes(clap, command, arg, group))] -#[proc_macro_error] pub fn subcommand(input: TokenStream) -> TokenStream { let input: DeriveInput = parse_macro_input!(input); - derives::derive_subcommand(&input).into() + derives::derive_subcommand(&input) + .unwrap_or_else(|err| { + let dummy = dummies::subcommand(&input.ident); + to_compile_error(err, dummy) + }) + .into() } /// Generates the `Args` impl. #[proc_macro_derive(Args, attributes(clap, command, arg, group))] -#[proc_macro_error] pub fn args(input: TokenStream) -> TokenStream { let input: DeriveInput = parse_macro_input!(input); - derives::derive_args(&input).into() + derives::derive_args(&input) + .unwrap_or_else(|err| { + let dummy = dummies::args(&input.ident); + to_compile_error(err, dummy) + }) + .into() +} + +fn to_compile_error( + error: syn::Error, + dummy: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let compile_errors = error.to_compile_error(); + quote::quote!( + #dummy + #compile_errors + ) } diff --git a/clap_derive/src/macros.rs b/clap_derive/src/macros.rs new file mode 100644 index 0000000000000000000000000000000000000000..282048bc1752e796205c17ca24555b98a948af0b --- /dev/null +++ b/clap_derive/src/macros.rs @@ -0,0 +1,21 @@ +macro_rules! format_err { + ($obj:expr, $($format:tt)+) => {{ + #[allow(unused_imports)] + use $crate::utils::error::*; + let msg = format!($($format)+); + $obj.EXPECTED_Span_OR_ToTokens(msg) + }}; +} + +macro_rules! abort { + ($obj:expr, $($format:tt)+) => {{ + return Err(format_err!($obj, $($format)+)); + }}; +} + +macro_rules! abort_call_site { + ($($format:tt)+) => {{ + let span = proc_macro2::Span::call_site(); + abort!(span, $($format)+) + }}; +} diff --git a/clap_derive/src/utils/doc_comments.rs b/clap_derive/src/utils/doc_comments.rs index 5183b6b25e1748ce4975c26a60c6416fe13c3dc9..63c6ad1ef32883d32133cf41d2810365cac1916d 100644 --- a/clap_derive/src/utils/doc_comments.rs +++ b/clap_derive/src/utils/doc_comments.rs @@ -6,24 +6,26 @@ use std::iter; pub fn extract_doc_comment(attrs: &[syn::Attribute]) -> Vec { - use syn::Lit::*; - use syn::Meta::*; - use syn::MetaNameValue; - // multiline comments (`/** ... */`) may have LFs (`\n`) in them, // we need to split so we could handle the lines correctly // // we also need to remove leading and trailing blank lines let mut lines: Vec<_> = attrs .iter() - .filter(|attr| attr.path.is_ident("doc")) + .filter(|attr| attr.path().is_ident("doc")) .filter_map(|attr| { - if let Ok(NameValue(MetaNameValue { lit: Str(s), .. })) = attr.parse_meta() { - Some(s.value()) - } else { - // non #[doc = "..."] attributes are not our concern - // we leave them for rustc to handle - None + // non #[doc = "..."] attributes are not our concern + // we leave them for rustc to handle + match &attr.meta { + syn::Meta::NameValue(syn::MetaNameValue { + value: + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(s), + .. + }), + .. + }) => Some(s.value()), + _ => None, } }) .skip_while(|s| is_blank(s)) diff --git a/clap_derive/src/utils/error.rs b/clap_derive/src/utils/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..276e349493caa5b5d02db074a13dcf743c741fdf --- /dev/null +++ b/clap_derive/src/utils/error.rs @@ -0,0 +1,22 @@ +pub trait SpanError { + #[allow(non_snake_case)] + fn EXPECTED_Span_OR_ToTokens(&self, msg: D) -> syn::Error; +} + +pub trait ToTokensError { + #[allow(non_snake_case)] + fn EXPECTED_Span_OR_ToTokens(&self, msg: D) -> syn::Error; +} + +impl ToTokensError for T { + fn EXPECTED_Span_OR_ToTokens(&self, msg: D) -> syn::Error { + // Curb monomorphization from generating too many identical `new_spanned`. + syn::Error::new_spanned(self.to_token_stream(), msg) + } +} + +impl SpanError for proc_macro2::Span { + fn EXPECTED_Span_OR_ToTokens(&self, msg: D) -> syn::Error { + syn::Error::new(*self, msg) + } +} diff --git a/clap_derive/src/utils/mod.rs b/clap_derive/src/utils/mod.rs index 9f8b6f380abf2b25b5d1e1833f188808e6307444..13e6e710620d4e632c4b14b1e3489c725d1b185b 100644 --- a/clap_derive/src/utils/mod.rs +++ b/clap_derive/src/utils/mod.rs @@ -1,3 +1,5 @@ +pub mod error; + mod doc_comments; mod spanned; mod ty; diff --git a/clap_lex/BUILD.gn b/clap_lex/BUILD.gn index 760036b03f7659c360e8b644d80f8d187cbd40da..e63c4ea58aeb6cc8f344c0650261fadbe59327b5 100644 --- a/clap_lex/BUILD.gn +++ b/clap_lex/BUILD.gn @@ -14,14 +14,14 @@ import("//build/ohos.gni") ohos_cargo_crate("lib") { - crate_name = "clap_lex" - crate_type = "rlib" - crate_root = "src/lib.rs" + crate_name = "clap_lex" + crate_type = "rlib" + crate_root = "src/lib.rs" - sources = ["src/lib.rs"] - edition = "2021" - cargo_pkg_version = "0.3.1" - cargo_pkg_name = "clap_lex" - cargo_pkg_description = "Minimal, flexible command line parser" - deps = ["//third_party/rust/crates/os_str_bytes:lib"] + sources = [ "src/lib.rs" ] + edition = "2021" + cargo_pkg_version = "0.3.3" + cargo_pkg_name = "clap_lex" + cargo_pkg_description = "Minimal, flexible command line parser" + deps = [ "//third_party/rust/crates/os_str_bytes:lib" ] } diff --git a/clap_lex/CHANGELOG.md b/clap_lex/CHANGELOG.md index 6b64af9063453237ce5e5727911f2f5cb7de96b5..9e2046799c4f32d1512ea6ddf053fa3886c6534b 100644 --- a/clap_lex/CHANGELOG.md +++ b/clap_lex/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +## [0.3.3] - 2023-03-16 + +## [0.3.2] - 2023-02-23 + ## [0.3.1] - 2023-01-13 ### Compatibility @@ -46,7 +50,9 @@ MSRV changed to 1.64.0 - Drop `memchr` dependency -[Unreleased]: https://github.com/clap-rs/clap/compare/clap_lex-v0.3.1...HEAD +[Unreleased]: https://github.com/clap-rs/clap/compare/clap_lex-v0.3.3...HEAD +[0.3.3]: https://github.com/clap-rs/clap/compare/clap_lex-v0.3.2...clap_lex-v0.3.3 +[0.3.2]: https://github.com/clap-rs/clap/compare/clap_lex-v0.3.1...clap_lex-v0.3.2 [0.3.1]: https://github.com/clap-rs/clap/compare/clap_lex-v0.3.0...clap_lex-v0.3.1 [0.3.0]: https://github.com/clap-rs/clap/compare/clap_lex-v0.2.4...clap_lex-v0.3.0 [0.2.4]: https://github.com/clap-rs/clap/compare/clap_lex-v0.2.3...clap_lex-v0.2.4 diff --git a/clap_lex/Cargo.toml b/clap_lex/Cargo.toml index e8d9673ee8fb3ed5b12af8d6588058961fde9566..da2d1e7351f61323aad5c33757281e74fc93ea82 100644 --- a/clap_lex/Cargo.toml +++ b/clap_lex/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clap_lex" -version = "0.3.1" +version = "0.3.3" description = "Minimal, flexible command line parser" repository = "https://github.com/clap-rs/clap/tree/master/clap_lex" categories = ["command-line-interface"] @@ -30,4 +30,4 @@ pre-release-replacements = [ bench = false [dependencies] -os_str_bytes = { version = "6.0", default-features = false, features = ["raw_os_str"] } +os_str_bytes = { version = "6.0.0", default-features = false, features = ["raw_os_str"] } diff --git a/clap_lex/README.md b/clap_lex/README.md index 7f4c46d3f395dbe82bff3cf641054b1cb75c70dd..93434053fb59c63882464adb2fef3eba7dcfe89e 100644 --- a/clap_lex/README.md +++ b/clap_lex/README.md @@ -5,15 +5,15 @@ [![Crates.io](https://img.shields.io/crates/v/clap_lex?style=flat-square)](https://crates.io/crates/clap_lex) [![Crates.io](https://img.shields.io/crates/d/clap_lex?style=flat-square)](https://crates.io/crates/clap_lex) -[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_lex-v0.3.1/LICENSE-APACHE) -[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_lex-v0.3.1/LICENSE-MIT) +[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_lex-v0.3.3/LICENSE-APACHE) +[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_lex-v0.3.3/LICENSE-MIT) Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT). 1. [About](#about) 2. [API Reference](https://docs.rs/clap_lex) 3. [Questions & Discussions](https://github.com/clap-rs/clap/discussions) -4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_lex-v0.3.1/clap_lex/CONTRIBUTING.md) -5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_lex-v0.3.1/README.md#sponsors) +4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_lex-v0.3.3/clap_lex/CONTRIBUTING.md) +5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_lex-v0.3.3/README.md#sponsors) ## About diff --git a/clap_lex/src/lib.rs b/clap_lex/src/lib.rs index bc4d4ba6594b87e1245b31e29e2d1a12af411258..bc9720fc570ee0cb71a7d8cafab6b259949b2520 100644 --- a/clap_lex/src/lib.rs +++ b/clap_lex/src/lib.rs @@ -7,8 +7,10 @@ //! # Examples //! //! ```rust -//! # use std::path::PathBuf; -//! # type BoxedError = Box; +//! use std::path::PathBuf; +//! +//! type BoxedError = Box; +//! //! #[derive(Debug)] //! struct Args { //! paths: Vec, diff --git a/clap_mangen/CHANGELOG.md b/clap_mangen/CHANGELOG.md index fbb4b9980af68d0cc1a36e24d8b61faf7d29ed6f..69634b1dbc864b1f18a619fdfe2d99100d47839c 100644 --- a/clap_mangen/CHANGELOG.md +++ b/clap_mangen/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +## [0.2.10] - 2023-03-16 + +## [0.2.9] - 2023-02-22 + +### Fixes + +- Only show value names if a value is taken + +## [0.2.8] - 2023-02-15 + ## [0.2.7] - 2023-01-13 ### Compatibility @@ -72,7 +82,10 @@ MSRV changed to 1.64.0 - Expanded the documentation -[Unreleased]: https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.7...HEAD +[Unreleased]: https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.10...HEAD +[0.2.10]: https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.9...clap_mangen-v0.2.10 +[0.2.9]: https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.8...clap_mangen-v0.2.9 +[0.2.8]: https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.7...clap_mangen-v0.2.8 [0.2.7]: https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.6...clap_mangen-v0.2.7 [0.2.6]: https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.5...clap_mangen-v0.2.6 [0.2.5]: https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.4...clap_mangen-v0.2.5 diff --git a/clap_mangen/Cargo.toml b/clap_mangen/Cargo.toml index 7696c7c0bceebae2e9fc9c40193a41be8072c83c..9eb3b428ebf637d11267f090b7168683d2089be9 100644 --- a/clap_mangen/Cargo.toml +++ b/clap_mangen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clap_mangen" -version = "0.2.7" +version = "0.2.10" description = "A manpage generator for clap" repository = "https://github.com/clap-rs/clap/tree/master/clap_mangen" categories = ["command-line-interface"] @@ -36,7 +36,7 @@ roff = "0.2.1" clap = { path = "../", version = "4.0.0", default-features = false, features = ["std", "env"] } [dev-dependencies] -snapbox = { version = "0.4", features = ["diff"] } +snapbox = { version = "0.4.10", features = ["diff"] } clap = { path = "../", version = "4.0.0", default-features = false, features = ["std", "help"] } [features] diff --git a/clap_mangen/README.md b/clap_mangen/README.md index 138e4206f5981486d16b1eeee396cf5cc3d8d159..fde4d79652bc54eb6ab2960ce10c353951b43834 100644 --- a/clap_mangen/README.md +++ b/clap_mangen/README.md @@ -5,16 +5,16 @@ [![Crates.io](https://img.shields.io/crates/v/clap_mangen?style=flat-square)](https://crates.io/crates/clap_mangen) [![Crates.io](https://img.shields.io/crates/d/clap_mangen?style=flat-square)](https://crates.io/crates/clap_mangen) -[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_mangen-v0.2.7/LICENSE-APACHE) -[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_mangen-v0.2.7/LICENSE-MIT) +[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_mangen-v0.2.10/LICENSE-APACHE) +[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_mangen-v0.2.10/LICENSE-MIT) Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT). 1. [About](#about) 2. [API Reference](https://docs.rs/clap_mangen) 3. [Questions & Discussions](https://github.com/clap-rs/clap/discussions) -4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_mangen-v0.2.7/clap_mangen/CONTRIBUTING.md) -5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_mangen-v0.2.7/README.md#sponsors) +4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_mangen-v0.2.10/clap_mangen/CONTRIBUTING.md) +5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_mangen-v0.2.10/README.md#sponsors) ## About @@ -25,10 +25,9 @@ Generate [ROFF](https://en.wikipedia.org/wiki/Roff_(software)) from a `clap::Com We're going to assume you want to generate your man page as part of your development rather than your shipped program having a flag to generate it. -In your `Cargo.toml`: -```toml -[build-dependencies] -clap_mangen = "0.1" +Run +```console +$ cargo add --build clap_mangen ``` In your `build.rs`: diff --git a/clap_mangen/src/render.rs b/clap_mangen/src/render.rs index ab9c9ec75c1507cc0d672f0bb233b4b5c6787772..99fa6ab447a2d9af2cf31ef00463bc045c1f981c 100644 --- a/clap_mangen/src/render.rs +++ b/clap_mangen/src/render.rs @@ -36,19 +36,19 @@ pub(crate) fn synopsis(roff: &mut Roff, cmd: &clap::Command) { match (opt.get_short(), opt.get_long()) { (Some(short), Some(long)) => { line.push(roman(lhs)); - line.push(bold(format!("-{}", short))); + line.push(bold(format!("-{short}"))); line.push(roman("|")); - line.push(bold(format!("--{}", long))); + line.push(bold(format!("--{long}",))); line.push(roman(rhs)); } (Some(short), None) => { line.push(roman(lhs)); - line.push(bold(format!("-{} ", short))); + line.push(bold(format!("-{short} "))); line.push(roman(rhs)); } (None, Some(long)) => { line.push(roman(lhs)); - line.push(bold(format!("--{}", long))); + line.push(bold(format!("--{long}"))); line.push(roman(rhs)); } (None, None) => continue, @@ -99,9 +99,11 @@ pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) { (None, None) => vec![], }; - if let Some(value) = &opt.get_value_names() { - header.push(roman("=")); - header.push(italic(value.join(" "))); + if opt.get_action().takes_values() { + if let Some(value) = &opt.get_value_names() { + header.push(roman("=")); + header.push(italic(value.join(" "))); + } } if let Some(defs) = option_default_values(opt) { @@ -168,7 +170,7 @@ pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) { header.push(roman(rhs)); if let Some(defs) = option_default_values(pos) { - header.push(roman(format!(" {}", defs))); + header.push(roman(format!(" {defs}"))); } let mut body = vec![]; @@ -273,11 +275,11 @@ fn markers(required: bool) -> (&'static str, &'static str) { } fn short_option(opt: char) -> Inline { - bold(format!("-{}", opt)) + bold(format!("-{opt}")) } fn long_option(opt: &str) -> Inline { - bold(format!("--{}", opt)) + bold(format!("--{opt}")) } fn option_help(opt: &clap::Arg) -> Option<&clap::builder::StyledStr> { @@ -319,7 +321,7 @@ fn option_default_values(opt: &clap::Arg) -> Option { .collect::>() .join(","); - return Some(format!("[default: {}]", values)); + return Some(format!("[default: {values}]")); } None @@ -343,7 +345,7 @@ fn format_possible_values(possibles: &Vec<&clap::builder::PossibleValue>) -> (Ve for value in possibles { let val_name = value.get_name(); match value.get_help() { - Some(help) => lines.push(format!("{}: {}", val_name, help)), + Some(help) => lines.push(format!("{val_name}: {help}")), None => lines.push(val_name.to_string()), } } diff --git a/clap_mangen/tests/common.rs b/clap_mangen/tests/common.rs index ca9f1f9b4fb395653b8a9de2a0cb2ec3a8afd5f0..029098c0956c265a6ff5e5e061aacb0a535e591b 100644 --- a/clap_mangen/tests/common.rs +++ b/clap_mangen/tests/common.rs @@ -312,3 +312,12 @@ pub fn possible_values_command(name: &'static str) -> clap::Command { ]), ) } + +pub fn value_name_without_arg(name: &'static str) -> clap::Command { + clap::Command::new(name).arg( + clap::Arg::new("flag") + .long("flag") + .value_name("SPURIOUS") + .action(clap::ArgAction::SetTrue), + ) +} diff --git a/clap_mangen/tests/roff.rs b/clap_mangen/tests/roff.rs index 223196c3a4474655fa5964f36db5106d13d606ca..38d1aef11bae882207c7b31a1c33b3e3be3c7d71 100644 --- a/clap_mangen/tests/roff.rs +++ b/clap_mangen/tests/roff.rs @@ -83,3 +83,10 @@ fn sub_subcommands_help() { common::assert_matches_path("tests/snapshots/sub_subcommand_help.roff", cmd.clone()); } } + +#[test] +fn value_name_without_arg() { + let name = "my-app"; + let cmd = common::value_name_without_arg(name); + common::assert_matches_path("tests/snapshots/value_name_without_arg.bash.roff", cmd); +} diff --git a/clap_mangen/tests/snapshots/value_name_without_arg.bash.roff b/clap_mangen/tests/snapshots/value_name_without_arg.bash.roff new file mode 100644 index 0000000000000000000000000000000000000000..2ffbe0210fab3823c10107446a2a28095a0887de --- /dev/null +++ b/clap_mangen/tests/snapshots/value_name_without_arg.bash.roff @@ -0,0 +1,15 @@ +.ie /n(.g .ds Aq /(aq +.el .ds Aq ' +.TH my-app 1 "my-app " +.SH NAME +my/-app +.SH SYNOPSIS +/fBmy/-app/fR [/fB/-/-flag/fR] [/fB/-h/fR|/fB/-/-help/fR] +.SH DESCRIPTION +.SH OPTIONS +.TP +/fB/-/-flag/fR + +.TP +/fB/-h/fR, /fB/-/-help/fR +Print help diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000000000000000000000000000000000000..cb4ba4cf66c7747594813c6bc3d94aa568f5cdaf --- /dev/null +++ b/deny.toml @@ -0,0 +1,149 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Note that all fields that take a lint level have these possible values: +# * deny - An error will be produced and the check will fail +# * warn - A warning will be produced, but the check will not fail +# * allow - No warning or error will be produced, though in some cases a note +# will be + +# This section is considered when running `cargo deny check advisories` +# More documentation for the advisories section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html +[advisories] +# The lint level for security vulnerabilities +vulnerability = "deny" +# The lint level for unmaintained crates +unmaintained = "warn" +# The lint level for crates that have been yanked from their source registry +yanked = "warn" +# The lint level for crates with security notices. Note that as of +# 2019-12-17 there are no security notice advisories in +# https://github.com/rustsec/advisory-db +notice = "warn" +# A list of advisory IDs to ignore. Note that ignored advisories will still +# output a note when they are encountered. +# +# e.g. "RUSTSEC-0000-0000", +ignore = [ +] + +# This section is considered when running `cargo deny check licenses` +# More documentation for the licenses section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html +[licenses] +unlicensed = "deny" +# List of explicitly allowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. +allow = [ + "MIT", + "Apache-2.0", + "Unicode-DFS-2016", + #"Apache-2.0 WITH LLVM-exception", +] +# List of explicitly disallowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. +deny = [ +] +# Lint level for licenses considered copyleft +copyleft = "deny" +# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses +# * both - The license will be approved if it is both OSI-approved *AND* FSF +# * either - The license will be approved if it is either OSI-approved *OR* FSF +# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF +# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved +# * neither - This predicate is ignored and the default lint level is used +allow-osi-fsf-free = "neither" +# Lint level used when no other predicates are matched +# 1. License isn't in the allow or deny lists +# 2. License isn't copyleft +# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" +default = "deny" +# The confidence threshold for detecting a license from license text. +# The higher the value, the more closely the license text must be to the +# canonical license text of a valid SPDX license file. +# [possible values: any between 0.0 and 1.0]. +confidence-threshold = 0.8 +# Allow 1 or more licenses on a per-crate basis, so that particular licenses +# aren't accepted for every possible crate as with the normal allow list +exceptions = [ + # Each entry is the crate and version constraint, and its specific allow + # list + #{ allow = ["Zlib"], name = "adler32", version = "*" }, +] + +[licenses.private] +# If true, ignores workspace crates that aren't published, or are only +# published to private registries. +# To see how to mark a crate as unpublished (to the official registry), +# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field. +ignore = true + +# This section is considered when running `cargo deny check bans`. +# More documentation about the 'bans' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html +[bans] +# Lint level for when multiple versions of the same crate are detected +multiple-versions = "warn" +# Lint level for when a crate version requirement is `*` +wildcards = "deny" +# The graph highlighting used when creating dotgraphs for crates +# with multiple versions +# * lowest-version - The path to the lowest versioned duplicate is highlighted +# * simplest-path - The path to the version with the fewest edges is highlighted +# * all - Both lowest-version and simplest-path are used +highlight = "all" +# The default lint level for `default` features for crates that are members of +# the workspace that is being checked. This can be overridden by allowing/denying +# `default` on a crate-by-crate basis if desired. +workspace-default-features = "allow" +# The default lint level for `default` features for external crates that are not +# members of the workspace. This can be overridden by allowing/denying `default` +# on a crate-by-crate basis if desired. +external-default-features = "allow" +# List of crates that are allowed. Use with care! +allow = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# List of crates to deny +deny = [ + # Each entry the name of a crate and a version range. If version is + # not specified, all versions will be matched. + #{ name = "ansi_term", version = "=0.11.0" }, + # + # Wrapper crates can optionally be specified to allow the crate when it + # is a direct dependency of the otherwise banned crate + #{ name = "ansi_term", version = "=0.11.0", wrappers = [] }, +] + +# This section is considered when running `cargo deny check sources`. +# More documentation about the 'sources' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html +[sources] +# Lint level for what to happen when a crate from a crate registry that is not +# in the allow list is encountered +unknown-registry = "deny" +# Lint level for what to happen when a crate from a git repository that is not +# in the allow list is encountered +unknown-git = "deny" +# List of URLs for allowed crate registries. Defaults to the crates.io index +# if not specified. If it is specified but empty, no registries are allowed. +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +# List of URLs for allowed Git repositories +allow-git = [] + +[sources.allow-org] +# 1 or more github.com organizations to allow git sources for +github = [] diff --git a/examples/cargo-example-derive.rs b/examples/cargo-example-derive.rs index d9603db896b110bff973274ae3f14706c5021a4f..dfdd799fc19e9188b6c36164849fae19d0dabeb5 100644 --- a/examples/cargo-example-derive.rs +++ b/examples/cargo-example-derive.rs @@ -3,18 +3,18 @@ use clap::Parser; #[derive(Parser)] // requires `derive` feature #[command(name = "cargo")] #[command(bin_name = "cargo")] -enum Cargo { - ExampleDerive(ExampleDerive), +enum CargoCli { + ExampleDerive(ExampleDeriveArgs), } #[derive(clap::Args)] #[command(author, version, about, long_about = None)] -struct ExampleDerive { +struct ExampleDeriveArgs { #[arg(long)] manifest_path: Option, } fn main() { - let Cargo::ExampleDerive(args) = Cargo::parse(); + let CargoCli::ExampleDerive(args) = CargoCli::parse(); println!("{:?}", args.manifest_path); } diff --git a/examples/derive_ref/augment_args.rs b/examples/derive_ref/augment_args.rs index 458beb44c1e9f7105544de532f00e2618ca4a5c8..39d837cfaf56708271f494be7bad2f80589aa51a 100644 --- a/examples/derive_ref/augment_args.rs +++ b/examples/derive_ref/augment_args.rs @@ -23,5 +23,5 @@ fn main() { let derived_matches = DerivedArgs::from_arg_matches(&matches) .map_err(|err| err.exit()) .unwrap(); - println!("Value of derived: {:#?}", derived_matches); + println!("Value of derived: {derived_matches:#?}"); } diff --git a/examples/derive_ref/augment_subcommands.rs b/examples/derive_ref/augment_subcommands.rs index 577527500c225b5be839725b660ecdc7e4c9d6d4..51cbe75d2a286f615e8df17e667322df26a137b9 100644 --- a/examples/derive_ref/augment_subcommands.rs +++ b/examples/derive_ref/augment_subcommands.rs @@ -17,5 +17,5 @@ fn main() { let derived_subcommands = Subcommands::from_arg_matches(&matches) .map_err(|err| err.exit()) .unwrap(); - println!("Derived subcommands: {:#?}", derived_subcommands); + println!("Derived subcommands: {derived_subcommands:#?}"); } diff --git a/examples/derive_ref/flatten_hand_args.rs b/examples/derive_ref/flatten_hand_args.rs index 2ceeb7c0f578188105356b6179b10960e35730d6..36aac09323aef0a4499041e5f5c46c337e9234b7 100644 --- a/examples/derive_ref/flatten_hand_args.rs +++ b/examples/derive_ref/flatten_hand_args.rs @@ -87,5 +87,5 @@ struct Cli { fn main() { let args = Cli::parse(); - println!("{:#?}", args); + println!("{args:#?}"); } diff --git a/examples/derive_ref/hand_subcommand.rs b/examples/derive_ref/hand_subcommand.rs index 5ea169cd1e1360abeefa7d7e404101b7f2bde29a..ebaa60d5d2e7c0f53dbed2817287546f5951a834 100644 --- a/examples/derive_ref/hand_subcommand.rs +++ b/examples/derive_ref/hand_subcommand.rs @@ -69,11 +69,11 @@ impl Subcommand for CliSub { struct Cli { #[arg(short, long)] top_level: bool, - #[clap(subcommand)] + #[command(subcommand)] subcommand: CliSub, } fn main() { let args = Cli::parse(); - println!("{:#?}", args); + println!("{args:#?}"); } diff --git a/examples/git-derive.md b/examples/git-derive.md index b5c4794d30a4f1901d315639cfd539729c9a18c5..57edb612e9ecc16ba399170b76e9889f14dfe781 100644 --- a/examples/git-derive.md +++ b/examples/git-derive.md @@ -102,13 +102,13 @@ Options: -h, --help Print help $ git-derive stash -m "Prototype" -Pushing StashPush { message: Some("Prototype") } +Pushing StashPushArgs { message: Some("Prototype") } $ git-derive stash pop Popping None $ git-derive stash push -m "Prototype" -Pushing StashPush { message: Some("Prototype") } +Pushing StashPushArgs { message: Some("Prototype") } $ git-derive stash pop Popping None diff --git a/examples/git-derive.rs b/examples/git-derive.rs index 519982d1b9777d19c231f88375bf70d2e91a330a..ad82e0cea7804e04c751bfac7c5373f75eaafc23 100644 --- a/examples/git-derive.rs +++ b/examples/git-derive.rs @@ -53,7 +53,7 @@ enum Commands { #[arg(required = true)] path: Vec, }, - Stash(Stash), + Stash(StashArgs), #[command(external_subcommand)] External(Vec), } @@ -76,23 +76,23 @@ impl std::fmt::Display for ColorWhen { #[derive(Debug, Args)] #[command(args_conflicts_with_subcommands = true)] -struct Stash { +struct StashArgs { #[command(subcommand)] command: Option, #[command(flatten)] - push: StashPush, + push: StashPushArgs, } #[derive(Debug, Subcommand)] enum StashCommands { - Push(StashPush), + Push(StashPushArgs), Pop { stash: Option }, Apply { stash: Option }, } #[derive(Debug, Args)] -struct StashPush { +struct StashPushArgs { #[arg(short, long)] message: Option, } @@ -102,7 +102,7 @@ fn main() { match args.command { Commands::Clone { remote } => { - println!("Cloning {}", remote); + println!("Cloning {remote}"); } Commands::Diff { mut base, @@ -136,22 +136,22 @@ fn main() { ); } Commands::Push { remote } => { - println!("Pushing to {}", remote); + println!("Pushing to {remote}"); } Commands::Add { path } => { - println!("Adding {:?}", path); + println!("Adding {path:?}"); } Commands::Stash(stash) => { let stash_cmd = stash.command.unwrap_or(StashCommands::Push(stash.push)); match stash_cmd { StashCommands::Push(push) => { - println!("Pushing {:?}", push); + println!("Pushing {push:?}"); } StashCommands::Pop { stash } => { - println!("Popping {:?}", stash); + println!("Popping {stash:?}"); } StashCommands::Apply { stash } => { - println!("Applying {:?}", stash); + println!("Applying {stash:?}"); } } } diff --git a/examples/git.rs b/examples/git.rs index 8a2fcc72dfd7dd2ae67ffa7dea19438c9352327f..bd05e531582a836888ad1a2084e4bb7ec2a3cb5b 100644 --- a/examples/git.rs +++ b/examples/git.rs @@ -86,7 +86,7 @@ fn main() { let base = base.unwrap_or("stage"); let head = head.unwrap_or("worktree"); let path = path.unwrap_or(""); - println!("Diffing {}..{} {} (color={})", base, head, path, color); + println!("Diffing {base}..{head} {path} (color={color})"); } Some(("push", sub_matches)) => { println!( @@ -100,22 +100,22 @@ fn main() { .into_iter() .flatten() .collect::>(); - println!("Adding {:?}", paths); + println!("Adding {paths:?}"); } Some(("stash", sub_matches)) => { let stash_command = sub_matches.subcommand().unwrap_or(("push", sub_matches)); match stash_command { ("apply", sub_matches) => { let stash = sub_matches.get_one::("STASH"); - println!("Applying {:?}", stash); + println!("Applying {stash:?}"); } ("pop", sub_matches) => { let stash = sub_matches.get_one::("STASH"); - println!("Popping {:?}", stash); + println!("Popping {stash:?}"); } ("push", sub_matches) => { let message = sub_matches.get_one::("message"); - println!("Pushing {:?}", message); + println!("Pushing {message:?}"); } (name, _) => { unreachable!("Unsupported subcommand `{}`", name) @@ -128,9 +128,9 @@ fn main() { .into_iter() .flatten() .collect::>(); - println!("Calling out to {:?} with {:?}", ext, args); + println!("Calling out to {ext:?} with {args:?}"); } - _ => unreachable!(), // If all subcommands are defined above, anything else is unreachabe!() + _ => unreachable!(), // If all subcommands are defined above, anything else is unreachable!() } // Continued program logic goes here... diff --git a/examples/pacman.rs b/examples/pacman.rs index 12e7ffaf2751c44118e2e87d97c98ca558769bdc..7ab30db953fcbf1164f48936fef0d1bd1cbf0449 100644 --- a/examples/pacman.rs +++ b/examples/pacman.rs @@ -78,7 +78,7 @@ fn main() { .map(|s| s.as_str()) .collect(); let values = packages.join(", "); - println!("Searching for {}...", values); + println!("Searching for {values}..."); return; } @@ -90,18 +90,18 @@ fn main() { let values = packages.join(", "); if sync_matches.get_flag("info") { - println!("Retrieving info for {}...", values); + println!("Retrieving info for {values}..."); } else { - println!("Installing {}...", values); + println!("Installing {values}..."); } } Some(("query", query_matches)) => { if let Some(packages) = query_matches.get_many::("info") { let comma_sep = packages.map(|s| s.as_str()).collect::>().join(", "); - println!("Retrieving info for {}...", comma_sep); + println!("Retrieving info for {comma_sep}..."); } else if let Some(queries) = query_matches.get_many::("search") { let comma_sep = queries.map(|s| s.as_str()).collect::>().join(", "); - println!("Searching Locally for {}...", comma_sep); + println!("Searching Locally for {comma_sep}..."); } else { println!("Displaying all locally installed packages..."); } diff --git a/examples/repl.rs b/examples/repl.rs index ef26591158831e247c824191efb5c938d597607d..2806ac8e9d4352a77c6b657332bca221fdae7856 100644 --- a/examples/repl.rs +++ b/examples/repl.rs @@ -17,7 +17,7 @@ fn main() -> Result<(), String> { } } Err(err) => { - write!(std::io::stdout(), "{}", err).map_err(|e| e.to_string())?; + write!(std::io::stdout(), "{err}").map_err(|e| e.to_string())?; std::io::stdout().flush().map_err(|e| e.to_string())?; } } diff --git a/examples/tutorial_derive/01_quick.rs b/examples/tutorial_derive/01_quick.rs index 960e7ea18c93aa770d02891e251923c62d4e4731..0edcd251f971c8ccf94beaba4bf9a0c47907e08c 100644 --- a/examples/tutorial_derive/01_quick.rs +++ b/examples/tutorial_derive/01_quick.rs @@ -35,7 +35,7 @@ fn main() { // You can check the value provided by positional arguments, or option arguments if let Some(name) = cli.name.as_deref() { - println!("Value for name: {}", name); + println!("Value for name: {name}"); } if let Some(config_path) = cli.config.as_deref() { diff --git a/examples/tutorial_derive/03_04_subcommands.rs b/examples/tutorial_derive/03_04_subcommands.rs index 279a5fff54f9fc3e2a4127541c2e6b9ccf61843a..da7c644c70420fbc38b9965cdc2aa6fdbf62b63d 100644 --- a/examples/tutorial_derive/03_04_subcommands.rs +++ b/examples/tutorial_derive/03_04_subcommands.rs @@ -21,7 +21,7 @@ fn main() { // matches just as you would the top level cmd match &cli.command { Commands::Add { name } => { - println!("'myapp add' was used, name is: {:?}", name) + println!("'myapp add' was used, name is: {name:?}") } } } diff --git a/examples/tutorial_derive/03_04_subcommands_alt.rs b/examples/tutorial_derive/03_04_subcommands_alt.rs index 8b70dba0a9fc17224784bcdfda182724933e7e58..80b0ec6b9586a3b9309f7bdb7fafa83e78d0c7fd 100644 --- a/examples/tutorial_derive/03_04_subcommands_alt.rs +++ b/examples/tutorial_derive/03_04_subcommands_alt.rs @@ -11,11 +11,11 @@ struct Cli { #[derive(Subcommand)] enum Commands { /// Adds files to myapp - Add(Add), + Add(AddArgs), } #[derive(Args)] -struct Add { +struct AddArgs { name: Option, } diff --git a/examples/tutorial_derive/04_02_validate.rs b/examples/tutorial_derive/04_02_validate.rs index 513084dffb159b3c1feebde1ad7be3545112b222..3391d5b72d58b9bff42a666d357b8b29723d8a34 100644 --- a/examples/tutorial_derive/04_02_validate.rs +++ b/examples/tutorial_derive/04_02_validate.rs @@ -21,7 +21,7 @@ const PORT_RANGE: RangeInclusive = 1..=65535; fn port_in_range(s: &str) -> Result { let port: usize = s .parse() - .map_err(|_| format!("`{}` isn't a port number", s))?; + .map_err(|_| format!("`{s}` isn't a port number"))?; if PORT_RANGE.contains(&port) { Ok(port as u16) } else { diff --git a/examples/tutorial_derive/04_03_relations.rs b/examples/tutorial_derive/04_03_relations.rs index e366095702ee131e400d9f72f009f1c5f131ebc3..cbe491deb4bb59a8e8396a8d19c195d13201ba7c 100644 --- a/examples/tutorial_derive/04_03_relations.rs +++ b/examples/tutorial_derive/04_03_relations.rs @@ -56,10 +56,10 @@ fn main() { (_, _, true) => patch += 1, _ => unreachable!(), }; - format!("{}.{}.{}", major, minor, patch) + format!("{major}.{minor}.{patch}") }; - println!("Version: {}", version); + println!("Version: {version}"); // Check for usage of -c if let Some(config) = cli.config.as_deref() { @@ -67,6 +67,6 @@ fn main() { .input_file .as_deref() .unwrap_or_else(|| cli.spec_in.as_deref().unwrap()); - println!("Doing work using input {} and config {}", input, config); + println!("Doing work using input {input} and config {config}"); } } diff --git a/examples/tutorial_derive/04_04_custom.rs b/examples/tutorial_derive/04_04_custom.rs index cbc460f19cae9cc2d6577e41756ec49effdcadea..a84b5ed5fe9c9f7b584c0d9435a7ae0be96a939b 100644 --- a/examples/tutorial_derive/04_04_custom.rs +++ b/examples/tutorial_derive/04_04_custom.rs @@ -66,10 +66,10 @@ fn main() { .exit(); } }; - format!("{}.{}.{}", major, minor, patch) + format!("{major}.{minor}.{patch}") }; - println!("Version: {}", version); + println!("Version: {version}"); // Check for usage of -c if let Some(config) = cli.config.as_deref() { @@ -86,6 +86,6 @@ fn main() { ) .exit() }); - println!("Doing work using input {} and config {}", input, config); + println!("Doing work using input {input} and config {config}"); } } diff --git a/examples/typed-derive.rs b/examples/typed-derive.rs index bdf8a5809117d1d516746d40d3d9077b68bd1a22..8d7a84e96724a45163756d5205ca6de73b59406b 100644 --- a/examples/typed-derive.rs +++ b/examples/typed-derive.rs @@ -54,7 +54,7 @@ where { let pos = s .find('=') - .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{}`", s))?; + .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?; Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) } @@ -98,5 +98,5 @@ mod foreign_crate { fn main() { let args = Args::parse(); - println!("{:?}", args); + println!("{args:?}"); } diff --git a/src/_derive/_tutorial.rs b/src/_derive/_tutorial.rs index eb95c8754148a1d35fd58d16b11c033f97fd6066..f3f55c3392d9eb866076ca0d74291039fb42cb2b 100644 --- a/src/_derive/_tutorial.rs +++ b/src/_derive/_tutorial.rs @@ -75,7 +75,7 @@ #![doc = include_str!("../../examples/tutorial_derive/03_03_positional.md")] //! //! Note that the default [`ArgAction`][crate::ArgAction] is [`Set`][crate::ArgAction::Set]. To -//! accept multiple values, use [`Append`][crate::ArgAction::Append]: +//! accept multiple values, use [`Append`][crate::ArgAction::Append] via `Vec`: //! ```rust #![doc = include_str!("../../examples/tutorial_derive/03_03_positional_mult.rs")] //! ``` @@ -98,7 +98,7 @@ #![doc = include_str!("../../examples/tutorial_derive/03_02_option.md")] //! //! Note that the default [`ArgAction`][crate::ArgAction] is [`Set`][crate::ArgAction::Set]. To -//! accept multiple occurrences, use [`Append`][crate::ArgAction::Append]: +//! accept multiple occurrences, use [`Append`][crate::ArgAction::Append] via `Vec`: //! ```rust #![doc = include_str!("../../examples/tutorial_derive/03_02_option_mult.rs")] //! ``` diff --git a/src/builder/arg.rs b/src/builder/arg.rs index ca2d6acbc2b739ad10fa7aa3b38a14020b0d3e8d..bf898cbfd6427118fd8e54ad352a6e4e0149368b 100644 --- a/src/builder/arg.rs +++ b/src/builder/arg.rs @@ -2111,7 +2111,8 @@ impl Arg { /// /// Args with a lower value will be displayed first in the help message. This is helpful when /// one would like to emphasise frequently used args, or prioritize those towards the top of - /// the list. Args with duplicate display orders will be displayed in alphabetical order. + /// the list. Args with duplicate display orders will be displayed in the order they are + /// defined. /// /// **NOTE:** The default is 999 for all arguments. /// @@ -4122,7 +4123,7 @@ impl Arg { if self.val_names.len() > 1 { self.val_names .iter() - .map(|n| format!("<{}>", n)) + .map(|n| format!("<{n}>")) .collect::>() .join(delim) } else { @@ -4206,9 +4207,9 @@ impl Arg { debug_assert!(self.is_takes_value_set()); for (n, val_name) in val_names.iter().enumerate() { let arg_name = if self.is_positional() && (num_vals.min_values() == 0 || !required) { - format!("[{}]", val_name) + format!("[{val_name}]") } else { - format!("<{}>", val_name) + format!("<{val_name}>") }; if n != 0 { diff --git a/src/builder/arg_settings.rs b/src/builder/arg_settings.rs index 0f63e439611a168085cb56cc67c2e1a27194d13f..b8bc069c5dbd9c9d9220fce650b3ee7faa707824 100644 --- a/src/builder/arg_settings.rs +++ b/src/builder/arg_settings.rs @@ -113,7 +113,7 @@ mod test { assert!(m.is_required_set()); let m = m.unset_setting(ArgSettings::Required); - assert!(!m.is_required_set(), "{:#?}", m); + assert!(!m.is_required_set(), "{m:#?}"); } #[test] @@ -138,8 +138,8 @@ mod test { assert!(m.is_last_set()); let m = m.unset_setting(ArgSettings::Required | ArgSettings::Hidden | ArgSettings::Last); - assert!(!m.is_required_set(), "{:#?}", m); - assert!(!m.is_hide_set(), "{:#?}", m); - assert!(!m.is_last_set(), "{:#?}", m); + assert!(!m.is_required_set(), "{m:#?}"); + assert!(!m.is_hide_set(), "{m:#?}"); + assert!(!m.is_last_set(), "{m:#?}"); } } diff --git a/src/builder/command.rs b/src/builder/command.rs index fc2c983e4d994849e5b9927caaf9f254c2018275..635aa1bcd57df86db3a8bd53a2ab1386c386ab43 100644 --- a/src/builder/command.rs +++ b/src/builder/command.rs @@ -243,7 +243,7 @@ impl Command { let a = self .args .remove_by_name(id) - .unwrap_or_else(|| panic!("Argument `{}` is undefined", id)); + .unwrap_or_else(|| panic!("Argument `{id}` is undefined")); self.args.push(f(a)); self @@ -287,7 +287,7 @@ impl Command { let subcmd = if let Some(idx) = pos { self.subcommands.remove(idx) } else { - panic!("Command `{}` is undefined", name) + panic!("Command `{name}` is undefined") }; self.subcommands.push(f(subcmd)); @@ -822,7 +822,7 @@ impl Command { let mut styled = StyledStr::new(); let usage = Usage::new(self); write_help(&mut styled, self, &usage, false); - ok!(write!(w, "{}", styled)); + ok!(write!(w, "{styled}")); w.flush() } @@ -837,7 +837,7 @@ impl Command { let mut styled = StyledStr::new(); let usage = Usage::new(self); write_help(&mut styled, self, &usage, true); - ok!(write!(w, "{}", styled)); + ok!(write!(w, "{styled}")); w.flush() } @@ -1057,8 +1057,8 @@ impl Command { /// /// Using `0` will ignore terminal widths and use source formatting. /// - /// Defaults to current terminal width when `wrap_help` feature flag is enabled. If the flag - /// is disabled or it cannot be determined, the default is 100. + /// Defaults to current terminal width when `wrap_help` feature flag is enabled. If current + /// width cannot be determined, the default is 100. /// /// **NOTE:** This setting applies globally and *not* on a per-command basis. /// @@ -2599,7 +2599,7 @@ impl Command { /// Set the placement of this subcommand within the help. /// /// Subcommands with a lower value will be displayed first in the help message. Subcommands - /// with duplicate display orders will be displayed in alphabetical order. + /// with duplicate display orders will be displayed in order they are defined. /// /// This is helpful when one would like to emphasize frequently used subcommands, or prioritize /// those towards the top of the list. @@ -3947,22 +3947,22 @@ impl Command { sc_names.push_str(sc.name.as_str()); let mut flag_subcmd = false; if let Some(l) = sc.get_long_flag() { - write!(sc_names, "|--{}", l).unwrap(); + write!(sc_names, "|--{l}").unwrap(); flag_subcmd = true; } if let Some(s) = sc.get_short_flag() { - write!(sc_names, "|-{}", s).unwrap(); + write!(sc_names, "|-{s}").unwrap(); flag_subcmd = true; } if flag_subcmd { - sc_names = format!("{{{}}}", sc_names); + sc_names = format!("{{{sc_names}}}"); } let usage_name = self .bin_name .as_ref() - .map(|bin_name| format!("{}{}{}", bin_name, mid_string, sc_names)) + .map(|bin_name| format!("{bin_name}{mid_string}{sc_names}")) .unwrap_or(sc_names); sc.usage_name = Some(usage_name); @@ -4044,19 +4044,19 @@ impl Command { sc_names.push_str(sc.name.as_str()); let mut flag_subcmd = false; if let Some(l) = sc.get_long_flag() { - write!(sc_names, "|--{}", l).unwrap(); + write!(sc_names, "|--{l}").unwrap(); flag_subcmd = true; } if let Some(s) = sc.get_short_flag() { - write!(sc_names, "|-{}", s).unwrap(); + write!(sc_names, "|-{s}").unwrap(); flag_subcmd = true; } if flag_subcmd { - sc_names = format!("{{{}}}", sc_names); + sc_names = format!("{{{sc_names}}}"); } - let usage_name = format!("{}{}{}", self_bin_name, mid_string, sc_names); + let usage_name = format!("{self_bin_name}{mid_string}{sc_names}"); debug!( "Command::_build_bin_names:iter: Setting usage_name of {} to {:?}", sc.name, usage_name @@ -4334,7 +4334,7 @@ impl Command { .unwrap_or_default() }; let display_name = self.get_display_name().unwrap_or_else(|| self.get_name()); - format!("{} {}\n", display_name, ver) + format!("{display_name} {ver}\n") } pub(crate) fn format_group(&self, g: &Id) -> StyledStr { @@ -4593,12 +4593,14 @@ impl Command { // specified by the user is sent through. If hide_short_help is not included, // then items specified with hidden_short_help will also be hidden. let should_long = |v: &Arg| { - v.get_long_help().is_some() - || v.is_hide_long_help_set() - || v.is_hide_short_help_set() - || v.get_possible_values() - .iter() - .any(PossibleValue::should_show_help) + !v.is_hide_set() + && (v.get_long_help().is_some() + || v.is_hide_long_help_set() + || v.is_hide_short_help_set() + || (!v.is_hide_possible_values_set() + && v.get_possible_values() + .iter() + .any(PossibleValue::should_show_help))) }; // Subcommands aren't checked because we prefer short help for them, deferring to diff --git a/src/builder/debug_asserts.rs b/src/builder/debug_asserts.rs index bfebbb4c43f38d096a2f84a74ca2190bb31e6a87..86e36778cff6e145a667ca203236a67a1624f778 100644 --- a/src/builder/debug_asserts.rs +++ b/src/builder/debug_asserts.rs @@ -40,20 +40,20 @@ pub(crate) fn assert_app(cmd: &Command) { for sc in cmd.get_subcommands() { if let Some(s) = sc.get_short_flag().as_ref() { - short_flags.push(Flag::Command(format!("-{}", s), sc.get_name())); + short_flags.push(Flag::Command(format!("-{s}"), sc.get_name())); } for short_alias in sc.get_all_short_flag_aliases() { - short_flags.push(Flag::Command(format!("-{}", short_alias), sc.get_name())); + short_flags.push(Flag::Command(format!("-{short_alias}"), sc.get_name())); } if let Some(l) = sc.get_long_flag().as_ref() { assert!(!l.starts_with('-'), "Command {}: long_flag {:?} must not start with a `-`, that will be handled by the parser", sc.get_name(), l); - long_flags.push(Flag::Command(format!("--{}", l), sc.get_name())); + long_flags.push(Flag::Command(format!("--{l}"), sc.get_name())); } for long_alias in sc.get_all_long_flag_aliases() { - long_flags.push(Flag::Command(format!("--{}", long_alias), sc.get_name())); + long_flags.push(Flag::Command(format!("--{long_alias}"), sc.get_name())); } } @@ -68,26 +68,20 @@ pub(crate) fn assert_app(cmd: &Command) { ); if let Some(s) = arg.get_short() { - short_flags.push(Flag::Arg(format!("-{}", s), arg.get_id().as_str())); + short_flags.push(Flag::Arg(format!("-{s}"), arg.get_id().as_str())); } for (short_alias, _) in &arg.short_aliases { - short_flags.push(Flag::Arg( - format!("-{}", short_alias), - arg.get_id().as_str(), - )); + short_flags.push(Flag::Arg(format!("-{short_alias}"), arg.get_id().as_str())); } if let Some(l) = arg.get_long() { assert!(!l.starts_with('-'), "Argument {}: long {:?} must not start with a `-`, that will be handled by the parser", arg.get_id(), l); - long_flags.push(Flag::Arg(format!("--{}", l), arg.get_id().as_str())); + long_flags.push(Flag::Arg(format!("--{l}"), arg.get_id().as_str())); } for (long_alias, _) in &arg.aliases { - long_flags.push(Flag::Arg( - format!("--{}", long_alias), - arg.get_id().as_str(), - )); + long_flags.push(Flag::Arg(format!("--{long_alias}"), arg.get_id().as_str())); } // Name conflicts @@ -429,19 +423,16 @@ fn detect_duplicate_flags(flags: &[Flag], short_or_long: &str) { for (one, two) in find_duplicates(flags) { match (one, two) { (Command(flag, one), Command(_, another)) if one != another => panic!( - "the '{}' {} flag is specified for both '{}' and '{}' subcommands", - flag, short_or_long, one, another + "the '{flag}' {short_or_long} flag is specified for both '{one}' and '{another}' subcommands" ), (Arg(flag, one), Arg(_, another)) if one != another => panic!( - "{} option names must be unique, but '{}' is in use by both '{}' and '{}'", - short_or_long, flag, one, another + "{short_or_long} option names must be unique, but '{flag}' is in use by both '{one}' and '{another}'" ), (Arg(flag, arg), Command(_, sub)) | (Command(flag, sub), Arg(_, arg)) => panic!( - "the '{}' {} flag for the '{}' argument conflicts with the short flag \ - for '{}' subcommand", - flag, short_or_long, arg, sub + "the '{flag}' {short_or_long} flag for the '{arg}' argument conflicts with the short flag \ + for '{sub}' subcommand" ), _ => {} @@ -529,10 +520,8 @@ fn _verify_positionals(cmd: &Command) -> bool { assert!( highest_idx == num_p, - "Found positional argument whose index is {} but there \ - are only {} positional arguments defined", - highest_idx, - num_p + "Found positional argument whose index is {highest_idx} but there \ + are only {num_p} positional arguments defined", ); for arg in cmd.get_arguments() { diff --git a/src/builder/range.rs b/src/builder/range.rs index 3f077bd86e64c50fd1167346a2c5b6b34f131717..6ecf05369a5fcbfceb86d1947e8467a10105668e 100644 --- a/src/builder/range.rs +++ b/src/builder/range.rs @@ -183,7 +183,7 @@ impl std::fmt::Display for ValueRange { impl std::fmt::Debug for ValueRange { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self) + write!(f, "{self}") } } diff --git a/src/builder/str.rs b/src/builder/str.rs index c6689d31065cf819a4871e6b9909e65dc5506631..be69c3f6ee632af3e63b62259da08b937683ab5b 100644 --- a/src/builder/str.rs +++ b/src/builder/str.rs @@ -246,7 +246,10 @@ pub(crate) mod inner { } pub(crate) fn into_string(self) -> String { - self.as_str().to_owned() + match self { + Self::Static(s) => s.to_owned(), + Self::Owned(s) => s.into(), + } } } } diff --git a/src/builder/value_hint.rs b/src/builder/value_hint.rs index 812d5d8668b093ed9e33424ddcc3bc419c7cee1c..ee286b194c85bc5d154644b5fceb46673ffb00c5 100644 --- a/src/builder/value_hint.rs +++ b/src/builder/value_hint.rs @@ -89,7 +89,7 @@ impl FromStr for ValueHint { "hostname" => ValueHint::Hostname, "url" => ValueHint::Url, "emailaddress" => ValueHint::EmailAddress, - _ => return Err(format!("unknown ValueHint: `{}`", s)), + _ => return Err(format!("unknown ValueHint: `{s}`")), }) } } diff --git a/src/derive.rs b/src/derive.rs index a30f2da50ac66e36a3141fb6fe8049abf9469bf8..98bb0121b6eb6c2bebc12a9efd5068c19ef064a6 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -385,7 +385,7 @@ pub trait ValueEnum: Sized + Clone { .matches(input, ignore_case) }) .cloned() - .ok_or_else(|| format!("invalid variant: {}", input)) + .ok_or_else(|| format!("invalid variant: {input}")) } /// The canonical argument value. diff --git a/src/error/format.rs b/src/error/format.rs index 98d34b7ef4e75d96f1208cf863b2b43f58d04f63..d3ce6c64c19edcedc28422c64307928c32c778bf 100644 --- a/src/error/format.rs +++ b/src/error/format.rs @@ -461,7 +461,7 @@ fn did_you_mean(styled: &mut StyledStr, context: &str, valid: &ContextValue) { fn escape(s: impl AsRef) -> String { let s = s.as_ref(); if s.contains(char::is_whitespace) { - format!("{:?}", s) + format!("{s:?}") } else { s.to_owned() } diff --git a/src/error/mod.rs b/src/error/mod.rs index 9c8828574d469ad43f33a46d3862c012daeadf98..2d8bf62e47c752427482339e0945facc6834496d 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -692,7 +692,7 @@ impl Error { Some((flag, None)) => { err = err.insert_context_unchecked( ContextKind::SuggestedArg, - ContextValue::String(format!("--{}", flag)), + ContextValue::String(format!("--{flag}")), ); } None => {} @@ -782,7 +782,7 @@ impl Display for Error { if let Some(backtrace) = self.inner.backtrace.as_ref() { ok!(writeln!(f)); ok!(writeln!(f, "Backtrace:")); - ok!(writeln!(f, "{}", backtrace)); + ok!(writeln!(f, "{backtrace}")); } Ok(()) } diff --git a/src/mkeymap.rs b/src/mkeymap.rs index a54fae455c0d8b1ee4301d22b984ad14edbdc4df..e9d6ab8442e07e6196ad7bd863384a50a71e932e 100644 --- a/src/mkeymap.rs +++ b/src/mkeymap.rs @@ -126,6 +126,8 @@ impl MKeyMap { /// We need a lazy build here since some we may change args after creating /// the map, you can checkout who uses `args_mut`. pub(crate) fn _build(&mut self) { + // There will be at least as many keys as args, so that is a good starting point + self.keys.reserve(self.args.len()); for (i, arg) in self.args.iter().enumerate() { append_keys(&mut self.keys, arg, i); } diff --git a/src/output/help_template.rs b/src/output/help_template.rs index 6fe9db1b4a7e8746a202a17d1eb364e5128cca46..e84cd727c79209f3bff0f918bd6f0f628a752e26 100644 --- a/src/output/help_template.rs +++ b/src/output/help_template.rs @@ -368,7 +368,8 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { .get_subcommand_help_heading() .unwrap_or(&default_help_heading), ); - self.header(":\n"); + self.header(":"); + self.none("\n"); self.write_subcommands(self.cmd); } @@ -379,7 +380,8 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { } first = false; // Write positional args if any - self.header("Arguments:\n"); + self.header("Arguments:"); + self.none("\n"); self.write_args(&pos, "Arguments", positional_sort_key); } @@ -388,7 +390,8 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { self.none("\n\n"); } first = false; - self.header("Options:\n"); + self.header("Options:"); + self.none("\n"); self.write_args(&non_pos, "Options", option_sort_key); } if !custom_headings.is_empty() { @@ -410,7 +413,9 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { self.none("\n\n"); } first = false; - self.header(format!("{}:\n", heading)); + self.header(heading); + self.header(":"); + self.none("\n"); self.write_args(&args, heading, option_sort_key); } } @@ -485,7 +490,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { debug!("HelpTemplate::short"); if let Some(s) = arg.get_short() { - self.literal(format!("-{}", s)); + self.literal(format!("-{s}")); } else if arg.get_long().is_some() { self.none(" "); } @@ -498,7 +503,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { if arg.get_short().is_some() { self.none(", "); } - self.literal(format!("--{}", long)); + self.literal(format!("--{long}")); } } @@ -738,7 +743,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { .map(|pvs| pvs.to_string_lossy()) .map(|pvs| { if pvs.contains(char::is_whitespace) { - Cow::from(format!("{:?}", pvs)) + Cow::from(format!("{pvs:?}")) } else { pvs } @@ -746,7 +751,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { .collect::>() .join(" "); - spec_vals.push(format!("[default: {}]", pvs)); + spec_vals.push(format!("[default: {pvs}]")); } let als = a @@ -758,7 +763,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { .join(", "); if !als.is_empty() { debug!("HelpTemplate::spec_vals: Found aliases...{:?}", a.aliases); - spec_vals.push(format!("[aliases: {}]", als)); + spec_vals.push(format!("[aliases: {als}]")); } let als = a @@ -773,7 +778,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { "HelpTemplate::spec_vals: Found short aliases...{:?}", a.short_aliases ); - spec_vals.push(format!("[short aliases: {}]", als)); + spec_vals.push(format!("[short aliases: {als}]")); } let possible_vals = a.get_possible_values(); @@ -792,7 +797,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { .collect::>() .join(", "); - spec_vals.push(format!("[possible values: {}]", pvs)); + spec_vals.push(format!("[possible values: {pvs}]")); } let connector = if self.use_long { "\n" } else { " " }; spec_vals.join(connector) @@ -835,11 +840,11 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { styled.literal(subcommand.get_name()); if let Some(short) = subcommand.get_short_flag() { styled.none(", "); - styled.literal(format!("-{}", short)); + styled.literal(format!("-{short}")); } if let Some(long) = subcommand.get_long_flag() { styled.none(", "); - styled.literal(format!("--{}", long)); + styled.literal(format!("--{long}")); } longest = longest.max(styled.display_width()); ord_v.push((subcommand.get_display_order(), styled, subcommand)); @@ -902,7 +907,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { let mut short_als = a .get_visible_short_flag_aliases() - .map(|a| format!("-{}", a)) + .map(|a| format!("-{a}")) .collect::>(); let als = a.get_visible_aliases().map(|s| s.to_string()); short_als.extend(als); @@ -916,7 +921,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { "HelpTemplate::spec_vals: Found short flag aliases...{:?}", a.get_all_short_flag_aliases().collect::>() ); - spec_vals.push(format!("[aliases: {}]", all_als)); + spec_vals.push(format!("[aliases: {all_als}]")); } spec_vals.join(" ") diff --git a/src/output/textwrap/core.rs b/src/output/textwrap/core.rs index b890c6c7bca1c8bffa6f040106a7d4425132d6b8..25c9eb6b0e9362ad44c90a827b3d0036fb2d84ea 100644 --- a/src/output/textwrap/core.rs +++ b/src/output/textwrap/core.rs @@ -105,7 +105,7 @@ mod tests { assert_eq!(ch.width().unwrap(), 1, "char: {}", desc); #[cfg(not(feature = "unicode"))] - assert_eq!(ch_width(ch), 1, "char: {}", desc); + assert_eq!(ch_width(ch), 1, "char: {desc}"); } } @@ -123,7 +123,7 @@ mod tests { assert!(ch.width().unwrap() <= 2, "char: {}", desc); #[cfg(not(feature = "unicode"))] - assert_eq!(ch_width(ch), 1, "char: {}", desc); + assert_eq!(ch_width(ch), 1, "char: {desc}"); } } diff --git a/src/parser/arg_matcher.rs b/src/parser/arg_matcher.rs index c7e94e6782687af19959d7118c7d934f0c14f67f..d584689cdbc204d201365c6a3700351976a4fc4c 100644 --- a/src/parser/arg_matcher.rs +++ b/src/parser/arg_matcher.rs @@ -217,9 +217,9 @@ impl ArgMatcher { raw_vals: Default::default(), trailing_idx: None, }); - debug_assert_eq!(pending.id, *id, "{}", INTERNAL_ERROR_MSG); + debug_assert_eq!(pending.id, *id, "{INTERNAL_ERROR_MSG}"); if ident.is_some() { - debug_assert_eq!(pending.ident, ident, "{}", INTERNAL_ERROR_MSG); + debug_assert_eq!(pending.ident, ident, "{INTERNAL_ERROR_MSG}"); } if trailing_values { pending.trailing_idx.get_or_insert(pending.raw_vals.len()); diff --git a/src/parser/error.rs b/src/parser/error.rs index 5652336139f40e97f1ebbdaf932d69070225b12c..66b2bc79e85f324ee954ecd3705af26dd41efd61 100644 --- a/src/parser/error.rs +++ b/src/parser/error.rs @@ -27,10 +27,7 @@ impl MatchesError { } Err(err) => err, }; - panic!( - "Mismatch between definition and access of `{}`. {}", - id, err - ) + panic!("Mismatch between definition and access of `{id}`. {err}",) } } @@ -42,8 +39,7 @@ impl std::fmt::Display for MatchesError { Self::Downcast { actual, expected } => { writeln!( f, - "Could not downcast to {:?}, need to downcast to {:?}", - expected, actual + "Could not downcast to {expected:?}, need to downcast to {actual:?}" ) } Self::UnknownArgument {} => { diff --git a/src/parser/matches/arg_matches.rs b/src/parser/matches/arg_matches.rs index 23f53c02b9f2d20cbf1d7eb1faa7195358bc9a84..67d2918d0bd26b6413f615995a6b5b003b712cdc 100644 --- a/src/parser/matches/arg_matches.rs +++ b/src/parser/matches/arg_matches.rs @@ -139,9 +139,12 @@ impl ArgMatches { /// ``` #[cfg_attr(debug_assertions, track_caller)] pub fn get_count(&self, id: &str) -> u8 { - *self - .get_one::(id) - .expect("ArgAction::Count is defaulted") + *self.get_one::(id).unwrap_or_else(|| { + panic!( + "arg `{}`'s `ArgAction` should be `Count` which should provide a default", + id + ) + }) } /// Gets the value of a specific [`ArgAction::SetTrue`][crate::ArgAction::SetTrue] or [`ArgAction::SetFalse`][crate::ArgAction::SetFalse] flag @@ -173,7 +176,12 @@ impl ArgMatches { pub fn get_flag(&self, id: &str) -> bool { *self .get_one::(id) - .expect("ArgAction::SetTrue / ArgAction::SetFalse is defaulted") + .unwrap_or_else(|| { + panic!( + "arg `{}`'s `ArgAction` should be one of `SetTrue`, `SetFalse` which should provide a default", + id + ) + }) } /// Iterate over values of a specific option or positional argument. @@ -1302,10 +1310,9 @@ impl ArgMatches { if arg == Id::EXTERNAL || self.valid_args.iter().any(|s| *s == arg) { } else { panic!( - "`{:?}` is not an id of an argument or a group.\n\ + "`{arg:?}` is not an id of an argument or a group.\n\ Make sure you're using the name of the argument itself \ - and not the name of short or long flags.", - arg + and not the name of short or long flags." ); } } @@ -1320,7 +1327,7 @@ impl ArgMatches { { if name.is_empty() || self.valid_subcommands.iter().any(|s| *s == name) { } else { - panic!("`{}` is not a name of a subcommand.", name); + panic!("`{name}` is not a name of a subcommand."); } } diff --git a/src/parser/parser.rs b/src/parser/parser.rs index dc99e32ece6ea004f3f95ce31344f0eb9f9c28c8..2579034318147c11328730045bd32d2fd4c05829 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -895,8 +895,7 @@ impl<'cmd> Parser<'cmd> { debug_assert_eq!( res, Ok(()), - "tracking of `flag_subcmd_skip` is off for `{:?}`", - short_arg + "tracking of `flag_subcmd_skip` is off for `{short_arg:?}`" ); while let Some(c) = short_arg.next_flag() { let c = match c { @@ -982,7 +981,7 @@ impl<'cmd> Parser<'cmd> { Ok(ParseResult::FlagSubCommand(name)) } else { Ok(ParseResult::NoMatchingArg { - arg: format!("-{}", c), + arg: format!("-{c}"), }) }; } @@ -1570,7 +1569,7 @@ impl<'cmd> Parser<'cmd> { did_you_mean.is_none() && !trailing_values && self.cmd.has_positionals(); ClapError::unknown_argument( self.cmd, - format!("--{}", arg), + format!("--{arg}"), did_you_mean, suggested_trailing_arg, Usage::new(self.cmd) diff --git a/src/parser/validator.rs b/src/parser/validator.rs index 991dc513f691c3d39644eee1164f771419d3ea8d..49d28a34f8cfaae69be1470f18a4d5e19fa82b98 100644 --- a/src/parser/validator.rs +++ b/src/parser/validator.rs @@ -519,13 +519,10 @@ fn gather_direct_conflicts(cmd: &Command, id: &Id) -> Vec { } else if let Some(group) = cmd.find_group(id) { gather_group_direct_conflicts(group) } else { - debug_assert!(false, "id={:?} is unknown", id); + debug_assert!(false, "id={id:?} is unknown"); Vec::new() }; - debug!( - "Conflicts::gather_direct_conflicts id={:?}, conflicts={:?}", - id, conf - ); + debug!("Conflicts::gather_direct_conflicts id={id:?}, conflicts={conf:?}",); conf } diff --git a/src/util/color.rs b/src/util/color.rs index 4d59b2eb288dfed69815a8d4347d93022a9b6f11..5a0a8ce6d69c394a71ceb4d958626a279d893e71 100644 --- a/src/util/color.rs +++ b/src/util/color.rs @@ -82,7 +82,7 @@ impl std::str::FromStr for ColorChoice { return Ok(*variant); } } - Err(format!("invalid variant: {}", s)) + Err(format!("invalid variant: {s}")) } } diff --git a/tests/builder/help.rs b/tests/builder/help.rs index 984a86c245ffdeee5206260bd57710338f9800de..28aed85556fe9f4abb0452c88321ae448524dd0f 100644 --- a/tests/builder/help.rs +++ b/tests/builder/help.rs @@ -676,6 +676,29 @@ Options: utils::assert_output(app, "ctest --help", POS_VALS_HELP, false); } +#[test] +fn hidden_possible_vals() { + static POS_VALS_HELP: &str = "\ +Usage: ctest [pos] + +Arguments: + [pos] + +Options: + -h, --help Print help +"; + let app = Command::new("ctest").arg( + Arg::new("pos") + .hide_possible_values(true) + .value_parser([ + PossibleValue::new("fast"), + PossibleValue::new("slow").help("not as fast"), + ]) + .action(ArgAction::Set), + ); + utils::assert_output(app, "ctest --help", POS_VALS_HELP, false); +} + #[test] #[cfg(feature = "wrap_help")] fn issue_626_panic() { diff --git a/tests/builder/hidden_args.rs b/tests/builder/hidden_args.rs index 4cdfb24534988e639187903c0febfd4ac3a5792b..537c8fc1b5aa7041b068baf487de5d17bf34df9d 100644 --- a/tests/builder/hidden_args.rs +++ b/tests/builder/hidden_args.rs @@ -1,6 +1,6 @@ use super::utils; -use clap::{arg, Arg, ArgAction, Command}; +use clap::{arg, builder::PossibleValue, Arg, ArgAction, Command}; static HIDDEN_ARGS: &str = "\ tests stuff @@ -278,3 +278,27 @@ fn hide_subcmds_only() { utils::assert_output(cmd, "test --help", HIDDEN_SUBCMDS_ONLY, false); } + +#[test] +fn hidden_arg_with_possible_value_with_help() { + // Normally the presence of a possible value with a help text triggers a + // change of the --help help text by appending `(see more with '--help')` + // or `(see a summary with '-h')`. When the argument is completely hidden + // we however do not want it to trigger that change. + static POS_VALS_HELP: &str = "\ +Usage: ctest + +Options: + -h, --help Print help +"; + let app = Command::new("ctest").arg( + Arg::new("pos") + .hide(true) + .value_parser([ + PossibleValue::new("fast"), + PossibleValue::new("slow").help("not as fast"), + ]) + .action(ArgAction::Set), + ); + utils::assert_output(app, "ctest --help", POS_VALS_HELP, false); +} diff --git a/tests/builder/ignore_errors.rs b/tests/builder/ignore_errors.rs index c98cfc2e1420c6552d2d985b074f6a8f2b04f604..dfb3575364023af6460a66333faaa5f17bfbef6d 100644 --- a/tests/builder/ignore_errors.rs +++ b/tests/builder/ignore_errors.rs @@ -21,7 +21,7 @@ fn single_long_arg_without_value() { let r = cmd.try_get_matches_from(vec!["cmd", "--config" /* missing: , "config file" */]); - assert!(r.is_ok(), "unexpected error: {:?}", r); + assert!(r.is_ok(), "unexpected error: {r:?}"); let m = r.unwrap(); assert!(m.contains_id("config")); } diff --git a/tests/builder/positionals.rs b/tests/builder/positionals.rs index b931b119abe23fd6a9b31f7f4b1d563983ee8c85..f15b2edc316b3fe1fb5bbfdf5a88ddcfa47e2c8c 100644 --- a/tests/builder/positionals.rs +++ b/tests/builder/positionals.rs @@ -179,7 +179,7 @@ fn positional_possible_values() { Arg::new("positional").index(1).value_parser(["test123"]), ]) .try_get_matches_from(vec!["", "-f", "test123"]); - assert!(r.is_ok(), "{:#?}", r); + assert!(r.is_ok(), "{r:#?}"); let m = r.unwrap(); assert!(m.contains_id("positional")); assert!(*m.get_one::("flag").expect("defaulted by clap")); diff --git a/tests/builder/tests.rs b/tests/builder/tests.rs index 072a8773cc300306872349f593eb3753cac0af48..a7148574fe4e250ae4f2c2f272b2464323486148 100644 --- a/tests/builder/tests.rs +++ b/tests/builder/tests.rs @@ -195,7 +195,7 @@ pub fn check_complex_output(args: &str, out: &str) { } if let Some(p) = matches.get_one::("positional").map(|v| v.as_str()) { - writeln!(w, "positional present with value: {}", p).unwrap(); + writeln!(w, "positional present with value: {p}").unwrap(); } else { writeln!(w, "positional NOT present").unwrap(); } @@ -207,17 +207,17 @@ pub fn check_complex_output(args: &str, out: &str) { writeln!(w, "flag NOT present").unwrap(); } n => { - writeln!(w, "flag present {} times", n).unwrap(); + writeln!(w, "flag present {n} times").unwrap(); } } if matches.contains_id("option") { if let Some(v) = matches.get_one::("option").map(|v| v.as_str()) { - writeln!(w, "scoption present with value: {}", v).unwrap(); + writeln!(w, "scoption present with value: {v}").unwrap(); } if let Some(ov) = matches.get_many::("option") { for o in ov { - writeln!(w, "An scoption: {}", o).unwrap(); + writeln!(w, "An scoption: {o}").unwrap(); } } } else { @@ -228,7 +228,7 @@ pub fn check_complex_output(args: &str, out: &str) { .get_one::("scpositional") .map(|v| v.as_str()) { - writeln!(w, "scpositional present with value: {}", p).unwrap(); + writeln!(w, "scpositional present with value: {p}").unwrap(); } } } else { diff --git a/tests/builder/utils.rs b/tests/builder/utils.rs index f76efbec26c0f74825106151a5610ed77d66b923..e0b434e2956055d81ed5ef78f336b7e88b446690 100644 --- a/tests/builder/utils.rs +++ b/tests/builder/utils.rs @@ -28,7 +28,7 @@ pub fn assert_output(l: Command, args: &str, expected: &str, stderr: bool) { let mut buf = Cursor::new(Vec::with_capacity(50)); let res = l.try_get_matches_from(args.split(' ').collect::>()); let err = res.unwrap_err(); - write!(&mut buf, "{}", err).unwrap(); + write!(&mut buf, "{err}").unwrap(); let actual = buf.into_inner(); let actual = String::from_utf8(actual).unwrap(); assert_eq!( diff --git a/tests/derive/groups.rs b/tests/derive/groups.rs index 88f674a5e8099e9e1155f47f40c1ad42badd6448..1c9bcb50762381b02dc164afe9422b2ec88f14a2 100644 --- a/tests/derive/groups.rs +++ b/tests/derive/groups.rs @@ -73,9 +73,9 @@ fn skip_group_avoids_duplicate_ids() { #[derive(clap::Args, Debug)] #[group(skip)] pub struct Compose { - #[clap(flatten)] + #[command(flatten)] pub left: L, - #[clap(flatten)] + #[command(flatten)] pub right: R, } @@ -108,9 +108,9 @@ fn helpful_panic_on_duplicate_groups() { #[derive(clap::Args, Debug)] pub struct Compose { - #[clap(flatten)] + #[command(flatten)] pub left: L, - #[clap(flatten)] + #[command(flatten)] pub right: R, } diff --git a/tests/derive/utils.rs b/tests/derive/utils.rs index a6a0e36d91759e478deba199862ab762a666de94..d08ef999283292017613c499954b352a146a71a4 100644 --- a/tests/derive/utils.rs +++ b/tests/derive/utils.rs @@ -17,8 +17,8 @@ pub const FULL_TEMPLATE: &str = "\ pub fn get_help() -> String { let output = ::command().render_help().to_string(); - eprintln!("\n%%% HELP %%%:=====\n{}\n=====\n", output); - eprintln!("\n%%% HELP (DEBUG) %%%:=====\n{:?}\n=====\n", output); + eprintln!("\n%%% HELP %%%:=====\n{output}\n=====\n"); + eprintln!("\n%%% HELP (DEBUG) %%%:=====\n{output:?}\n=====\n"); output } @@ -28,8 +28,8 @@ pub fn get_long_help() -> String { .render_long_help() .to_string(); - eprintln!("\n%%% LONG_HELP %%%:=====\n{}\n=====\n", output); - eprintln!("\n%%% LONG_HELP (DEBUG) %%%:=====\n{:?}\n=====\n", output); + eprintln!("\n%%% LONG_HELP %%%:=====\n{output}\n=====\n"); + eprintln!("\n%%% LONG_HELP (DEBUG) %%%:=====\n{output:?}\n=====\n"); output } @@ -42,14 +42,8 @@ pub fn get_subcommand_long_help(subcmd: &str) -> String { .render_long_help() .to_string(); - eprintln!( - "\n%%% SUBCOMMAND `{}` HELP %%%:=====\n{}\n=====\n", - subcmd, output - ); - eprintln!( - "\n%%% SUBCOMMAND `{}` HELP (DEBUG) %%%:=====\n{:?}\n=====\n", - subcmd, output - ); + eprintln!("\n%%% SUBCOMMAND `{subcmd}` HELP %%%:=====\n{output}\n=====\n",); + eprintln!("\n%%% SUBCOMMAND `{subcmd}` HELP (DEBUG) %%%:=====\n{output:?}\n=====\n",); output } diff --git a/tests/derive_ui/clap_empty_attr.stderr b/tests/derive_ui/clap_empty_attr.stderr index 414ffdc0f49d8528fd68b8bd3dda1150393bca61..596fbe09db7b6e2328b03486d8ea65f1a6dcfd1a 100644 --- a/tests/derive_ui/clap_empty_attr.stderr +++ b/tests/derive_ui/clap_empty_attr.stderr @@ -1,8 +1,8 @@ error: expected attribute arguments in parentheses: #[command(...)] - --> tests/derive_ui/clap_empty_attr.rs:4:1 + --> tests/derive_ui/clap_empty_attr.rs:4:3 | 4 | #[command] - | ^^^^^^^^^^ + | ^^^^^^^ error: expected parentheses: #[arg(...)] --> tests/derive_ui/clap_empty_attr.rs:9:11 diff --git a/tests/derive_ui/default_values_t_invalid.stderr b/tests/derive_ui/default_values_t_invalid.stderr index c8923f3f621f3d0d1ad2f6031064a236f4ab718d..78a336960fd7b4f2bd606ccdb9dd9814dc202274 100644 --- a/tests/derive_ui/default_values_t_invalid.stderr +++ b/tests/derive_ui/default_values_t_invalid.stderr @@ -1,6 +1,6 @@ error: #[arg(default_values_t)] can be used only on Vec types - = note: see https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes + = note: see https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes --> tests/derive_ui/default_values_t_invalid.rs:6:11 |