Merge epage/clapng

This was a short-lived fork that touched on nearly everything,
including:
- Reference documentation updated
- Examples cleaned up
- Changelog updated
- YAML and runtime usage parser reverted back to nearly 2.x behavior and marked as deprecated
- More smoothing out migration for users
- Cleaned up CI
This commit is contained in:
Ed Page 2021-12-07 08:42:16 -06:00
commit 7a750a3066
385 changed files with 14935 additions and 15912 deletions

View file

@ -1,45 +0,0 @@
<!--
Please use the following template to assist with creating an issue and to ensure a speedy resolution. If an area is not applicable, feel free to delete the area or mark with `N/A`
-->
### Rust Version
* Use the output of `rustc -V`
### Affected Version of clap
* Can be found in Cargo.lock of your project (i.e. `grep clap Cargo.lock`)
### Expected Behavior Summary
### Actual Behavior Summary
### Steps to Reproduce the issue
### Sample Code or Link to Sample Code
### Debug output
Compile clap with `debug` feature:
```toml
[dependencies]
clap = { version = "*", features = ["debug"] }
```
The output may be very long, so feel free to link to a gist or attach a text file
<details>
<summary> Debug Output </summary>
<pre>
<code>
Paste Debug Output Here
</code>
</pre>
</details>

View file

@ -1,27 +1,24 @@
name: Bug report
description: An issue with clap, clap_derive or clap_generate
labels: 'T: bug'
description: Things not quite working right
labels:
- 'T: bug'
# - 'C-bug'
# - 'S-triage'
body:
- type: checkboxes
attributes:
label: Please complete the following tasks
options:
- label: I have searched the [discussions](https://github.com/clap-rs/clap/discussions)
required: true
- label: I have searched the existing issues
required: true
- type: input
- type: textarea
attributes:
label: Rust Version
description: Output of `rustc -V`
label: Description
validations:
required: true
- type: input
attributes:
label: Clap Version
label: Version
description: Can be found in Cargo.lock or Cargo.toml of your project (i.e. `grep clap Cargo.lock`). PLEASE DO NOT PUT "latest" HERE, use precise version. Put `master` (or other branch) if you're using the repo directly.
validations:
required: true
- type: textarea
attributes:
label: Minimal reproducible code
@ -32,28 +29,14 @@ body:
```
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce the bug with the above code
description: A command like `cargo run -- options...` or multiple commands.
validations:
required: true
- type: textarea
attributes:
label: Actual Behaviour
description: When I do like *this*, *that* is happening and I think it shouldn't.
validations:
required: true
- type: textarea
attributes:
label: Expected Behaviour
description: I think *this* should happen instead.
validations:
required: true
- type: textarea
attributes:
label: Additional Context
description: Add any other context about the problem here.
- type: textarea
attributes:
label: Debug Output

View file

@ -1,6 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Ask a question
about: You've come to seek help or want to discuss something related to clap
about: For support or brainstorming
url: https://github.com/clap-rs/clap/discussions/new
# TODO: Question template (waiting for discussions template feature)

View file

@ -1,34 +0,0 @@
name: Documentation issue
description: An issue with our docs, like typo, something's missing, incorrect docs, etc...
labels: 'C: docs'
body:
- type: checkboxes
attributes:
label: Please complete the following tasks
options:
- label: I have searched the [discussions](https://github.com/clap-rs/clap/discussions)
required: true
- label: I have searched the existing issues
required: true
- type: input
attributes:
label: Clap Version
description: Can be found in Cargo.lock or Cargo.toml of your project (i.e. `grep clap Cargo.lock`). PLEASE DO NOT PUT "latest" HERE, use precise version. Put `master` (or other branch) if you're using the repo directly.
validations:
required: true
- type: input
attributes:
label: Where?
description: A hyperlink, file path, some item, *something*.
validations:
required: true
- type: textarea
attributes:
label: What's wrong?
description: Explain what the issue is about, like - "The doc says that the sun is green!".
validations:
required: true
- type: textarea
attributes:
label: How to fix?
description: Any solution you think is right, like - "Everybody knows that the sun is blue!".

View file

@ -1,38 +1,28 @@
name: Feature request
description: Suggest an idea for this project
labels: 'T: new feature'
labels:
- 'T: new feature'
# - 'C-enhancement'
# - 'S-triage'
body:
- type: checkboxes
attributes:
label: Please complete the following tasks
options:
- label: I have searched the [discussions](https://github.com/clap-rs/clap/discussions)
required: true
- label: I have searched the existing issues
required: true
- type: input
attributes:
label: Clap Version
label: Version
description: Can be found in Cargo.lock or Cargo.toml of your project (i.e. `grep clap Cargo.lock`). PLEASE DO NOT PUT "latest" HERE, use precise version. Put `master` (or other branch) if you're using the repo directly.
validations:
required: true
- type: textarea
attributes:
label: Describe your use case
description: Describe the problem you're trying to solve. This is not mandatory and we *do* consider features without a specific use case, but real problems have priority.
validations:
required: true
label: Use Case
description: Describe the problem you're trying to solve.
- type: textarea
attributes:
label: Describe the solution you'd like
description: Please explain what the wanted solution should look like. You are **strongly encouraged** to attach a snippet of (pseudo)code.
validations:
required: true
label: Requirements
description: Describe what is needed to satisfy your use case.
- type: textarea
attributes:
label: Alternatives, if applicable
description: A clear and concise description of any alternative solutions or features you've managed to come up with.
- type: textarea
attributes:
label: Additional Context
description: Add any other context about the feature request here.
label: Possible Solutions
description: A clear and concise description of any solutions or features you've managed to come up with.

View file

@ -1,5 +0,0 @@
<!--
If your PR closes some issues, please write `Closes #XXXX`
where `XXXX` is the number of the issue you want to fix.
Each issue goes on its own line.
-->

10
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: cargo
directory: "/"
schedule:
interval: monthly
time: "07:00"
open-pull-requests-limit: 10
labels:
- C-dependencies

104
.github/settings.yml vendored Normal file
View file

@ -0,0 +1,104 @@
# These settings are synced to GitHub by https://probot.github.io/apps/settings/
repository:
description: A full featured, fast Command Line Argument Parser for Rust
homepage: docs.rs/clap
topics: rust cli command-line argparse clap
has_issues: true
has_projects: false
has_wiki: false
has_downloads: true
default_branch: master
allow_squash_merge: true
allow_merge_commit: true
allow_rebase_merge: true
# Manual: allow_auto_merge: true, see https://github.com/probot/settings/issues/402
delete_branch_on_merge: true
#labels:
# - name: "A-builder"
# description: "Area: Builder API"
# color: '#f7e101'
# - name: "A-derive"
# description: "Area: #[derive]` macro API"
# color: '#f7e101'
# - name: "A-docs"
# description: "Area: documentation, including docs.rs, readme, examples, etc..."
# color: '#f7e101'
# - name: "A-completion"
# description: "Area: completion generator"
# color: '#f7e101'
# - name: "A-help"
# description: "Area: Help or usage messages"
# color: '#f7e101'
# - name: "A-meta"
# description: "Area: administrative question or tracking issue"
# color: '#f7e101'
# - name: "A-parsing"
# description: "Area: Parser's logic and needs it changed somehow."
# color: '#f7e101'
# - name: "C-bug"
# description: "Category: Things not working as expected"
# color: '#f5f1fd'
# - name: "C-enhancement"
# description: "Category: Raise on the bar on expectations"
# color: '#f5f1fd'
# - name: "C-tracking-issue"
# description: "Category: A tracking issue for an unstable feature"
# color: '#f5f1fd'
# - name: "C-dependencies"
# description: "Category: Updating dependencies"
# color: '#f5f1fd'
# - name: "E-easy"
# description: "Call for participation: Experience needed to fix: Easy / not much"
# color: '#02E10C'
# - name: "E-medium"
# description: "Call for participation: Experience needed to fix: Medium / intermediate"
# color: '#02E10C'
# - name: "E-hard"
# description: "Call for participation: Experience needed to fix: Hard / a lot"
# color: '#02E10C'
# - name: "E-help-wanted"
# description: "Call for participation: Help is requested to fix this issue."
# color: '#02E10C'
# - name: "S-triage"
# description: "Status: New; needs maintainer attention."
# color: '#D3DDDD'
# - name: "S-blocked"
# description: "Status: Blocked on something else such as an RFC or other implementation work."
# color: '#D3DDDD'
# - name: "S-experimental"
# description: "Status: Ongoing experiment that does not require reviewing and won't be merged in its current state."
# color: '#D3DDDD'
# - name: "S-waiting-on-design"
# description: "Status: Waiting on user-facing design to be resolved before implementing"
# color: '#D3DDDD'
# - name: "S-waiting-on-decision"
# description: "Status: Waiting on a go/no-go before implementing"
# color: '#D3DDDD'
# - name: "S-waiting-on-mentor"
# description: "Status: Needs elaboration on the details before doing a 'Call for participation'"
# color: '#D3DDDD'
# - name: "S-waiting-on-author"
# description: "Status: This is awaiting some action (such as code changes or more information) from the author."
# color: '#D3DDDD'
# - name: "M-breaking-change"
# description: "Meta: Implementing or merging this will introduce a breaking change."
# color: '#E10C02'
# - name: "M-unreviewed"
# description: "Meta: Request for post-merge review."
# color: '#E10C02'
branches:
- name: master
protection:
required_pull_request_reviews: null
required_conversation_resolution: true
required_status_checks:
# Required. Require branches to be up to date before merging.
strict: false
contexts: ["CI", "Lint Commits", "Spell Check with Typos"]
enforce_admins: false
restrictions: null

21
.github/workflows/audit.yml vendored Normal file
View file

@ -0,0 +1,21 @@
name: Security audit
on:
pull_request:
paths:
- '**/Cargo.toml'
- '**/Cargo.lock'
push:
paths:
- '**/Cargo.toml'
- '**/Cargo.lock'
schedule:
- cron: '3 3 3 * *'
jobs:
security_audit:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -1,87 +0,0 @@
name: CI-PR
on:
pull_request:
branches: [master]
concurrency:
group: ci-pr-${{ github.ref }}
cancel-in-progress: true
jobs:
ci-pr:
name: CI-PR
needs: [test-minimal, test-full, msrv]
runs-on: ubuntu-latest
steps:
- name: Done
run: exit 0
test-minimal:
name: Tests (Minimal)
env:
FLAGS: --no-default-features --features 'std cargo'
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
rust: [stable]
runs-on: ${{ matrix.os }}
steps:
- name: Install rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- name: Cache Builds
uses: Swatinem/rust-cache@v1
- name: Checkout
uses: actions/checkout@v2
- name: Compile
run: cargo test --no-run ${{ env.FLAGS }}
- name: Test
run: cargo test ${{ env.FLAGS }}
test-full:
name: Tests (Full)
env:
FLAGS: --features 'wrap_help yaml regex unstable-replace unstable-multicall unstable-grouped'
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
rust: [stable]
runs-on: ${{ matrix.os }}
steps:
- name: Install rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- name: Cache Builds
uses: Swatinem/rust-cache@v1
- name: Checkout
uses: actions/checkout@v2
- name: Compile
run: cargo test --no-run ${{ env.FLAGS }}
- name: Test
run: cargo test ${{ env.FLAGS }}
msrv:
name: "Check MSRV: 1.54.0"
runs-on: ubuntu-latest
steps:
- name: Install rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: 1.54.0 # MSRV
override: true
- name: Cache Builds
uses: Swatinem/rust-cache@v1
- name: Checkout
uses: actions/checkout@v2
- name: Default features
run: cargo check --all-targets
- name: All features + Debug
run: cargo check --all-targets --features "wrap_help yaml regex unstable-replace unstable-multicall unstable-grouped debug"
- name: No features
run: cargo check --all-targets --no-default-features --features "std cargo"
- name: UI Tests
run: cargo test --package clap_derive -- ui

View file

@ -1,175 +1,181 @@
name: CI
on:
pull_request:
paths:
- '**'
- '!/*.md'
- '!/docs/**'
- "!/LICENSE-*"
push:
branches: [staging, trying]
branches:
- master
paths:
- '**'
- '!/*.md'
- '!/docs/**'
- "!/LICENSE-*"
schedule:
- cron: '3 3 3 * *'
jobs:
ci:
name: CI
needs: [test, wasm]
needs: [test, check, docs, rustfmt, clippy]
runs-on: ubuntu-latest
steps:
- name: Done
run: exit 0
test:
name: Tests
name: Test
strategy:
matrix:
build: [linux, windows, mac, minimal, default]
include:
- build: linux
os: ubuntu-latest
rust: "stable"
features: "full"
- build: windows
os: windows-latest
rust: "stable"
features: "full"
- build: mac
os: macos-latest
rust: "stable"
features: "full"
- build: minimal
os: ubuntu-latest
rust: "stable"
features: "minimal"
- build: default
os: ubuntu-latest
rust: "stable"
features: "default"
continue-on-error: ${{ matrix.rust != 'stable' }}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
profile: minimal
override: true
- uses: Swatinem/rust-cache@v1
- name: Build
run: make build-${{matrix.features}}
- name: Test
run: make test-${{matrix.features}}
- name: Test (benches)
run: make test-${{matrix.features}} ARGS='--workspace --benches'
- name: Test (ultra-minimal)
if: matrix.build == 'minimal'
run: make test-minimal ARGS='--manifest-path Cargo.toml'
check:
name: Check
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
rust: [1.54.0, stable, beta]
os: [windows-latest, macos-latest, ubuntu-latest]
target:
- i686-pc-windows-msvc
- x86_64-pc-windows-msvc
- i686-pc-windows-gnu
- x86_64-pc-windows-gnu
- x86_64-unknown-linux-gnu
- i686-unknown-linux-gnu
- x86_64-apple-darwin
features:
- none
- all
- release
exclude:
- features: release
build: [msrv, wasm, wasm-wasi, debug, release]
include:
- build: msrv
rust: 1.54.0 # MSRV
target: x86_64-unknown-linux-gnu
features: full
- build: wasm
rust: stable
target: wasm32-unknown-unknown
features: wasm
- build: wasm-wasi
rust: stable
target: wasm32-wasi
features: wasm
- build: debug
rust: stable
- features: release
rust: beta
- os: windows-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-unknown-linux-gnu
- os: windows-latest
target: i686-unknown-linux-gnu
- os: macos-latest
target: i686-pc-windows-msvc
- os: macos-latest
target: x86_64-pc-windows-msvc
- os: macos-latest
target: i686-pc-windows-gnu
- os: macos-latest
target: x86_64-pc-windows-gnu
- os: macos-latest
features: debug
- build: release
rust: stable
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: i686-unknown-linux-gnu
- os: ubuntu-latest
target: i686-pc-windows-msvc
- os: ubuntu-latest
target: x86_64-pc-windows-msvc
- os: ubuntu-latest
target: i686-pc-windows-gnu
- os: ubuntu-latest
target: x86_64-pc-windows-gnu
- os: ubuntu-latest
target: x86_64-apple-darwin
runs-on: ${{ matrix.os }}
features: release
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Checkout
uses: actions/checkout@v2
- name: Install linker
if: matrix.target == 'i686-pc-windows-gnu'
uses: egor-tensin/setup-mingw@v2
with:
platform: x86
- name: Install linker
if: matrix.target == 'x86_64-pc-windows-gnu'
uses: egor-tensin/setup-mingw@v2
- name: Install linker
if: matrix.target == 'i686-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install gcc-multilib
- name: Test almost no features
uses: actions-rs/cargo@v1
if: matrix.features == 'none'
with:
command: test
args: --target ${{ matrix.target }} --no-default-features --features "std cargo" -p clap:3.0.0-beta.5
- name: Test all features
uses: actions-rs/cargo@v1
if: matrix.features == 'all'
with:
command: test
args: --target ${{ matrix.target }} --features "wrap_help yaml regex unstable-replace unstable-multicall unstable-grouped"
- name: Check debug
uses: actions-rs/cargo@v1
if: matrix.features == 'all'
with:
command: check
args: --target ${{ matrix.target }} --features "wrap_help yaml regex unstable-replace unstable-multicall unstable-grouped debug"
- name: Test release
uses: actions-rs/cargo@v1
if: matrix.features == 'release'
with:
command: test
args: --target ${{ matrix.target }} --features "wrap_help yaml regex unstable-replace unstable-multicall unstable-grouped" --release
nightly:
name: Nightly Tests
strategy:
fail-fast: false
matrix:
features:
- none
- all
- release
runs-on: ubuntu-latest
steps:
- name: Install rust
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: Checkout
uses: actions/checkout@v2
- name: Test almost no features
uses: actions-rs/cargo@v1
if: matrix.features == 'none'
with:
command: test
args: --no-default-features --features "std cargo" -p clap:3.0.0-beta.5
- name: Test all features
uses: actions-rs/cargo@v1
if: matrix.features == 'all'
with:
command: test
args: --features "wrap_help yaml regex unstable-replace unstable-multicall unstable-grouped"
- name: Check debug
uses: actions-rs/cargo@v1
if: matrix.features == 'all'
with:
command: check
args: --features "wrap_help yaml regex unstable-replace unstable-multicall unstable-grouped debug"
- name: Test release
uses: actions-rs/cargo@v1
if: matrix.features == 'release'
with:
command: test
args: --features "wrap_help yaml regex unstable-replace unstable-multicall unstable-grouped" --release
wasm:
name: Wasm Check
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target: [wasm32-unknown-unknown, wasm32-wasi]
steps:
- name: Install rust
uses: actions-rs/toolchain@v1
with:
toolchain: 1.54.0
target: ${{ matrix.target }}
override: true
- name: Checkout
uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- name: Check
uses: actions-rs/cargo@v1
with:
command: check
args: --target ${{ matrix.target }} --features "yaml regex unstable-replace unstable-multicall unstable-grouped"
run: make check-${{ matrix.features }}
ui:
name: UI Tests
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: 1.54.0 # MSRV
profile: minimal
override: true
- uses: Swatinem/rust-cache@v1
- name: UI Tests
run: cargo test --test derive_ui --features derive
docs:
name: Docs
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- uses: Swatinem/rust-cache@v1
- name: Check documentation
env:
RUSTDOCFLAGS: -D warnings
run: cargo doc --workspace --all-features --no-deps --document-private-items
rustfmt:
name: rustfmt
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
# Not MSRV because its harder to jump between versions and people are
# more likely to have stable
toolchain: stable
profile: minimal
override: true
components: rustfmt
- uses: Swatinem/rust-cache@v1
- name: Check formatting
run: cargo fmt --all -- --check
clippy:
name: clippy
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: 1.54.0 # MSRV
profile: minimal
override: true
components: clippy
- uses: Swatinem/rust-cache@v1
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace --all-features --all-targets -- -D warnings

14
.github/workflows/committed.yml vendored Normal file
View file

@ -0,0 +1,14 @@
name: Lint Commits
on: [pull_request]
jobs:
committed:
name: Lint Commits
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Lint Commits
uses: crate-ci/committed@master

View file

@ -1,39 +0,0 @@
name: Coverage
on:
pull_request:
branches: [master]
push:
branches: [master]
concurrency:
group: coverage-${{ github.ref }}
cancel-in-progress: true
jobs:
coverage:
name: Coverage
continue-on-error: true
runs-on: ubuntu-latest
steps:
- name: Install rust
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: llvm-tools-preview
override: true
- name: Checkout
uses: actions/checkout@v2
- name: Install llvm-cov
uses: actions-rs/install@v0.1
with:
crate: cargo-llvm-cov
version: 0.1.0-alpha.4
use-tool-cache: true
- name: Coverage
uses: actions-rs/cargo@v1
with:
command: llvm-cov
args: --features "wrap_help yaml regex unstable-replace unstable-multicall unstable-grouped" --lcov --output-path lcov.info
- name: Coveralls
uses: coverallsapp/github-action@master
with:
path-to-lcov: lcov.info
github-token: ${{ secrets.github_token }}

View file

@ -1,40 +0,0 @@
name: Lint
on:
pull_request:
branches: [master]
push:
branches: [staging, trying]
concurrency:
group: lint-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Install rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Cache Builds
uses: Swatinem/rust-cache@v1
- name: Checkout
uses: actions/checkout@v2
- name: Clippy for almost no features
uses: actions-rs/cargo@v1
with:
command: clippy
args: --no-default-features --features "std cargo"
- name: Clippy for all features
uses: actions-rs/cargo@v1
with:
command: clippy
args: --features "wrap_help yaml regex unstable-replace unstable-multicall unstable-grouped" -- -D warnings
- name: Format check
uses: actions-rs/cargo@v1
with:
command: fmt
args: -- --check

12
.github/workflows/pre-commit.yml vendored Normal file
View file

@ -0,0 +1,12 @@
name: pre-commit
on:
pull_request:
push:
branches: [source]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: pre-commit/action@v2.0.3

106
.github/workflows/rust-next.yml vendored Normal file
View file

@ -0,0 +1,106 @@
name: rust-next
on:
schedule:
- cron: '3 3 3 * *'
jobs:
test:
name: Test
strategy:
matrix:
build: [linux, windows, mac, beta, nightly, minimal, default]
include:
- build: linux
os: ubuntu-latest
rust: "stable"
features: "full"
- build: windows
os: windows-latest
rust: "stable"
features: "full"
- build: mac
os: macos-latest
rust: "stable"
features: "full"
- build: beta
os: ubuntu-latest
rust: "beta"
features: "full"
- build: nightly
os: ubuntu-latest
rust: "nightly"
features: "full"
- build: minimal
os: ubuntu-latest
rust: "stable"
features: "minimal"
- build: default
os: ubuntu-latest
rust: "stable"
features: "default"
continue-on-error: ${{ matrix.rust != 'stable' }}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
profile: minimal
override: true
- uses: Swatinem/rust-cache@v1
- name: Build
run: make build-${{matrix.features}}
- name: Test
run: make test-${{matrix.features}}
- name: Test (benches)
run: make test-${{matrix.features}} ARGS='--workspace --benches'
- name: Test (ultra-minimal)
if: matrix.build == 'minimal'
run: make test-minimal ARGS='--manifest-path Cargo.toml'
rustfmt:
name: rustfmt
strategy:
matrix:
rust:
- stable
- beta
continue-on-error: ${{ matrix.rust != 'stable' }}
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
profile: minimal
override: true
components: rustfmt
- uses: Swatinem/rust-cache@v1
- name: Check formatting
run: cargo fmt --all -- --check
clippy:
name: clippy
strategy:
matrix:
rust:
- 1.54.0 # MSRV
- stable
continue-on-error: ${{ matrix.rust != '1.54.0' }} # MSRV
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
profile: minimal
override: true
components: clippy
- uses: Swatinem/rust-cache@v1
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace --all-features --all-targets -- -D warnings

View file

@ -1,28 +0,0 @@
name: Site
on:
push:
branches: [master]
paths:
- site/**
- .github/workflows/site.yml
concurrency:
group: site
cancel-in-progress: true
jobs:
site:
name: Deploy site
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build
uses: shalzz/zola-deploy-action@v0.14.1
env:
BUILD_DIR: site
BUILD_ONLY: true
- name: Deploy
if: ${{ success() }}
uses: JamesIves/github-pages-deploy-action@4.1.5
with:
branch: gh-pages
folder: site/public

12
.github/workflows/spelling.yml vendored Normal file
View file

@ -0,0 +1,12 @@
name: Spelling
on: [pull_request]
jobs:
spelling:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v2
- name: Spell Check Repo
uses: crate-ci/typos@master

35
.gitignore vendored
View file

@ -1,35 +1,2 @@
# Compiled files
*.o
*.so
*.rlib
*.dll
# Executables
*.exe
# Generated by Cargo
target/
# Cargo files
target
Cargo.lock
# Temp files
.*~
# Backup files
*.bak
*.bk
*.orig
# Project files
.vscode/*
.idea/*
clap-rs.iml
# Auxiliary files
test-results.test
expanded.rs
clap_derive/expanded.rs
# ctags
**/*.vi

21
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,21 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
stages: [commit]
- id: check-json
stages: [commit]
- id: check-toml
stages: [commit]
- id: check-merge-conflict
stages: [commit]
- id: check-case-conflict
stages: [commit]
- id: detect-private-key
stages: [commit]
- repo: https://github.com/crate-ci/committed
rev: v0.2.5
hooks:
- id: committed
stages: [commit-msg]

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,17 @@
[workspace]
members = [
"clap_derive",
"clap_generate",
"clap_generate_fig",
]
[package]
name = "clap"
version = "3.0.0-beta.5"
edition = "2018"
authors = [
"Kevin K. <kbknapp@gmail.com>",
"Clap Maintainers"
]
include = [
"src/**/*",
"Cargo.toml",
"LICENSE-*",
"README.md"
]
description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
repository = "https://github.com/clap-rs/clap"
documentation = "https://docs.rs/clap/"
homepage = "https://clap.rs/"
categories = ["command-line-interface"]
keywords = [
"argument",
"cli",
@ -23,9 +19,48 @@ keywords = [
"parser",
"parse"
]
categories = ["command-line-interface"]
edition = "2018"
license = "MIT OR Apache-2.0"
readme = "README.md"
include = [
"build.rs",
"src/**/*",
"Cargo.toml",
"LICENSE*",
"README.md",
"benches/**/*",
"examples/**/*"
]
[package.metadata.docs.rs]
features = ["doc"]
[features]
default = [
"std",
"color",
"suggestions",
]
debug = ["clap_derive/debug", "backtrace"] # Enables debug messages
doc = ["derive", "cargo", "wrap_help", "yaml", "env", "unicode", "regex", "unstable-replace", "unstable-multicall", "unstable-grouped"] # for docs.rs
# Used in default
std = ["indexmap/std"] # support for no_std in a backwards-compatible way
color = ["atty", "termcolor"]
suggestions = ["strsim"]
# Optional
derive = ["clap_derive", "lazy_static"]
cargo = ["lazy_static"] # Disable if you're not using Cargo, enables Cargo-env-var-dependent macros
wrap_help = ["terminal_size", "textwrap/terminal_size"]
yaml = ["yaml-rust"]
env = [] # Use environment variables during arg parsing
unicode = ["textwrap/unicode-width", "unicase"] # Support for unicode characters in arguments and help messages
# In-work features
unstable-replace = []
unstable-multicall = []
unstable-grouped = []
[lib]
bench = false
@ -79,46 +114,182 @@ backtrace = { version = "0.3", optional = true }
[dev-dependencies]
regex = "1.0"
lazy_static = "1"
version-sync = "0.9"
criterion = "0.3.2"
trybuild = "1.0"
rustversion = "1"
# Cutting out `filesystem` feature
trycmd = { version = "0.8.2", default-features = false, features = ["color-auto", "diff", "examples"] }
[[example]]
name = "demo"
required-features = ["derive"]
[[example]]
name = "escaped_positional"
required-features = ["cargo"]
[[example]]
name = "escaped_positional_derive"
required-features = ["derive"]
[[example]]
name = "git_derive"
required-features = ["derive"]
[[example]]
name = "busybox"
path = "examples/24a_multicall_busybox.rs"
path = "examples/multicall_busybox.rs"
required-features = ["unstable-multicall"]
[[example]]
name = "hostname"
path = "examples/24b_multicall_hostname.rs"
path = "examples/multicall_hostname.rs"
required-features = ["unstable-multicall"]
[features]
default = [
"std",
"derive",
"cargo",
"color",
"env",
"suggestions",
"unicode",
]
debug = ["clap_derive/debug", "backtrace"] # Enables debug messages
[[example]]
name = "01_quick"
path = "examples/tutorial_builder/01_quick.rs"
required-features = ["cargo"]
# Used in default
std = ["indexmap/std"] # support for no_std in a backwards-compatible way
derive = ["clap_derive", "lazy_static"]
cargo = ["lazy_static"] # Disable if you're not using Cargo, enables Cargo-env-var-dependent macros
color = ["atty", "termcolor"]
env = [] # Use environment variables during arg parsing
suggestions = ["strsim"]
unicode = ["textwrap/unicode-width", "unicase"] # Support for unicode characters in arguments and help messages
[[example]]
name = "02_apps"
path = "examples/tutorial_builder/02_apps.rs"
required-features = ["cargo"]
# Optional
wrap_help = ["terminal_size", "textwrap/terminal_size"]
yaml = ["yaml-rust"]
[[example]]
name = "02_crate"
path = "examples/tutorial_builder/02_crate.rs"
required-features = ["cargo"]
# In-work features
unstable-replace = []
unstable-multicall = []
unstable-grouped = []
[[example]]
name = "02_app_settings"
path = "examples/tutorial_builder/02_app_settings.rs"
required-features = ["cargo"]
[[example]]
name = "03_01_flag_bool"
path = "examples/tutorial_builder/03_01_flag_bool.rs"
required-features = ["cargo"]
[[example]]
name = "03_01_flag_count"
path = "examples/tutorial_builder/03_01_flag_count.rs"
required-features = ["cargo"]
[[example]]
name = "03_02_option"
path = "examples/tutorial_builder/03_02_option.rs"
required-features = ["cargo"]
[[example]]
name = "03_03_positional"
path = "examples/tutorial_builder/03_03_positional.rs"
required-features = ["cargo"]
[[example]]
name = "03_04_subcommands"
path = "examples/tutorial_builder/03_04_subcommands.rs"
required-features = ["cargo"]
[[example]]
name = "03_05_default_values"
path = "examples/tutorial_builder/03_05_default_values.rs"
required-features = ["cargo"]
[[example]]
name = "04_01_possible"
path = "examples/tutorial_builder/04_01_possible.rs"
required-features = ["cargo"]
[[example]]
name = "04_01_enum"
path = "examples/tutorial_builder/04_01_enum.rs"
required-features = ["cargo", "derive"]
[[example]]
name = "04_02_validate"
path = "examples/tutorial_builder/04_02_validate.rs"
required-features = ["cargo"]
[[example]]
name = "04_03_relations"
path = "examples/tutorial_builder/04_03_relations.rs"
required-features = ["cargo"]
[[example]]
name = "04_04_custom"
path = "examples/tutorial_builder/04_04_custom.rs"
required-features = ["cargo"]
[[example]]
name = "01_quick_derive"
path = "examples/tutorial_derive/01_quick.rs"
required-features = ["derive"]
[[example]]
name = "02_apps_derive"
path = "examples/tutorial_derive/02_apps.rs"
required-features = ["derive"]
[[example]]
name = "02_crate_derive"
path = "examples/tutorial_derive/02_crate.rs"
required-features = ["derive"]
[[example]]
name = "02_app_settings_derive"
path = "examples/tutorial_derive/02_app_settings.rs"
required-features = ["derive"]
[[example]]
name = "03_01_flag_bool_derive"
path = "examples/tutorial_derive/03_01_flag_bool.rs"
required-features = ["derive"]
[[example]]
name = "03_01_flag_count_derive"
path = "examples/tutorial_derive/03_01_flag_count.rs"
required-features = ["derive"]
[[example]]
name = "03_02_option_derive"
path = "examples/tutorial_derive/03_02_option.rs"
required-features = ["derive"]
[[example]]
name = "03_03_positional_derive"
path = "examples/tutorial_derive/03_03_positional.rs"
required-features = ["derive"]
[[example]]
name = "03_04_subcommands_derive"
path = "examples/tutorial_derive/03_04_subcommands.rs"
required-features = ["derive"]
[[example]]
name = "03_05_default_values_derive"
path = "examples/tutorial_derive/03_05_default_values.rs"
required-features = ["derive"]
[[example]]
name = "04_01_enum_derive"
path = "examples/tutorial_derive/04_01_enum.rs"
required-features = ["derive"]
[[example]]
name = "04_02_validate_derive"
path = "examples/tutorial_derive/04_02_validate.rs"
required-features = ["derive"]
[[example]]
name = "04_03_relations_derive"
path = "examples/tutorial_derive/04_03_relations.rs"
required-features = ["derive"]
[[example]]
name = "04_04_custom_derive"
path = "examples/tutorial_derive/04_04_custom.rs"
required-features = ["derive"]
[profile.test]
opt-level = 1
@ -127,20 +298,3 @@ opt-level = 1
lto = true
codegen-units = 1
[package.metadata.docs.rs]
features = ["yaml", "regex", "unstable-replace", "unstable-multicall", "unstable-grouped"]
targets = ["x86_64-unknown-linux-gnu"]
[workspace]
members = [
"clap_derive",
"clap_generate",
"clap_generate_fig",
"clap_up",
]
default-members = [
".",
"clap_derive",
"clap_generate",
"clap_generate_fig",
]

90
FAQ.md
View file

@ -1,90 +0,0 @@
1. [Comparisons](#comparisons)
1. [How does `clap` compare to structopt?](#how-does-clap-compare-to-structopt)
2. [How does `clap` compare to getopts?](#how-does-clap-compare-to-getopts)
3. [How does `clap` compare to docopt.rs?](#how-does-clap-compare-to-docoptrs)
4. [What are some reasons to use `clap`? (The Pitch)](#what-are-some-reasons-to-use-clap-the-pitch)
5. [What are some reasons *not* to use `clap`? (The Anti Pitch)](#what-are-some-reasons-not-to-use-clap-the-anti-pitch)
6. [Reasons to use `clap`](#reasons-to-use-clap)
7. [Reasons to `docopt`](#reasons-to-docopt)
8. [Reasons to use `getopts`](#reasons-to-use-getopts)
2. [How many methods are there to create an App/Arg?](#how-many-methods-are-there-to-create-an-apparg)
3. [Why is there a default subcommand of help?](#why-is-there-a-default-subcommand-of-help)
### Comparisons
First, let me say that these comparisons are highly subjective, and not meant in a critical or harsh manner. All the argument parsing libraries out there (to include `clap`) have their own strengths and weaknesses. Sometimes it just comes down to personal taste when all other factors are equal. When in doubt, try them all and pick one that you enjoy :) There's plenty of room in the Rust community for multiple implementations!
#### How does `clap` compare to [structopt](https://github.com/TeXitoi/structopt)?
Simple! `clap` *is* `structopt`. With the 3.0 release, `clap` imported the `structopt` code into its own codebase as the [`clap_derive`](https://github.com/clap-rs/clap/tree/master/clap_derive) crate. Since `structopt` already used `clap` under the hood, the transition was nearly painless, and is 100% feature compatible. `structopt` is soft deprecated and will only support clap 2.x.
If you were using `structopt` before, you have to change the attributes from `#[structopt(...)]` to `#[clap(...)]`.
Also the derive statements changed from `#[derive(Structopt)]` to `#[derive(Parser)]`. There is also some additional functionality and breaking changes that's been added to the `clap_derive` crate. See the documentation for that crate, for more details.
#### How does `clap` compare to [getopts](https://github.com/rust-lang-nursery/getopts)?
`getopts` is a very basic, fairly minimalist argument parsing library. This isn't a bad thing, sometimes you don't need tons of features, you just want to parse some simple arguments, and have some help text generated for you based on valid arguments you specify. The downside to this approach is that you must manually implement most of the common features (such as checking to display help messages, usage strings, etc.). If you want a highly custom argument parser, and don't mind writing the majority of the functionality yourself, `getopts` is an excellent base.
`getopts` also doesn't allocate much, or at all. This gives it a very small performance boost. Although, as you start implementing additional features, that boost quickly disappears.
Personally, I find many, many uses of `getopts` are manually implementing features that `clap` provides by default. Using `clap` simplifies your codebase allowing you to focus on your application, and not argument parsing.
#### How does `clap` compare to [docopt.rs](https://github.com/docopt/docopt.rs)?
I first want to say I'm a big a fan of BurntSushi's work, the creator of `Docopt.rs`. I aspire to produce the quality of libraries that this man does! When it comes to comparing these two libraries they are very different. `docopt` tasks you with writing a help message, and then it parses that message for you to determine all valid arguments and their use. Some people LOVE this approach, others do not. If you're willing to write a detailed help message, it's nice that you can stick that in your program and have `docopt` do the rest. On the downside, it's far less flexible.
`docopt` is also excellent at translating arguments into Rust types automatically. There is even a syntax extension which will do all this for you, if you're willing to use a nightly compiler (use of a stable compiler requires you to somewhat manually translate from arguments to Rust types). To use BurntSushi's words, `docopt` is also a sort of black box. You get what you get, and it's hard to tweak implementation or customize the experience for your use case.
Because `docopt` is doing a ton of work to parse your help messages and determine what you were trying to communicate as valid arguments, it's also one of the more heavy weight parsers performance-wise. For most applications this isn't a concern and this isn't to say `docopt` is slow, in fact far from it. This is just something to keep in mind while comparing.
#### What are some reasons to use `clap`? (The Pitch)
`clap` is as fast, and as lightweight as possible while still giving all the features you'd expect from a modern argument parser. In fact, for the amount and type of features `clap` offers it remains about as fast as `getopts`. If you use `clap` when just need some simple arguments parsed, you'll find it's a walk in the park. `clap` also makes it possible to represent extremely complex, and advanced requirements, without too much thought. `clap` aims to be intuitive, easy to use, and fully capable for wide variety use cases and needs.
#### What are some reasons *not* to use `clap`? (The Anti Pitch)
Depending on the style in which you choose to define the valid arguments, `clap` can be very verbose. `clap` also offers so many finetuning knobs and dials, that learning everything can seem overwhelming. I strive to keep the simple cases simple, but when turning all those custom dials it can get complex. `clap` is also opinionated about parsing. Even though so much can be tweaked and tuned with `clap` (and I'm adding more all the time), there are still certain features which `clap` implements in specific ways which may be contrary to some users use-cases.
#### Reasons to use `clap`
* You want all the nice CLI features your users may expect, yet you don't want to implement them all yourself. You'd like to focus your application, not argument parsing.
* In addition to the point above; you don't want to sacrifice performance to get all those nice features
* You have complex requirements/conflicts between your various valid args.
* You want to use subcommands (although other libraries also support subcommands, they are not nearly as feature rich as those provided by `clap`)
* You want some sort of custom validation built into the argument parsing process, instead of as part of your application (which allows for earlier failures, better error messages, more cohesive experience, etc.)
* You need more performance than `docopt` provides
#### Reasons to `docopt`
* You want *automatic* serialization of your arguments into Rust types (Although `clap` can do this, docopt is better at it)
* You are on nightly Rust and want the library to automatically generate an "arguments struct" from the matched args
* You are porting an application which uses docopt and already have the usage string already defined
#### Reasons to use `getopts`
* You need absolutely as few allocations as possible and don't mind implementing nearly everything yourself
* You want a portion of the arg parsing process to be very custom (again, implementing the details yourself)
### How many methods are there to create an App/Arg?
To build an `App` there are three:
* Derive Macros
* Builder Pattern
* Yaml
To build an `Arg` there are four:
* Derive Macros
* Builder Pattern
* Usage Strings
* Yaml
### Why is there a default subcommand of help?
There is only a default subcommand of `help` when other subcommands have been defined manually. So it's opt-in(ish), being that you only get a `help` subcommand if you're actually using subcommands.
Also, if the user defined a `help` subcommand themselves, the auto-generated one wouldn't be added (meaning it's only generated if the user hasn't defined one themselves).

28
Makefile Normal file
View file

@ -0,0 +1,28 @@
# CI Steps
#
# Considerations
# - Easy to debug: show the command being run
# - Leverage CI features: Only run individual steps so we can use features like reporting elapsed time per step
ARGS?=--workspace
TOOLCHAIN_TARGET ?=
ifneq (${TOOLCHAIN_TARGET},)
ARGS+=--target ${TOOLCHAIN_TARGET}
endif
_FEATURES = minimal default wasm full release
_FEATURES_minimal = --no-default-features --features "std"
_FEATURES_default =
_FEATURES_wasm = --features "derive cargo env unicode yaml regex unstable-replace unstable-multicall unstable-grouped"
_FEATURES_full = --features "derive cargo env unicode yaml regex unstable-replace unstable-multicall unstable-grouped wrap_help doc"
_FEATURES_debug = ${_FEATURES_full} --features debug
_FEATURES_release = ${_FEATURES_full} --release
check-%:
cargo check ${_FEATURES_${@:check-%=%}} --all-targets ${ARGS}
build-%:
cargo test ${_FEATURES_${@:build-%=%}} --all-targets --no-run ${ARGS}
test-%:
cargo test ${_FEATURES_${@:test-%=%}} ${ARGS}

571
README.md
View file

@ -1,6 +1,8 @@
<!-- omit in TOC -->
# clap
> **Command Line Argument Parser for Rust**
[![Crates.io](https://img.shields.io/crates/v/clap?style=flat-square)](https://crates.io/crates/clap)
[![Crates.io](https://img.shields.io/crates/d/clap?style=flat-square)](https://crates.io/crates/clap)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/master/LICENSE-APACHE)
@ -9,558 +11,133 @@
[![Coverage Status](https://img.shields.io/coveralls/github/clap-rs/clap/master?style=flat-square)](https://coveralls.io/github/clap-rs/clap?branch=master)
[![Contributors](https://img.shields.io/github/contributors/clap-rs/clap?style=flat-square)](https://github.com/clap-rs/clap/graphs/contributors)
Command Line Argument Parser for Rust
It is a simple-to-use, efficient, and full-featured library for parsing command line arguments and subcommands when writing command line, console or terminal applications.
* [Documentation][docs]
* [Questions & Discussions](https://github.com/clap-rs/clap/discussions)
* [Website](https://clap.rs/)
We are currently hard at work trying to release `3.0`. We have a `3.0.0-beta.5` prerelease out but we do not give any guarantees that its API is stable. We do not have a changelog yet which will be written down after we are sure about the API stability. We recommend users to not update to the prerelease version yet and to wait for the official `3.0`.
> If you're looking for the readme & examples for `clap v2.33` - find it on [github](https://github.com/clap-rs/clap/tree/v2.33.0), [crates.io](https://crates.io/crates/clap/2.33.0), [docs.rs](https://docs.rs/clap/2.33.0/clap/).
Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT).
1. [About](#about)
2. [FAQ](#faq)
3. [Features](#features)
4. [Quick Example](#quick-example)
1. [Using Derive Macros](#using-derive-macros)
2. [Using Builder Pattern](#using-builder-pattern)
3. [Using YAML](#using-yaml)
4. [Running it](#running-it)
5. [Try it!](#try-it)
1. [Pre-Built Test](#pre-built-test)
2. [Build Your Own Binary](#build-your-own-binary)
6. [Usage](#usage)
1. [Optional Dependencies / Features](#optional-dependencies--features)
1. [Features enabled by default](#features-enabled-by-default)
2. [Opt-in features](#opt-in-features)
3. [Experimental features](#experimental-features)
2. [More Information](#more-information)
7. [Sponsors](#sponsors)
8. [Contributing](#contributing)
1. [Compatibility Policy](#compatibility-policy)
1. [Minimum Supported Version of Rust (MSRV)](#minimum-supported-version-of-rust-msrv)
2. [Breaking Changes](#breaking-changes)
9. [License](#license)
10. [Related Crates](#related-crates)
2. Tutorial: [Builder API](https://github.com/clap-rs/clap/blob/master/examples/tutorial_builder/README.md), [Derive API](https://github.com/clap-rs/clap/blob/master/examples/tutorial_derive/README.md)
3. [Examples](https://github.com/clap-rs/clap/blob/master/examples/README.md)
4. [API Reference](https://docs.rs/clap)
- [Derive Reference](https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md)
- [Feature Flags](#feature-flags)
5. [CHANGELOG](https://github.com/clap-rs/clap/blob/master/docs/CHANGELOG.md)
6. [FAQ](https://github.com/clap-rs/clap/blob/master/docs/FAQ.md)
7. [Questions & Discussions](https://github.com/clap-rs/clap/discussions)
8. [Contributing](https://github.com/clap-rs/clap/blob/master/CONTRIBUTING.md)
## About
`clap` is used to parse *and validate* the string of command line arguments provided by a user at runtime. You provide the list of valid possibilities, and `clap` handles the rest. This means you focus on your *application's* functionality, and less on the parsing and validating of arguments.
Create your command-line parser, with all of the bells and whistles, declaratively or procedurally.
`clap` provides many things 'for free' (with no configuration) including the traditional version and help switches (or flags) along with associated messages. If you are using subcommands, `clap` will also auto-generate a `help` subcommand and separate associated help messages.
### Example
Once `clap` parses the user-provided string of arguments, it returns the matches along with any applicable values. If the user made an error or typo, `clap` informs them with a friendly message and exits gracefully (or returns a `Result` type and allows you to perform any clean up prior to exit). Because of this, you can make reasonable assumptions in your code about the validity of the arguments prior to your applications main execution.
<!-- Copied from examples/demo.{rs,md} -->
```rust,no_run
use clap::Parser;
## FAQ
[How does `clap` compare to structopt?](https://github.com/clap-rs/clap/blob/master/FAQ.md#how-does-clap-compare-to-structopt)
For a full FAQ, see [this](https://github.com/clap-rs/clap/blob/master/FAQ.md)
## Features
Below are a few of the features which `clap` supports, full descriptions and usage can be found in the [documentation][docs] and [examples][examples] directory
* Generate a CLI simply by defining a struct!
* **Auto-generated Help, Version, and Usage information**
- Can optionally be fully, or partially overridden if you want custom help, version, or usage statements
* **Auto-generated completion scripts (Bash, Zsh, Fish, Fig, Elvish and PowerShell)**
- Using [`clap_generate`](https://github.com/clap-rs/clap/tree/master/clap_generate)
- Even works through multiple levels of subcommands
- Works with options which only accept certain values
- Works with subcommand aliases
* **Flags / Switches** (i.e. bool fields)
- Both short and long versions supported (i.e. `-f` and `--flag` respectively)
- Supports combining short versions (i.e. `-fBgoZ` is the same as `-f -B -g -o -Z`)
- Supports multiple occurrences (i.e. `-vvv` or `-v -v -v`)
* **Positional Arguments** (i.e. those which are based off an index from the program name)
- Supports multiple values (i.e. `myprog <file>...` such as `myprog file1.txt file2.txt` being two values for the same "file" argument)
- Supports Specific Value Sets (See below)
- Can set value parameters (such as the minimum number of values, the maximum number of values, or the exact number of values)
- Can set custom validations on values to extend the argument-parsing capability to truly custom domains
* **Option Arguments** (i.e. those that take values)
- Both short and long versions supported (i.e. `-o value`, `-ovalue`, `-o=value` and `--option value` or `--option=value` respectively)
- Supports multiple values (i.e. `-o <val1> -o <val2>` or `-o <val1> <val2>`)
- Supports delimited values (i.e. `-o=val1,val2,val3`, can also change the delimiter)
- Supports Specific Value Sets (See below)
- Supports named values so that the usage/help info appears as `-o <FILE> <INTERFACE>` etc. for when you require specific multiple values
- Can set value parameters (such as the minimum number of values, the maximum number of values, or the exact number of values)
- Can set custom validations on values to extend the argument-parsing capability to truly custom domains
* **Sub-Commands** (i.e. `git add <file>` where `add` is a sub-command of `git`)
- Support their own sub-arguments, and sub-sub-commands independent of the parent
- Get their own auto-generated Help, Version, and Usage independent of parent
* **Support for building CLIs from YAML** - This keeps your Rust source nice and tidy and makes supporting localized translation very simple!
* **Requirement Rules**: Arguments can define the following types of requirement rules
- Can be required by default
- Can be required only if certain arguments are present
- Can require other arguments to be present
- Can be required only if certain values of other arguments are used
* **Confliction Rules**: Arguments can optionally define the following types of exclusion rules
- Can be disallowed when certain arguments are present
- Can disallow use of other arguments when present
* **Groups**: Arguments can be made part of a group
- Fully compatible with other relational rules (requirements, conflicts, and overrides) which allows things like requiring the use of any arg in a group, or denying the use of an entire group conditionally
* **Specific Value Sets**: Positional or Option Arguments can define a specific set of allowed values (i.e. imagine a `--mode` option which may *only* have one of two values `fast` or `slow` such as `--mode fast` or `--mode slow`)
* **Default Values**
- Also supports conditional default values (i.e. a default which only applies if specific arguments are used, or specific values of those arguments)
* **Automatic Version from Cargo.toml**: `clap` is fully compatible with Rust's `env!()` macro for automatically setting the version of your application to the version in your Cargo.toml. See [09_auto_version example](examples/09_auto_version.rs) for how to do this (Thanks to [jhelwig](https://github.com/jhelwig) for pointing this out)
* **Typed Values**: You can use several convenience macros provided by `clap` to get typed values (i.e. `i32`, `u8`, etc.) from positional or option arguments so long as the type you request implements `std::str::FromStr` See the [12_typed_values example](examples/12_typed_values.rs). You can also use `clap`s `arg_enum!` macro to create an enum with variants that automatically implement `std::str::FromStr`. See [13_enum_values example](examples/13_enum_values.rs) for details
* **Suggestions**: Suggests corrections when the user enters a typo. For example, if you defined a `--myoption` argument, and the user mistakenly typed `--moyption` (notice `y` and `o` transposed), they would receive a `Did you mean '--myoption'?` error and exit gracefully. This also works for subcommands and flags. (Thanks to [Byron](https://github.com/Byron) for the implementation) (This feature can optionally be disabled, see 'Optional Dependencies / Features')
* **Colorized Errors (Non Windows OS only)**: Error message are printed in colored text (this feature can optionally be disabled, see 'Optional Dependencies / Features').
* **Global Arguments**: Arguments can optionally be defined once, and be available to all child subcommands. These values will also be propagated up/down throughout all subcommands.
* **Custom Validations**: You can define a function to use as a validator of argument values. Imagine defining a function to validate IP addresses, or fail parsing upon error. This means your application logic can be solely focused on *using* values.
* **POSIX Compatible Conflicts/Overrides** - In POSIX args can be conflicting, but not fail parsing because whichever arg comes *last* "wins" so to speak. This allows things such as aliases (i.e. `alias ls='ls -l'` but then using `ls -C` in your terminal which ends up passing `ls -l -C` as the final arguments. Since `-l` and `-C` aren't compatible, this effectively runs `ls -C` in `clap` if you choose...`clap` also supports hard conflicts that fail parsing). (Thanks to [Vinatorul](https://github.com/Vinatorul)!)
* Supports the Unix `--` meaning, only positional arguments follow
## Quick Example
The following examples show a quick example of some of the very basic functionality of `clap`. For more advanced usage, such as requirements, conflicts, groups, multiple values and occurrences see the [documentation][docs], [examples][examples] directory of this repository.
**NOTE:** All of these examples are functionally the same, but show different styles in which to use `clap`. These different styles are purely a matter of personal preference.
Add `clap` to your `Cargo.toml`
```toml
[dependencies]
clap = "3.0.0-beta.5"
```
#### Using Derive Macros
The first example shows the simplest way to use `clap`, by defining a struct. If you're familiar with the `structopt` crate you're in luck, it's the same! (In fact it's the exact same code running under the covers!)
```rust,ignore
// (Full example with detailed comments in examples/01d_quick_example.rs)
//
// This example demonstrates clap's full 'custom derive' style of creating arguments which is the
// simplest method of use, but sacrifices some flexibility.
use clap::{AppSettings, Parser};
/// This doc string acts as a help message when the user runs '--help'
/// as do all doc strings on fields
#[derive(Parser)]
#[clap(version = "1.0", author = "Kevin K. <kbknapp@gmail.com>")]
struct Opts {
#[clap(about, version, author)] // Pull these from `Cargo.toml`
struct Cli {
/// Sets a custom config file. Could have been an Option<T> with no default too
#[clap(short, long, default_value = "default.conf")]
config: String,
#[clap(short, long, default_value = "default.toml", value_name = "PATH")]
config: std::path::PathBuf,
/// Some input. Because this isn't an Option<T> it's required to be used
input: String,
/// A level of verbosity, and can be used multiple times
#[clap(short, long, parse(from_occurrences))]
verbose: i32,
#[clap(subcommand)]
subcmd: SubCommand,
}
#[derive(Parser)]
enum SubCommand {
#[clap(version = "1.3", author = "Someone E. <someone_else@other.com>")]
Test(Test),
}
/// A subcommand for controlling testing
#[derive(Parser)]
struct Test {
/// Print debug info
#[clap(short)]
debug: bool
}
fn main() {
let opts: Opts = Opts::parse();
let args = Cli::parse();
// Gets a value for config if supplied by user, or defaults to "default.conf"
println!("Value for config: {}", opts.config);
println!("Using input file: {}", opts.input);
println!("Value for config: {}", args.config.display());
println!("Using input file: {}", args.input);
// Vary the output based on how many times the user used the "verbose" flag
// (i.e. 'myprog -v -v -v' or 'myprog -vvv' vs 'myprog -v'
match opts.verbose {
match args.verbose {
0 => println!("No verbose info"),
1 => println!("Some verbose info"),
2 => println!("Tons of verbose info"),
_ => println!("Don't be ridiculous"),
}
// You can handle information about subcommands by requesting their matches by name
// (as below), requesting just the name used, or both at the same time
match opts.subcmd {
SubCommand::Test(t) => {
if t.debug {
println!("Printing debug info...");
} else {
println!("Printing normally...");
}
}
}
// more program logic goes here...
}
```
#### Using Builder Pattern
This second method shows a method using the 'Builder Pattern' which allows more advanced configuration options (not shown in this small example), or even dynamically generating arguments when desired. The downside is it's more verbose.
```rust,no_run
// (Full example with detailed comments in examples/01b_quick_example.rs)
//
// This example demonstrates clap's "builder pattern" method of creating arguments
// which is the most flexible, but also most verbose.
use clap::{Arg, App};
fn main() {
let matches = App::new("My Super Program")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.arg(Arg::new("config")
.short('c')
.long("config")
.value_name("FILE")
.about("Sets a custom config file")
.takes_value(true))
.arg(Arg::new("INPUT")
.about("Sets the input file to use")
.required(true)
.index(1))
.arg(Arg::new("v")
.short('v')
.multiple_occurrences(true)
.takes_value(true)
.about("Sets the level of verbosity"))
.subcommand(App::new("test")
.about("controls testing features")
.version("1.3")
.author("Someone E. <someone_else@other.com>")
.arg(Arg::new("debug")
.short('d')
.about("print debug information verbosely")))
.get_matches();
// You can check the value provided by positional arguments, or option arguments
if let Some(i) = matches.value_of("INPUT") {
println!("Value for input: {}", i);
}
if let Some(c) = matches.value_of("config") {
println!("Value for config: {}", c);
}
// You can see how many times a particular flag or argument occurred
// Note, only flags can have multiple occurrences
match matches.occurrences_of("v") {
0 => println!("Verbose mode is off"),
1 => println!("Verbose mode is kind of on"),
2 => println!("Verbose mode is on"),
_ => println!("Don't be crazy"),
}
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level app
if let Some(ref matches) = matches.subcommand_matches("test") {
// "$ myapp test" was run
if matches.is_present("debug") {
// "$ myapp test -d" was run
println!("Printing debug info...");
} else {
println!("Printing normally...");
}
}
// Continued program logic goes here...
}
```
The next example shows a far less verbose method, but sacrifices some of the advanced configuration options (not shown in this small example). This method also takes a *very* minor runtime penalty.
```rust,no_run
// (Full example with detailed comments in examples/01a_quick_example.rs)
//
// This example demonstrates clap's "usage strings" method of creating arguments
// which is less verbose
use clap::App;
fn main() {
let matches = App::new("myapp")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.arg("-c, --config=[FILE] 'Sets a custom config file'")
.arg("<INPUT> 'Sets the input file to use'")
.arg("-v... 'Sets the level of verbosity'")
.subcommand(App::new("test")
.about("controls testing features")
.version("1.3")
.author("Someone E. <someone_else@other.com>")
.arg("-d, --debug 'Print debug information'"))
.get_matches();
// Same as previous example...
}
```
#### Using YAML
This third method shows how you can use a YAML file to build your CLI and keep your Rust source tidy
or support multiple localized translations by having different YAML files for each localization.
First, create the `cli.yaml` file to hold your CLI options, but it could be called anything we like:
```yaml
name: myapp
version: "1.0"
author: Kevin K. <kbknapp@gmail.com>
about: Does awesome things
args:
- config:
short: c
long: config
value_name: FILE
about: Sets a custom config file
takes_value: true
- INPUT:
about: Sets the input file to use
required: true
index: 1
- verbose:
short: v
multiple_occurrences: true
about: Sets the level of verbosity
subcommands:
- test:
about: controls testing features
version: "1.3"
author: Someone E. <someone_else@other.com>
args:
- debug:
short: d
long: debug
about: Print debug information
```
Since this feature requires additional dependencies that not everyone may want, it is *not* compiled in by default and we need to enable a feature flag in Cargo.toml:
Simply add the `yaml` feature flag to your `Cargo.toml`.
```toml
[dependencies]
clap = { version = "3.0.0-beta.5", features = ["yaml"] }
```
Finally we create our `main.rs` file just like we would have with the previous two examples:
```rust,ignore
// (Full example with detailed comments in examples/17_yaml.rs)
//
// This example demonstrates clap's building from YAML style of creating arguments which is far
// more clean, but takes a very small performance hit compared to the other two methods.
use clap::{App, load_yaml};
fn main() {
// The YAML file is found relative to the current file, similar to how modules are found
let yaml = load_yaml!("cli.yaml");
let matches = App::from(yaml).get_matches();
// Same as previous examples...
}
```
#### Running it
If you were to compile any of the above programs and run them with the flag `--help` or `-h` (or `help` subcommand, since we defined `test` as a subcommand) the following would be output (except the first example where the help message sort of explains the Rust code).
```bash
$ myprog --help
My Super Program 1.0
Kevin K. <kbknapp@gmail.com>
Does awesome things
$ demo --help
clap [..]
ARGS:
INPUT The input file to use
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
MyApp [OPTIONS] <INPUT> [SUBCOMMAND]
demo[EXE] [OPTIONS] <INPUT>
ARGS:
<INPUT> Some input. Because this isn't an Option<T> it's required to be used
OPTIONS:
-c, --config <FILE> Sets a custom config file
-c, --config <PATH> Sets a custom config file. Could have been an Option<T> with no default
too [default: default.toml]
-h, --help Print help information
-v Sets the level of verbosity
-v, --verbose A level of verbosity, and can be used multiple times
-V, --version Print version information
SUBCOMMANDS:
help Print this message or the help of the given subcommand(s)
test Controls testing features
```
*(version number and `.exe` extension on windows replaced by placeholders)*
**NOTE:** You could also run `myapp test --help` or `myapp help test` to see the help message for the `test` subcommand.
### Aspirations
## Try it!
- Out of the box, users get a polished CLI experience
- Including common argument behavior, help generation, suggested fixes for users, colored output, [shell completions](https://github.com/clap-rs/clap/tree/master/clap_generate), etc
- Flexible enough to port your existing CLI interface
- However, we won't necessarily streamline support for each use case
- Reasonable parse performance
- Resilient maintainership, including
- Willing to break compatibility rather than batching up breaking changes in large releases
- Leverage feature flags to keep to one active branch
- Being under [WG-CLI](https://github.com/rust-cli/team/) to increase the bus factor
- We follow semver and will wait about 6 months between major breaking changes
- We will support the last two minor Rust releases (MSRV)
### Pre-Built Test
While these aspirations can be at odds with fast build times and low binary
size, we will still strive to keep these reasonable for the flexibility you
get. Check out the
[argparse-benchmarks](https://github.com/rust-cli/argparse-benchmarks-rs) for
CLI parsers optimized for other use cases.
To try out the pre-built [examples][examples], use the following steps:
### Related Projects
* Clone the repository `$ git clone https://github.com/clap-rs/clap && cd clap/`
* Compile the example `$ cargo build --example <EXAMPLE>`
* Run the help info `$ ./target/debug/examples/<EXAMPLE> --help`
* Play with the arguments!
* You can also do a onetime run via `$ cargo run --example <EXAMPLE> -- [args to example]`
- [clap-verbosity-flag](https://github.com/rust-cli/clap-verbosity-flag)
- [clap-cargo](https://github.com/crate-ci/clap-cargo)
- [concolor-clap](https://github.com/rust-cli/concolor/tree/main/crates/clap)
- [Command-line Apps for Rust](https://rust-cli.github.io/book/index.html) book
- [`trycmd`](https://github.com/epage/trycmd): Snapshot testing
- Or for more control, [`assert_cmd`](https://github.com/assert-rs/assert_cmd) and [`assert_fs`](https://github.com/assert-rs/assert_fs)
### Build Your Own Binary
## Feature Flags
To test out `clap`'s default auto-generated help/version follow these steps:
* Create a new cargo project `$ cargo new fake --bin && cd fake`
* Write your program as described in the quick example section.
* Build your program `$ cargo build --release`
* Run with help or version `$ ./target/release/fake --help` or `$ ./target/release/fake --version`
## Usage
For full usage, add `clap` as a dependency in your `Cargo.toml` to use from crates.io:
```toml
[dependencies]
clap = "3.0.0-beta.5"
```
Define a list of valid arguments for your program (see the [documentation][docs] or [examples][examples] directory of this repo)
Then run `cargo build` or `cargo update && cargo build` for your project.
### Optional Dependencies / Features
Disabling optional features can decrease the binary size of `clap` and decrease the compile time. If binary size or compile times are extremely important to you, it is a good idea to disable the feautres that you are not using.
#### Features enabled by default
### Default Features
* **std**: _Not Currently Used._ Placeholder for supporting `no_std` environments in a backwards compatible manner.
* **derive**: Enables the custom derive (i.e. `#[derive(Parser)]`). Without this you must use one of the other methods of creating a `clap` CLI listed above. (builds dependency `clap_derive`)
* **color**: Turns on colored error messages.
* **suggestions**: Turns on the `Did you mean '--myoption'?` feature for when users make typos.
#### Optional features
* **derive**: Enables the custom derive (i.e. `#[derive(Parser)]`). Without this you must use one of the other methods of creating a `clap` CLI listed above.
* **cargo**: Turns on macros that read values from `CARGO_*` environment variables.
* **color**: Turns on colored error messages. (builds dependency `atty`, `termcolor`)
* **env**: Turns on the usage of environment variables during parsing.
* **suggestions**: Turns on the `Did you mean '--myoption'?` feature for when users make typos. (builds dependency `strsim`)
* **unicode**: Turns on support for unicode characters in arguments and help messages. (builds dependency `textwrap`, `unicase`)
To disable these, add this to your `Cargo.toml`:
```toml
[dependencies.clap]
version = "3.0.0-beta.5"
default-features = false
features = ["std"]
```
You can also selectively enable only the features you'd like to include, by adding:
```toml
[dependencies.clap]
version = "3.0.0-beta.5"
default-features = false
# Cherry-pick the features you'd like to use
features = ["std", "suggestions", "color"]
```
#### Opt-in features
* **regex**: Enables regex validators. (builds dependency `regex`)
* **wrap_help**: Turns on the help text wrapping feature, based on the terminal size. (builds dependency `term-size`)
* **yaml**: Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
* **regex**: Enables regex validators.
* **unicode**: Turns on support for unicode characters (including emoji) in arguments and help messages.
* **wrap_help**: Turns on the help text wrapping feature, based on the terminal size.
#### Experimental features
These features are opt-in. But be wary that they can contain breaking changes between minor releases.
**Warning:** These may contain breaking changes between minor releases.
* **unstable-replace**: Enable [`App::replace`](https://github.com/clap-rs/clap/issues/2836)
* **unstable-multicall**: Enable [`AppSettings::Multicall`](https://github.com/clap-rs/clap/issues/2861)
* **unstable-grouped**: Enable [`ArgMatches::grouped_values_of`](https://github.com/clap-rs/clap/issues/2924)
### More Information
You can find complete documentation on the [docs.rs][docs] for this project.
You can also find usage examples in the [examples][examples] directory of this repo.
## Sponsors
<!-- omit in TOC -->
### Gold
[![](https://opencollective.com/clap/tiers/gold.svg?avatarHeight=36&width=600)](https://opencollective.com/clap)
<!-- omit in TOC -->
### Silver
[![](https://opencollective.com/clap/tiers/silver.svg?avatarHeight=36&width=600)](https://opencollective.com/clap)
<!-- omit in TOC -->
### Bronze
[![](https://opencollective.com/clap/tiers/bronze.svg?avatarHeight=36&width=600)](https://opencollective.com/clap)
<!-- omit in TOC -->
### Backer
[![](https://opencollective.com/clap/tiers/backer.svg?avatarHeight=36&width=600)](https://opencollective.com/clap)
## Contributing
Details on how to contribute can be found in the [CONTRIBUTING.md](CONTRIBUTING.md) file.
### Compatibility Policy
Because `clap` takes SemVer and compatibility seriously, this is the official policy regarding breaking changes and minimum required versions of Rust.
`clap` will pin the minimum required version of Rust to the CI builds. Bumping the minimum version of Rust is considered a minor breaking change, meaning *at a minimum* the minor version of `clap` will be bumped.
In order to keep from being surprised of breaking changes, it is **highly** recommended to use the `~major.minor.patch` style in your `Cargo.toml` only if you wish to target a version of Rust that is *older* than current stable minus two releases:
```toml
[dependencies]
clap = "~3.0.0-beta.5"
```
This will cause *only* the patch version to be updated upon a `cargo update` call, and therefore cannot break due to new features, or bumped minimum versions of Rust.
#### Minimum Supported Version of Rust (MSRV)
The following is a list of the minimum required version of Rust to compile `clap` by our `MAJOR.MINOR` version number:
| clap | MSRV |
| :----: | :----: |
| >=3.0 | 1.54.0 |
| >=2.21 | 1.24.0 |
| >=2.2 | 1.12.0 |
| >=2.1 | 1.6.0 |
| >=1.5 | 1.4.0 |
| >=1.4 | 1.2.0 |
| >=1.2 | 1.1.0 |
| >=1.0 | 1.0.0 |
#### Breaking Changes
`clap` takes a similar policy to Rust and will bump the major version number upon breaking changes with only the following exceptions:
* The breaking change is to fix a security concern
* The breaking change is to be fixing a bug (i.e. relying on a bug as a feature)
* The breaking change is a feature isn't used in the wild, or all users of said feature have given approval *prior* to the change
## License
`clap` is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
See the [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) files in this repository for more information.
## Related Crates
There are several excellent crates which can be used with `clap`, I recommend checking them all out! If you've got a crate that would be a good fit to be used with `clap` open an issue and let me know, I'd love to add it!
* [`assert_cmd`](https://github.com/assert-rs/assert_cmd) - This crate allows you test your CLIs in a very intuitive and functional way!
[docs]: https://docs.rs/clap
[examples]: https://github.com/clap-rs/clap/tree/master/examples

View file

@ -1,4 +1,4 @@
use clap::{App, Arg};
use clap::{arg, App, Arg};
use criterion::{criterion_group, criterion_main, Criterion};
macro_rules! create_app {
@ -7,9 +7,9 @@ macro_rules! create_app {
.version("0.1")
.about("tests clap library")
.author("Kevin K. <kbknapp@gmail.com>")
.arg("-f --flag 'tests flags'")
.arg("-o --option=[opt] 'tests options'")
.arg("[positional] 'tests positional'")
.arg(arg!(-f --flag "tests flags"))
.arg(arg!(-o --option <opt> "tests options").required(false))
.arg(arg!([positional] "tests positional"))
}};
}
@ -19,14 +19,14 @@ pub fn build_simple(c: &mut Criterion) {
pub fn build_with_flag(c: &mut Criterion) {
c.bench_function("build_with_flag", |b| {
b.iter(|| App::new("claptests").arg(Arg::from("-s, --some 'something'")))
b.iter(|| App::new("claptests").arg(arg!(-s --some "something")))
});
}
pub fn build_with_flag_ref(c: &mut Criterion) {
c.bench_function("build_with_flag_ref", |b| {
b.iter(|| {
let arg = Arg::from("-s, --some 'something'");
let arg = arg!(-s --some "something");
App::new("claptests").arg(&arg)
})
});
@ -34,14 +34,14 @@ pub fn build_with_flag_ref(c: &mut Criterion) {
pub fn build_with_opt(c: &mut Criterion) {
c.bench_function("build_with_opt", |b| {
b.iter(|| App::new("claptests").arg(Arg::from("-s, --some <FILE> 'something'")))
b.iter(|| App::new("claptests").arg(arg!(-s --some <FILE> "something")))
});
}
pub fn build_with_opt_ref(c: &mut Criterion) {
c.bench_function("build_with_opt_ref", |b| {
b.iter(|| {
let arg = Arg::from("-s, --some <FILE> 'something'");
let arg = arg!(-s --some <FILE> "something");
App::new("claptests").arg(&arg)
})
});

View file

@ -1,4 +1,4 @@
use clap::{App, AppSettings, Arg, ArgSettings};
use clap::{arg, App, AppSettings, Arg};
use criterion::{criterion_group, criterion_main, Criterion};
static OPT3_VALS: [&str; 2] = ["fast", "slow"];
@ -10,41 +10,41 @@ macro_rules! create_app {
.version("0.1")
.about("tests clap library")
.author("Kevin K. <kbknapp@gmail.com>")
.arg("-o --option=[opt]... 'tests options'")
.arg("[positional] 'tests positionals'")
.arg(Arg::from("-f --flag... 'tests flags'").global(true))
.arg(arg!(-o --option <opt> ... "tests options").required(false))
.arg(arg!([positional] "tests positionals"))
.arg(arg!(-f --flag ... "tests flags").global(true))
.args(&[
Arg::from("[flag2] -F 'tests flags with exclusions'")
arg!(flag2: -F "tests flags with exclusions")
.conflicts_with("flag")
.requires("option2"),
Arg::from("--long-option-2 [option2] 'tests long options with exclusions'")
arg!(option2: --"long-option-2" <option2> "tests long options with exclusions")
.required(false)
.conflicts_with("option")
.requires("positional2"),
Arg::from("[positional2] 'tests positionals with exclusions'"),
Arg::from("-O --Option [option3] 'tests options with specific value sets'")
arg!([positional2] "tests positionals with exclusions"),
arg!(-O --Option <option3> "tests options with specific value sets")
.required(false)
.possible_values(OPT3_VALS),
Arg::from("[positional3]... 'tests positionals with specific values'")
arg!([positional3] ... "tests positionals with specific values")
.possible_values(POS3_VALS),
Arg::from("--multvals [one] [two] 'Tests multiple values, not mult occs'"),
Arg::from("--multvalsmo... [one] [two] 'Tests multiple values, not mult occs'"),
Arg::from("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2),
Arg::from("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3),
arg!(--multvals "Tests multiple values not mult occs").required(false).value_names(&["one", "two"]),
arg!(
--multvalsmo "Tests multiple values, not mult occs"
).multiple_values(true).required(false).value_names(&["one", "two"]),
arg!(--minvals2 <minvals> ... "Tests 2 min vals").min_values(2).multiple_values(true).required(false),
arg!(--maxvals3 <maxvals> ... "Tests 3 max vals").max_values(3).multiple_values(true).required(false),
])
.subcommand(
App::new("subcmd")
.about("tests subcommands")
.version("0.1")
.author("Kevin K. <kbknapp@gmail.com>")
.arg("-o --option [scoption]... 'tests options'")
.arg("[scpositional] 'tests positionals'"),
.arg(arg!(-o --option <scoption> ... "tests options").required(false))
.arg(arg!([scpositional] "tests positionals"))
)
}};
}
pub fn build_from_usage(c: &mut Criterion) {
c.bench_function("build_from_usage", |b| b.iter(|| create_app!()));
}
pub fn build_from_builder(c: &mut Criterion) {
c.bench_function("build_from_builder", |b| {
b.iter(|| {
@ -54,90 +54,90 @@ pub fn build_from_builder(c: &mut Criterion) {
.author("Kevin K. <kbknapp@gmail.com>")
.arg(
Arg::new("opt")
.about("tests options")
.help("tests options")
.short('o')
.long("option")
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::MultipleValues)
.setting(ArgSettings::MultipleOccurrences),
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true),
)
.arg(Arg::new("positional").about("tests positionals").index(1))
.arg(Arg::new("positional").help("tests positionals").index(1))
.arg(
Arg::new("flag")
.short('f')
.about("tests flags")
.help("tests flags")
.long("flag")
.global(true)
.setting(ArgSettings::MultipleOccurrences),
.multiple_occurrences(true),
)
.arg(
Arg::new("flag2")
.short('F')
.about("tests flags with exclusions")
.help("tests flags with exclusions")
.conflicts_with("flag")
.requires("option2"),
)
.arg(
Arg::new("option2")
.about("tests long options with exclusions")
.help("tests long options with exclusions")
.conflicts_with("option")
.requires("positional2")
.setting(ArgSettings::TakesValue)
.takes_value(true)
.long("long-option-2"),
)
.arg(
Arg::new("positional2")
.index(3)
.about("tests positionals with exclusions"),
.help("tests positionals with exclusions"),
)
.arg(
Arg::new("option3")
.short('O')
.long("Option")
.setting(ArgSettings::TakesValue)
.about("tests options with specific value sets")
.takes_value(true)
.help("tests options with specific value sets")
.possible_values(OPT3_VALS),
)
.arg(
Arg::new("positional3")
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::MultipleValues)
.setting(ArgSettings::MultipleOccurrences)
.about("tests positionals with specific values")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.help("tests positionals with specific values")
.index(4)
.possible_values(POS3_VALS),
)
.arg(
Arg::new("multvals")
.long("multvals")
.about("Tests multiple values, not mult occs")
.help("Tests multiple values, not mult occs")
.value_names(&["one", "two"]),
)
.arg(
Arg::new("multvalsmo")
.long("multvalsmo")
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::MultipleValues)
.setting(ArgSettings::MultipleOccurrences)
.about("Tests multiple values, not mult occs")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.help("Tests multiple values, not mult occs")
.value_names(&["one", "two"]),
)
.arg(
Arg::new("minvals")
.long("minvals2")
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::MultipleValues)
.setting(ArgSettings::MultipleOccurrences)
.about("Tests 2 min vals")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.help("Tests 2 min vals")
.min_values(2),
)
.arg(
Arg::new("maxvals")
.long("maxvals3")
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::MultipleValues)
.setting(ArgSettings::MultipleOccurrences)
.about("Tests 3 max vals")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.help("Tests 3 max vals")
.max_values(3),
)
.subcommand(
@ -149,12 +149,12 @@ pub fn build_from_builder(c: &mut Criterion) {
Arg::new("scoption")
.short('o')
.long("option")
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::MultipleValues)
.setting(ArgSettings::MultipleOccurrences)
.about("tests options"),
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.help("tests options"),
)
.arg(Arg::new("scpositional").index(1).about("tests positionals")),
.arg(Arg::new("scpositional").index(1).help("tests positionals")),
)
})
});
@ -289,7 +289,6 @@ pub fn parse_complex_with_sc_complex(c: &mut Criterion) {
criterion_group!(
benches,
build_from_usage,
build_from_builder,
parse_complex,
parse_complex_with_flag,

View file

@ -1,5 +1,5 @@
use clap::App;
use clap::{Arg, ArgSettings};
use clap::{arg, Arg};
use criterion::{criterion_group, criterion_main, Criterion};
use std::io::Cursor;
@ -15,13 +15,18 @@ fn app_example1<'c>() -> App<'c> {
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.arg("-c, --config=[FILE] 'Sets a custom config file'")
.arg("<output> 'Sets an optional output file'")
.arg("-d... 'Turn debugging information on'")
.arg(
arg!(
-c --config <FILE> "Sets a custom config file"
)
.required(false),
)
.arg(arg!(<output> "Sets an optional output file"))
.arg(arg!(d: -d ... "Turn debugging information on"))
.subcommand(
App::new("test")
.about("does testing things")
.arg("-l, --list 'lists test values'"),
.arg(arg!(-l --list "lists test values")),
)
}
@ -36,23 +41,27 @@ fn app_example3<'c>() -> App<'c> {
App::new("MyApp")
.arg(
Arg::new("debug")
.about("turn on debugging information")
.help("turn on debugging information")
.short('d'),
)
.args(&[
Arg::new("config")
.about("sets the config file to use")
.setting(ArgSettings::TakesValue)
.help("sets the config file to use")
.takes_value(true)
.short('c')
.long("config"),
Arg::new("input")
.about("the input file to use")
.index(1)
.setting(ArgSettings::Required),
.help("the input file to use")
.required(true),
])
.arg("--license 'display the license file'")
.arg("[output] 'Supply an output file to use'")
.arg("-i, --int=[IFACE] 'Set an interface to use'")
.arg(arg!(--license "display the license file"))
.arg(arg!([output] "Supply an output file to use"))
.arg(
arg!(
-i --int <IFACE> "Set an interface to use"
)
.required(false),
)
}
fn app_example4<'c>() -> App<'c> {
@ -62,33 +71,31 @@ fn app_example4<'c>() -> App<'c> {
.author("Kevin K. <kbknapp@gmail.com>")
.arg(
Arg::new("debug")
.about("turn on debugging information")
.help("turn on debugging information")
.short('d')
.long("debug"),
)
.arg(
Arg::new("config")
.about("sets the config file to use")
.help("sets the config file to use")
.short('c')
.long("config"),
)
.arg(
Arg::new("input")
.about("the input file to use")
.help("the input file to use")
.index(1)
.setting(ArgSettings::Required),
.required(true),
)
}
fn app_example5<'c>() -> App<'c> {
App::new("MyApp").arg(
Arg::new("awesome")
.about("turns up the awesome")
.help("turns up the awesome")
.short('a')
.long("awesome")
.setting(ArgSettings::MultipleOccurrences)
.requires("config")
.conflicts_with("output"),
.multiple_occurrences(true),
)
}
@ -96,13 +103,12 @@ fn app_example6<'c>() -> App<'c> {
App::new("MyApp")
.arg(
Arg::new("input")
.about("the input file to use")
.help("the input file to use")
.index(1)
.requires("config")
.conflicts_with("output")
.setting(ArgSettings::Required),
.required(true),
)
.arg(Arg::new("config").about("the config file to use").index(2))
.arg(Arg::new("config").help("the config file to use").index(2))
}
fn app_example7<'c>() -> App<'c> {
@ -111,11 +117,11 @@ fn app_example7<'c>() -> App<'c> {
.arg(Arg::new("output"))
.arg(
Arg::new("input")
.about("the input file to use")
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::MultipleValues)
.setting(ArgSettings::MultipleOccurrences)
.setting(ArgSettings::Required)
.help("the input file to use")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.required(true)
.short('i')
.long("input")
.requires("config")
@ -129,11 +135,11 @@ fn app_example8<'c>() -> App<'c> {
.arg(Arg::new("output"))
.arg(
Arg::new("input")
.about("the input file to use")
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::MultipleValues)
.setting(ArgSettings::MultipleOccurrences)
.setting(ArgSettings::Required)
.help("the input file to use")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.required(true)
.short('i')
.long("input")
.requires("config")
@ -144,9 +150,9 @@ fn app_example8<'c>() -> App<'c> {
fn app_example10<'c>() -> App<'c> {
App::new("myapp").about("does awesome things").arg(
Arg::new("CONFIG")
.about("The config file to use (default is \"config.json\")")
.help("The config file to use (default is \"config.json\")")
.short('c')
.setting(ArgSettings::TakesValue),
.takes_value(true),
)
}

View file

@ -3,7 +3,7 @@
//
// CLI used is adapted from ripgrep 48a8a3a691220f9e5b2b08f4051abe8655ea7e8a
use clap::{App, Arg, ArgSettings};
use clap::{App, Arg};
use criterion::{criterion_group, criterion_main, Criterion};
use std::collections::HashMap;
use std::io::Cursor;
@ -298,7 +298,7 @@ fn app<F>(_next_line_help: bool, doc: F) -> App<'static>
where
F: Fn(&'static str) -> &'static str,
{
let arg = |name| Arg::new(name).about(doc(name));
let arg = |name| Arg::new(name).help(doc(name));
let flag = |name| arg(name).long(name);
App::new("ripgrep")
@ -325,16 +325,16 @@ where
]))
.arg(
arg("path")
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::MultipleValues)
.setting(ArgSettings::MultipleOccurrences),
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true),
)
.arg(
flag("regexp")
.short('e')
.setting(ArgSettings::AllowHyphenValues)
.setting(ArgSettings::MultipleOccurrences)
.setting(ArgSettings::TakesValue)
.allow_hyphen_values(true)
.multiple_occurrences(true)
.takes_value(true)
.value_name("pattern"),
)
.arg(
@ -350,22 +350,22 @@ where
.arg(
flag("color")
.value_name("WHEN")
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::HidePossibleValues)
.takes_value(true)
.hide_possible_values(true)
.possible_values(["never", "auto", "always", "ansi"]),
)
.arg(
flag("colors")
.value_name("SPEC")
.setting(ArgSettings::MultipleOccurrences)
.setting(ArgSettings::TakesValue),
.multiple_occurrences(true)
.takes_value(true),
)
.arg(flag("fixed-strings").short('F'))
.arg(
flag("glob")
.short('g')
.setting(ArgSettings::MultipleOccurrences)
.setting(ArgSettings::TakesValue)
.multiple_occurrences(true)
.takes_value(true)
.value_name("GLOB"),
)
.arg(flag("ignore-case").short('i'))
@ -375,22 +375,18 @@ where
.arg(
flag("type")
.short('t')
.setting(ArgSettings::MultipleOccurrences)
.setting(ArgSettings::TakesValue)
.multiple_occurrences(true)
.takes_value(true)
.value_name("TYPE"),
)
.arg(
flag("type-not")
.short('T')
.setting(ArgSettings::MultipleOccurrences)
.setting(ArgSettings::TakesValue)
.multiple_occurrences(true)
.takes_value(true)
.value_name("TYPE"),
)
.arg(
flag("unrestricted")
.short('u')
.setting(ArgSettings::MultipleOccurrences),
)
.arg(flag("unrestricted").short('u').multiple_occurrences(true))
.arg(flag("invert-match").short('v'))
.arg(flag("word-regexp").short('w'))
// Third, set up less common flags.
@ -419,7 +415,7 @@ where
flag("file")
.short('f')
.value_name("FILE")
.setting(ArgSettings::MultipleOccurrences),
.multiple_occurrences(true),
)
.arg(flag("files-with-matches").short('l'))
.arg(flag("files-without-match"))
@ -431,7 +427,7 @@ where
.arg(
flag("ignore-file")
.value_name("FILE")
.setting(ArgSettings::MultipleOccurrences),
.multiple_occurrences(true),
)
.arg(flag("follow").short('L'))
.arg(
@ -468,12 +464,12 @@ where
.arg(
flag("type-add")
.value_name("TYPE")
.setting(ArgSettings::MultipleOccurrences),
.multiple_occurrences(true),
)
.arg(
flag("type-clear")
.value_name("TYPE")
.setting(ArgSettings::MultipleOccurrences),
.multiple_occurrences(true),
)
}

View file

@ -2,7 +2,7 @@
//
// CLI used is from rustup 408ed84f0e50511ed44a405dd91365e5da588790
use clap::{App, AppSettings, Arg, ArgGroup, ArgSettings};
use clap::{App, AppSettings, Arg, ArgGroup};
use criterion::{criterion_group, criterion_main, Criterion};
pub fn build_rustup(c: &mut Criterion) {
@ -30,7 +30,7 @@ fn build_cli() -> App<'static> {
// .setting(AppSettings::SubcommandRequiredElseHelp)
.arg(
Arg::new("verbose")
.about("Enable verbose output")
.help("Enable verbose output")
.short('v')
.long("verbose"),
)
@ -44,25 +44,25 @@ fn build_cli() -> App<'static> {
.about("Update Rust toolchains")
.after_help(TOOLCHAIN_INSTALL_HELP)
.setting(AppSettings::Hidden) // synonym for 'toolchain install'
.arg(Arg::new("toolchain").setting(ArgSettings::Required)),
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("update")
.about("Update Rust toolchains")
.after_help(UPDATE_HELP)
.arg(Arg::new("toolchain").setting(ArgSettings::Required))
.arg(Arg::new("toolchain").required(true))
.arg(
Arg::new("no-self-update")
.about("Don't perform self update when running the `rustup` command")
.help("Don't perform self update when running the `rustup` command")
.long("no-self-update")
.setting(ArgSettings::Hidden),
.hide(true),
),
)
.subcommand(
App::new("default")
.about("Set the default toolchain")
.after_help(DEFAULT_HELP)
.arg(Arg::new("toolchain").setting(ArgSettings::Required)),
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("toolchain")
@ -74,33 +74,33 @@ fn build_cli() -> App<'static> {
.subcommand(
App::new("install")
.about("Install or update a given toolchain")
.arg(Arg::new("toolchain").setting(ArgSettings::Required)),
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("uninstall")
.about("Uninstall a toolchain")
.arg(Arg::new("toolchain").setting(ArgSettings::Required)),
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("link")
.about("Create a custom toolchain by symlinking to a directory")
.arg(Arg::new("toolchain").setting(ArgSettings::Required))
.arg(Arg::new("path").setting(ArgSettings::Required)),
.arg(Arg::new("toolchain").required(true))
.arg(Arg::new("path").required(true)),
)
.subcommand(
App::new("update")
.setting(AppSettings::Hidden) // synonym for 'install'
.arg(Arg::new("toolchain").setting(ArgSettings::Required)),
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("add")
.setting(AppSettings::Hidden) // synonym for 'install'
.arg(Arg::new("toolchain").setting(ArgSettings::Required)),
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("remove")
.setting(AppSettings::Hidden) // synonym for 'uninstall'
.arg(Arg::new("toolchain").setting(ArgSettings::Required)),
.arg(Arg::new("toolchain").required(true)),
),
)
.subcommand(
@ -111,51 +111,31 @@ fn build_cli() -> App<'static> {
.subcommand(
App::new("list")
.about("List installed and available targets")
.arg(
Arg::new("toolchain")
.long("toolchain")
.setting(ArgSettings::TakesValue),
),
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
)
.subcommand(
App::new("add")
.about("Add a target to a Rust toolchain")
.arg(Arg::new("target").setting(ArgSettings::Required))
.arg(
Arg::new("toolchain")
.long("toolchain")
.setting(ArgSettings::TakesValue),
),
.arg(Arg::new("target").required(true))
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
)
.subcommand(
App::new("remove")
.about("Remove a target from a Rust toolchain")
.arg(Arg::new("target").setting(ArgSettings::Required))
.arg(
Arg::new("toolchain")
.long("toolchain")
.setting(ArgSettings::TakesValue),
),
.arg(Arg::new("target").required(true))
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
)
.subcommand(
App::new("install")
.setting(AppSettings::Hidden) // synonym for 'add'
.arg(Arg::new("target").setting(ArgSettings::Required))
.arg(
Arg::new("toolchain")
.long("toolchain")
.setting(ArgSettings::TakesValue),
),
.arg(Arg::new("target").required(true))
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
)
.subcommand(
App::new("uninstall")
.setting(AppSettings::Hidden) // synonym for 'remove'
.arg(Arg::new("target").setting(ArgSettings::Required))
.arg(
Arg::new("toolchain")
.long("toolchain")
.setting(ArgSettings::TakesValue),
),
.arg(Arg::new("target").required(true))
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
),
)
.subcommand(
@ -166,41 +146,21 @@ fn build_cli() -> App<'static> {
.subcommand(
App::new("list")
.about("List installed and available components")
.arg(
Arg::new("toolchain")
.long("toolchain")
.setting(ArgSettings::TakesValue),
),
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
)
.subcommand(
App::new("add")
.about("Add a component to a Rust toolchain")
.arg(Arg::new("component").setting(ArgSettings::Required))
.arg(
Arg::new("toolchain")
.long("toolchain")
.setting(ArgSettings::TakesValue),
)
.arg(
Arg::new("target")
.long("target")
.setting(ArgSettings::TakesValue),
),
.arg(Arg::new("component").required(true))
.arg(Arg::new("toolchain").long("toolchain").takes_value(true))
.arg(Arg::new("target").long("target").takes_value(true)),
)
.subcommand(
App::new("remove")
.about("Remove a component from a Rust toolchain")
.arg(Arg::new("component").setting(ArgSettings::Required))
.arg(
Arg::new("toolchain")
.long("toolchain")
.setting(ArgSettings::TakesValue),
)
.arg(
Arg::new("target")
.long("target")
.setting(ArgSettings::TakesValue),
),
.arg(Arg::new("component").required(true))
.arg(Arg::new("toolchain").long("toolchain").takes_value(true))
.arg(Arg::new("target").long("target").takes_value(true)),
),
)
.subcommand(
@ -213,7 +173,7 @@ fn build_cli() -> App<'static> {
.subcommand(
App::new("set")
.about("Set the override toolchain for a directory")
.arg(Arg::new("toolchain").setting(ArgSettings::Required)),
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("unset")
@ -222,33 +182,29 @@ fn build_cli() -> App<'static> {
.arg(
Arg::new("path")
.long("path")
.setting(ArgSettings::TakesValue)
.about("Path to the directory"),
.takes_value(true)
.help("Path to the directory"),
)
.arg(
Arg::new("nonexistent")
.long("nonexistent")
.about("Remove override toolchain for all nonexistent directories"),
.help("Remove override toolchain for all nonexistent directories"),
),
)
.subcommand(
App::new("add")
.setting(AppSettings::Hidden) // synonym for 'set'
.arg(Arg::new("toolchain").setting(ArgSettings::Required)),
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("remove")
.setting(AppSettings::Hidden) // synonym for 'unset'
.about("Remove the override toolchain for a directory")
.arg(
Arg::new("path")
.long("path")
.setting(ArgSettings::TakesValue),
)
.arg(Arg::new("path").long("path").takes_value(true))
.arg(
Arg::new("nonexistent")
.long("nonexistent")
.about("Remove override toolchain for all nonexistent directories"),
.help("Remove override toolchain for all nonexistent directories"),
),
),
)
@ -257,19 +213,19 @@ fn build_cli() -> App<'static> {
.about("Run a command with an environment configured for a given toolchain")
.after_help(RUN_HELP)
.setting(AppSettings::TrailingVarArg)
.arg(Arg::new("toolchain").setting(ArgSettings::Required))
.arg(Arg::new("toolchain").required(true))
.arg(
Arg::new("command")
.setting(ArgSettings::Required)
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::MultipleValues)
.setting(ArgSettings::MultipleOccurrences),
.required(true)
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true),
),
)
.subcommand(
App::new("which")
.about("Display which binary will be run for a given command")
.arg(Arg::new("command").setting(ArgSettings::Required)),
.arg(Arg::new("command").required(true)),
)
.subcommand(
App::new("doc")
@ -278,24 +234,20 @@ fn build_cli() -> App<'static> {
.arg(
Arg::new("book")
.long("book")
.about("The Rust Programming Language book"),
.help("The Rust Programming Language book"),
)
.arg(
Arg::new("std")
.long("std")
.about("Standard library API documentation"),
.help("Standard library API documentation"),
)
.group(ArgGroup::new("page").args(&["book", "std"])),
)
.subcommand(
App::new("man")
.about("View the man page for a given command")
.arg(Arg::new("command").setting(ArgSettings::Required))
.arg(
Arg::new("toolchain")
.long("toolchain")
.setting(ArgSettings::TakesValue),
),
.arg(Arg::new("command").required(true))
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
)
.subcommand(
App::new("self")
@ -322,7 +274,7 @@ fn build_cli() -> App<'static> {
App::new("set").about("Alter rustup settings").subcommand(
App::new("default-host")
.about("The triple used to identify toolchains when not specified")
.arg(Arg::new("host_triple").setting(ArgSettings::Required)),
.arg(Arg::new("host_triple").required(true)),
),
)
}

View file

@ -1,11 +0,0 @@
status = [
"CI",
]
pr_status = [
"CI-PR", "Lint",
]
timeout_sec = 7200
prerun_timeout_sec = 7200
delete_merged_branches = true
cut_body_after = ""
block_labels = ["M: blocked", "M: require changes"]

View file

@ -2,10 +2,6 @@
name = "clap_derive"
version = "3.0.0-beta.5"
edition = "2018"
authors = [
"Guillaume Pinot <texitoi@texitoi.eu>",
"Clap Maintainers"
]
include = [
"src/**/*",
"Cargo.toml",
@ -15,7 +11,6 @@ include = [
description = "Parse command line argument by defining a struct, derive crate."
repository = "https://github.com/clap-rs/clap/tree/master/clap_derive"
documentation = "https://docs.rs/clap_derive"
homepage = "https://clap.rs/"
keywords = [
"clap",
"cli",
@ -38,12 +33,6 @@ proc-macro2 = "1.0.28"
heck = "0.3.0"
proc-macro-error = "1"
[dev-dependencies]
clap = { path = "../", default-features = false, features = ["std", "derive", "env"] }
trybuild = "1.0"
rustversion = "1"
version-sync = "0.9"
[features]
default = []
debug = []

View file

@ -1,147 +1,6 @@
# clap_derive
Parse command line argument by defining a struct. It combines [structopt](https://github.com/TeXitoi/structopt) and [clap](https://crates.io/crates/clap) into a single experience. This crate is used by clap, and not meant to be used directly by
consumers.
## Documentation
Find it on [Docs.rs](https://docs.rs/clap_derive). You can also check the [examples](https://github.com/clap-rs/clap/tree/master/clap_derive/examples) and the [changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md).
## Example
Add `clap` to your dependencies of your `Cargo.toml`:
```toml
[dependencies]
clap = "3"
```
And then, in your rust file:
```rust,no_run
use std::path::PathBuf;
use clap::{Parser, ValueHint};
/// A basic example
#[derive(Parser, Debug)]
#[clap(name = "basic")]
struct Opt {
// A flag, true if used in the command line. Note doc comment will
// be used for the help message of the flag. The name of the
// argument will be, by default, based on the name of the field.
/// Activate debug mode
#[clap(short, long)]
debug: bool,
// The number of occurrences of the `v/verbose` flag
/// Verbose mode (-v, -vv, -vvv, etc.)
#[clap(short, long, parse(from_occurrences))]
verbose: u8,
/// Set speed
#[clap(short, long, default_value = "42")]
speed: f64,
/// Output file
#[clap(short, long, parse(from_os_str), value_hint = ValueHint::FilePath)]
output: PathBuf,
// the long option will be translated by default to kebab case,
// i.e. `--nb-cars`.
/// Number of cars
#[clap(short = 'c', long)]
nb_cars: Option<i32>,
/// admin_level to consider
#[clap(short, long)]
level: Vec<String>,
/// Files to process
#[clap(name = "FILE", parse(from_os_str), value_hint = ValueHint::AnyPath)]
files: Vec<PathBuf>,
}
fn main() {
let opt = Opt::parse();
println!("{:#?}", opt);
}
```
Using this example:
```bash
$ ./basic
error: The following required arguments were not provided:
--output <output>
USAGE:
basic --output <output> --speed <speed>
For more information try --help
$ ./basic --help
basic 0.3.0
Guillaume Pinot <texitoi@texitoi.eu>, others
A basic example
USAGE:
basic [OPTIONS] --output <output> [--] [file]...
ARGS:
<FILE>... Files to process
OPTIONS:
-c, --nb-cars <nb-cars> Number of cars
-d, --debug Activate debug mode
-h, --help Print help information
-l, --level <level>... admin_level to consider
-o, --output <output> Output file
-s, --speed <speed> Set speed [default: 42]
-V, --version Print version information
-v, --verbose Verbose mode (-v, -vv, -vvv, etc.)
ARGS:
<file>... Files to process
$ ./basic -o foo.txt
Opt {
debug: false,
verbose: 0,
speed: 42.0,
output: "foo.txt",
nb_cars: None,
level: [],
files: [],
}
$ ./basic -o foo.txt -dvvvs 1337 -l alice -l bob --nb-cars 4 bar.txt baz.txt
Opt {
debug: true,
verbose: 3,
speed: 1337.0,
output: "foo.txt",
nb_cars: Some(
4,
),
level: [
"alice",
"bob",
],
files: [
"bar.txt",
"baz.txt",
],
}
```
## clap_derive rustc version policy
- Minimum rustc version modification must be specified in the [changelog](https://github.com/clap-rs/clap_derive/blob/master/CHANGELOG.md) and in the [travis configuration](https://github.com/clap-rs/clap_derive/blob/master/.travis.yaml).
- Contributors can increment minimum rustc version without any justification if the new version is required by the latest version of one of clap_derive's dependencies (`cargo update` will not fail on clap_derive).
- Contributors can increment minimum rustc version if the library user experience is improved.
## Why
I've (@TeXitoi) used [docopt](https://crates.io/crates/docopt) for a long time (pre rust 1.0). I really like the fact that you have a structure with the parsed argument: no need to convert `String` to `f64`, no useless `unwrap`. But on the other hand, I don't like to write by hand the usage string. That's like going back to the golden age of WYSIWYG editors. Field naming is also a bit artificial.
Today, the new standard to read command line arguments in Rust is [clap](https://crates.io/crates/clap). This library is so feature full! But I think there is one downside: even if you can validate argument and expressing that an argument is required, you still need to transform something looking like a hashmap of string vectors to something useful for your application.
Now, there is stable custom derive. Thus I can add to clap the automatic conversion that I miss. Here is the result.
Macro implementation for clap's derives.
## License

View file

@ -1,82 +0,0 @@
# Collection of examples "how to use `clap_derive`"
### [Help on the bottom](after_help.rs)
How to append a postscript to the help message generated.
### [Arg Enum](arg_enum.rs)
How to use `ArgEnum`
### [At least N](at_least_two.rs)
How to require presence of at least N values, like `val1 val2 ... valN ... valM`.
### [Basic](basic.rs)
A basic example how to use `clap_derive`.
### [Deny missing docs](deny_missing_docs.rs)
**This is not an example but a test**, it should be moved to `tests` folder
as soon as [this](https://github.com/rust-lang/rust/issues/24584) is fixed (if ever).
### [Doc comments](doc_comments.rs)
How to use doc comments in place of `help/long_help`.
### [Arguments of subcommands in separate `struct`](enum_tuple.rs)
How to extract subcommands' args into external structs.
### [Environment variables](env.rs)
How to use environment variable fallback an how it interacts with `default_value`.
### [Advanced](example.rs)
Somewhat complex example of usage of `clap_derive`.
### [Flatten](flatten.rs)
How to use `#[clap(flatten)]`
### [Git](git.rs)
Pseudo-`git` example, shows how to use subcommands and how to document them.
### [Groups](group.rs)
Using `clap::Arg::group` with `clap`.
### [`key=value` pairs](keyvalue.rs)
How to parse `key=value` pairs.
### [`--no-*` flags](negative_flag.rs)
How to add `no-thing` flag which is `true` by default and `false` if passed.
### [Rename all](rename_all.rs)
How `#[clap(rename_all)]` works.
### [Skip](skip.rs)
How to use `#[clap(skip)]`.
### [Aliases](subcommand_aliases.rs)
How to use aliases
### [`true` or `false`](true_or_false.rs)
How to express "`"true"` or `"false"` argument.
### [Author, description, and version from `Cargo.toml`](from_crate.rs)
How to derive the author, description, and version from Cargo.toml
### [Value hints for shell completion](value_hints_derive.rs)
How to provide `ValueHint` attributes and generate shell completion files.

View file

@ -1,19 +0,0 @@
//! How to append a postscript to the help message generated.
use clap::Parser;
/// I am a program and I do things.
///
/// Sometimes they even work.
#[derive(Parser, Debug)]
#[clap(after_help = "Beware `-d`, dragons be here")]
struct Opt {
/// Release the dragon.
#[clap(short)]
dragon: bool,
}
fn main() {
let opt = Opt::parse();
println!("{:?}", opt);
}

View file

@ -1,31 +0,0 @@
//! Usage example of `arg_enum`
//!
//! All the variants of the enum and the enum itself support `rename_all`
use clap::{ArgEnum, Parser};
#[derive(ArgEnum, Debug, PartialEq, Clone)]
enum ArgChoice {
/// Descriptions are supported as doc-comment
Foo,
// Renames are supported
#[clap(name = "b-a-r")]
Bar,
// Aliases are supported
#[clap(alias = "b", alias = "z")]
Baz,
// Hiding variants from help and completion is supported
#[clap(hidden = true)]
Hidden,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(arg_enum)]
arg: ArgChoice,
}
fn main() {
let opt = Opt::parse();
println!("{:#?}", opt);
}

View file

@ -1,15 +0,0 @@
//! How to require presence of at least N values,
//! like `val1 val2 ... valN ... valM`.
use clap::Parser;
#[derive(Parser, Debug)]
struct Opt {
#[clap(required = true, min_values = 2)]
foos: Vec<String>,
}
fn main() {
let opt = Opt::parse();
println!("{:?}", opt);
}

View file

@ -1,48 +0,0 @@
//! A somewhat comprehensive example of a typical `clap_derive` usage.
use clap::{Parser, ValueHint};
use std::path::PathBuf;
/// A basic example
#[derive(Parser, Debug)]
#[clap(name = "basic")]
struct Opt {
// A flag, true if used in the command line. Note doc comment will
// be used for the help message of the flag. The name of the
// argument will be, by default, based on the name of the field.
/// Activate debug mode
#[clap(short, long)]
debug: bool,
// The number of occurrences of the `v/verbose` flag
/// Verbose mode (-v, -vv, -vvv, etc.)
#[clap(short, long, parse(from_occurrences))]
verbose: u8,
/// Set speed
#[clap(short, long, default_value = "42")]
speed: f64,
/// Output file
#[clap(short, long, parse(from_os_str), value_hint = ValueHint::FilePath)]
output: PathBuf,
// the long option will be translated by default to kebab case,
// i.e. `--nb-cars`.
/// Number of cars
#[clap(short = 'c', long)]
nb_cars: Option<i32>,
/// admin_level to consider
#[clap(short, long)]
level: Vec<String>,
/// Files to process
#[clap(name = "FILE", parse(from_os_str), value_hint = ValueHint::AnyPath)]
files: Vec<PathBuf>,
}
fn main() {
let opt = Opt::parse();
println!("{:#?}", opt);
}

View file

@ -1,54 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
//! A test to check that clap_derive compiles with deny(missing_docs)
#![deny(missing_docs)]
use clap::{Args, Parser, Subcommand};
/// The options
#[derive(Parser, Debug, PartialEq)]
pub struct Opt {
#[clap(short)]
verbose: bool,
#[clap(subcommand)]
cmd: Option<Cmd>,
}
/// Some subcommands
#[derive(Subcommand, Debug, PartialEq)]
pub enum Cmd {
/// command A
A,
/// command B
B {
/// Alice?
#[clap(short)]
alice: bool,
},
/// command C
C(COpt),
}
/// The options for C
#[derive(Args, Debug, PartialEq)]
pub struct COpt {
#[clap(short)]
bob: bool,
}
fn main() {
println!("{:?}", Opt::parse());
}

View file

@ -1,74 +0,0 @@
//! How to use doc comments in place of `help/long_help`.
use clap::{Parser, Subcommand};
/// A basic example for the usage of doc comments as replacement
/// of the arguments `help`, `long_help`, `about` and `long_about`.
#[derive(Parser, Debug)]
#[clap(name = "basic")]
struct Opt {
/// Just use doc comments to replace `help`, `long_help`,
/// `about` or `long_about` input.
#[clap(short, long)]
first_flag: bool,
/// Split between `help` and `long_help`.
///
/// In the previous case clap is going to present
/// the whole comment both as text for the `help` and the
/// `long_help` argument.
///
/// But if the doc comment is formatted like this example
/// -- with an empty second line splitting the heading and
/// the rest of the comment -- only the first line is used
/// as `help` argument. The `long_help` argument will still
/// contain the whole comment.
///
/// ## Attention
///
/// Any formatting next to empty lines that could be used
/// inside a doc comment is currently not preserved. If
/// lists or other well formatted content is required it is
/// necessary to use the related clap argument with a
/// raw string as shown on the `third_flag` description.
#[clap(short, long)]
second_flag: bool,
#[clap(
short,
long,
long_about = r"This is a raw string.
It can be used to pass well formatted content (e.g. lists or source
code) in the description:
- first example list entry
- second example list entry
"
)]
third_flag: bool,
#[clap(subcommand)]
sub_command: SubCommand,
}
#[derive(Subcommand, Debug)]
#[clap()]
enum SubCommand {
/// The same rules described previously for flags. Are
/// also true for in regards of sub-commands.
First,
/// Applicable for both `about` an `help`.
///
/// The formatting rules described in the comment of the
/// `second_flag` also apply to the description of
/// sub-commands which is normally given through the `about`
/// and `long_about` arguments.
Second,
}
fn main() {
let opt = Opt::parse();
println!("{:?}", opt);
}

View file

@ -1,26 +0,0 @@
//! How to extract subcommands' args into external structs.
use clap::{Args, Parser, Subcommand};
#[derive(Debug, Args)]
pub struct Foo {
pub bar: Option<String>,
}
#[derive(Debug, Subcommand)]
pub enum Command {
#[clap(name = "foo")]
Foo(Foo),
}
#[derive(Debug, Parser)]
#[clap(name = "classify")]
pub struct ApplicationArguments {
#[clap(subcommand)]
pub command: Command,
}
fn main() {
let opt = ApplicationArguments::parse();
println!("{:?}", opt);
}

View file

@ -1,32 +0,0 @@
//! How to use environment variable fallback an how it
//! interacts with `default_value`.
use clap::{ArgSettings, Parser};
/// Example for allowing to specify options via environment variables.
#[derive(Parser, Debug)]
#[clap(name = "env")]
struct Opt {
// Use `env` to enable specifying the option with an environment
// variable. Command line arguments take precedence over env.
/// URL for the API server
#[clap(long, env = "API_URL")]
api_url: String,
// The default value is used if neither argument nor environment
// variable is specified.
/// Number of retries
#[clap(long, env = "RETRIES", default_value = "5")]
retries: u32,
// If an environment variable contains a sensitive value, it can be hidden
// from the help screen with a special setting.
/// Secret token
#[clap(long, env = "SECRET_TOKEN", setting = ArgSettings::HideEnvValues)]
token: String,
}
fn main() {
let opt = Opt::parse();
println!("{:#?}", opt);
}

View file

@ -1,54 +0,0 @@
//! Somewhat complex example of usage of #[derive(Parser)].
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(name = "example")]
/// An example of clap_derive usage.
struct Opt {
// A flag, true if used in the command line.
#[clap(short, long)]
/// Activate debug mode
debug: bool,
// An argument of type float, with a default value.
#[clap(short, long, default_value = "42")]
/// Set speed
speed: f64,
// Needed parameter, the first on the command line.
/// Input file
input: String,
// An optional parameter, will be `None` if not present on the
// command line.
/// Output file, stdout if not present
output: Option<String>,
// An optional parameter with optional value, will be `None` if
// not present on the command line, will be `Some(None)` if no
// argument is provided (i.e. `--log`) and will be
// `Some(Some(String))` if argument is provided (e.g. `--log
// log.txt`).
#[clap(long)]
#[allow(clippy::option_option)]
/// Log file, stdout if no file, no logging if not present
log: Option<Option<String>>,
// An optional list of values, will be `None` if not present on
// the command line, will be `Some(vec![])` if no argument is
// provided (i.e. `--optv`) and will be `Some(Vec<String>)` if
// argument list is provided (e.g. `--optv a b c`).
#[clap(long)]
optv: Option<Vec<String>>,
// Skipped option: it won't be parsed and will be filled with the
// default value for its type (in this case it'll be an empty string).
#[clap(skip)]
skipped: String,
}
fn main() {
let opt = Opt::parse();
println!("{:?}", opt.skipped);
}

View file

@ -1,29 +0,0 @@
//! How to use flattening.
use clap::{Args, Parser};
#[derive(Parser, Debug)]
struct Cmdline {
/// switch verbosity on
#[clap(short)]
verbose: bool,
#[clap(flatten)]
daemon_opts: DaemonOpts,
}
#[derive(Args, Debug)]
struct DaemonOpts {
/// daemon user
#[clap(short)]
user: String,
/// daemon group
#[clap(short)]
group: String,
}
fn main() {
let opt = Cmdline::parse();
println!("{:?}", opt);
}

View file

@ -1,15 +0,0 @@
//! How to derive the author, description, and version from Cargo.toml
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(author, about, version)]
// ^^^^^^ <- derive author from Cargo.toml
// ^^^^^ <- derive description from Cargo.toml
// ^^^^^^^ <- derive version from Cargo.toml
struct Opt {}
fn main() {
let opt = Opt::parse();
println!("{:?}", opt);
}

View file

@ -1,35 +0,0 @@
//! `git.rs` serves as a demonstration of how to use subcommands,
//! as well as a demonstration of adding documentation to subcommands.
//! Documentation can be added either through doc comments or
//! `help`/`about` attributes.
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(name = "git")]
/// the stupid content tracker
enum Opt {
/// fetch branches from remote repository
Fetch {
#[clap(long)]
dry_run: bool,
#[clap(long)]
all: bool,
#[clap(default_value = "origin")]
repository: String,
},
#[clap(override_help = "add files to the staging area")]
Add {
#[clap(short)]
interactive: bool,
#[clap(short)]
all: bool,
files: Vec<String>,
},
}
fn main() {
let matches = Opt::parse();
println!("{:?}", matches);
}

View file

@ -1,31 +0,0 @@
//! How to use `clap::Arg::group`
use clap::{ArgGroup, Parser};
#[derive(Parser, Debug)]
#[clap(group = ArgGroup::new("verb").required(true))]
struct Opt {
/// Set a custom HTTP verb
#[clap(long, group = "verb")]
method: Option<String>,
/// HTTP GET
#[clap(long, group = "verb")]
get: bool,
/// HTTP HEAD
#[clap(long, group = "verb")]
head: bool,
/// HTTP POST
#[clap(long, group = "verb")]
post: bool,
/// HTTP PUT
#[clap(long, group = "verb")]
put: bool,
/// HTTP DELETE
#[clap(long, group = "verb")]
delete: bool,
}
fn main() {
let opt = Opt::parse();
println!("{:?}", opt);
}

View file

@ -1,36 +0,0 @@
//! How to parse "key=value" pairs with #[derive(Parser)].
use clap::Parser;
use std::error::Error;
/// Parse a single key-value pair
fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
where
T: std::str::FromStr,
T::Err: Error + Send + Sync + 'static,
U: std::str::FromStr,
U::Err: Error + Send + Sync + 'static,
{
let pos = s
.find('=')
.ok_or_else(|| format!("invalid KEY=value: no `=` found in `{}`", s))?;
Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
}
#[derive(Parser, Debug)]
struct Opt {
// number_of_values = 1 forces the user to repeat the -D option for each key-value pair:
// my_program -D a=1 -D b=2
// Without number_of_values = 1 you can do:
// my_program -D a=1 b=2
// but this makes adding an argument after the values impossible:
// my_program -D a=1 -D b=2 my_input_file
// becomes invalid.
#[clap(short = 'D', parse(try_from_str = parse_key_val), multiple_occurrences(true), number_of_values = 1)]
defines: Vec<(String, i32)>,
}
fn main() {
let opt = Opt::parse();
println!("{:?}", opt);
}

View file

@ -1,15 +0,0 @@
//! How to add `no-thing` flag which is `true` by default and
//! `false` if passed.
use clap::Parser;
#[derive(Debug, Parser)]
struct Opt {
#[clap(long = "no-verbose", parse(from_flag = std::ops::Not::not))]
verbose: bool,
}
fn main() {
let cmd = Opt::parse();
println!("{:#?}", cmd);
}

View file

@ -1,74 +0,0 @@
//! Example on how the `rename_all` parameter works.
//!
//! `rename_all` can be used to override the casing style used during argument
//! generation. By default the `kebab-case` style will be used but there are a wide
//! variety of other styles available.
//!
//! ## Supported styles overview:
//!
//! - **Camel Case**: Indicate word boundaries with uppercase letter, excluding
//! the first word.
//! - **Kebab Case**: Keep all letters lowercase and indicate word boundaries
//! with hyphens.
//! - **Pascal Case**: Indicate word boundaries with uppercase letter,
//! including the first word.
//! - **Screaming Snake Case**: Keep all letters uppercase and indicate word
//! boundaries with underscores.
//! - **Snake Case**: Keep all letters lowercase and indicate word boundaries
//! with underscores.
//! - **Verbatim**: Use the original attribute name defined in the code.
use clap::{Args, Parser, Subcommand};
#[derive(Parser, Debug)]
#[clap(name = "rename_all", rename_all = "screaming_snake_case")]
enum Opt {
// This subcommand will be named `FIRST_COMMAND`. As the command doesn't
// override the initial casing style, ...
/// A screaming loud first command. Only use if necessary.
FirstCommand {
// this flag will be available as `--FOO` and `-F`.
/// This flag will even scream louder.
#[clap(long, short)]
foo: bool,
},
// As we override the casing style for this variant the related subcommand
// will be named `SecondCommand`.
/// Not nearly as loud as the first command.
#[clap(rename_all = "pascal_case")]
SecondCommand {
// We can also override it again on a single field.
/// Nice quiet flag. No one is annoyed.
#[clap(rename_all = "snake_case", long)]
bar_option: bool,
// Renaming will not be propagated into subcommand flagged enums. If
// a non default casing style is required it must be defined on the
// enum itself.
#[clap(subcommand)]
cmds: Subcommands,
// or flattened structs.
#[clap(flatten)]
options: BonusOptions,
},
}
#[derive(Subcommand, Debug)]
enum Subcommands {
// This one will be available as `first-subcommand`.
FirstSubcommand,
}
#[derive(Args, Debug)]
struct BonusOptions {
// And this one will be available as `baz-option`.
#[clap(long)]
baz_option: bool,
}
fn main() {
let opt = Opt::parse();
println!("{:?}", opt);
}

View file

@ -1,47 +0,0 @@
//! How to use `#[clap(skip)]`
use clap::Parser;
#[derive(Parser, Debug, PartialEq)]
pub struct Opt {
#[clap(long, short)]
number: u32,
#[clap(skip)]
k: Kind,
#[clap(skip)]
v: Vec<u32>,
#[clap(skip = Kind::A)]
k2: Kind,
#[clap(skip = vec![1, 2, 3])]
v2: Vec<u32>,
#[clap(skip = "cake")] // &str implements Into<String>
s: String,
}
#[derive(Debug, PartialEq)]
enum Kind {
A,
B,
}
impl Default for Kind {
fn default() -> Self {
Kind::B
}
}
fn main() {
assert_eq!(
Opt::parse_from(&["test", "-n", "10"]),
Opt {
number: 10,
k: Kind::B,
v: vec![],
k2: Kind::A,
v2: vec![1, 2, 3],
s: String::from("cake")
}
);
}

View file

@ -1,20 +0,0 @@
//! How to assign some aliases to subcommands
use clap::{AppSettings, Parser};
#[derive(Parser, Debug)]
// https://docs.rs/clap/2/clap/enum.AppSettings.html#variant.InferSubcommands
#[clap(setting = AppSettings::InferSubcommands)]
enum Opt {
// https://docs.rs/clap/2/clap/struct.App.html#method.alias
#[clap(alias = "foobar")]
Foo,
// https://docs.rs/clap/2/clap/struct.App.html#method.aliases
#[clap(aliases = &["baz", "fizz"])]
Bar,
}
fn main() {
let opt = Opt::parse();
println!("{:?}", opt);
}

View file

@ -1,41 +0,0 @@
//! How to parse `--foo=true --bar=false` and turn them into bool.
use clap::Parser;
fn true_or_false(s: &str) -> Result<bool, &'static str> {
match s {
"true" => Ok(true),
"false" => Ok(false),
_ => Err("expected `true` or `false`"),
}
}
#[derive(Parser, Debug, PartialEq)]
struct Opt {
// Default parser for `try_from_str` is FromStr::from_str.
// `impl FromStr for bool` parses `true` or `false` so this
// works as expected.
#[clap(long, parse(try_from_str))]
foo: bool,
// Of course, this could be done with an explicit parser function.
#[clap(long, parse(try_from_str = true_or_false))]
bar: bool,
// `bool` can be positional only with explicit `parse(...)` annotation
#[clap(long, parse(try_from_str))]
boom: bool,
}
fn main() {
assert_eq!(
Opt::parse_from(&["test", "--foo=true", "--bar=false", "true"]),
Opt {
foo: true,
bar: false,
boom: true
}
);
// no beauty, only truth and falseness
assert!(Opt::try_parse_from(&["test", "--foo=beauty"]).is_err());
}

View file

@ -34,66 +34,6 @@ pub const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
/// Default casing style for environment variables
pub const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
#[allow(clippy::large_enum_variant)]
#[derive(Clone)]
pub enum Kind {
Arg(Sp<Ty>),
FromGlobal(Sp<Ty>),
Subcommand(Sp<Ty>),
Flatten,
Skip(Option<Expr>),
ExternalSubcommand,
}
#[derive(Clone)]
pub struct Method {
name: Ident,
args: TokenStream,
}
#[derive(Clone)]
pub struct Parser {
pub kind: Sp<ParserKind>,
pub func: TokenStream,
}
#[derive(Debug, PartialEq, Clone)]
pub enum ParserKind {
FromStr,
TryFromStr,
FromOsStr,
TryFromOsStr,
FromOccurrences,
FromFlag,
}
/// Defines the casing for the attributes long representation.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum CasingStyle {
/// Indicate word boundaries with uppercase letter, excluding the first word.
Camel,
/// Keep all letters lowercase and indicate word boundaries with hyphens.
Kebab,
/// Indicate word boundaries with uppercase letter, including the first word.
Pascal,
/// Keep all letters uppercase and indicate word boundaries with underscores.
ScreamingSnake,
/// Keep all letters lowercase and indicate word boundaries with underscores.
Snake,
/// Keep all letters lowercase and remove word boundaries.
Lower,
/// Keep all letters uppercase and remove word boundaries.
Upper,
/// Use the original attribute name defined in the code.
Verbatim,
}
#[derive(Clone)]
pub enum Name {
Derived(Ident),
Assigned(TokenStream),
}
#[derive(Clone)]
pub struct Attrs {
name: Name,
@ -112,345 +52,7 @@ pub struct Attrs {
kind: Sp<Kind>,
}
impl Method {
pub fn new(name: Ident, args: TokenStream) -> Self {
Method { name, args }
}
fn from_lit_or_env(ident: Ident, lit: Option<LitStr>, env_var: &str) -> Self {
let mut lit = match lit {
Some(lit) => lit,
None => match env::var(env_var) {
Ok(val) => 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;
);
}
},
};
if ident == "author" {
let edited = process_author_str(&lit.value());
lit = LitStr::new(&edited, lit.span());
}
Method::new(ident, quote!(#lit))
}
}
impl ToTokens for Method {
fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
let Method { ref name, ref args } = self;
let tokens = quote!( .#name(#args) );
tokens.to_tokens(ts);
}
}
impl Parser {
fn default_spanned(span: Span) -> Sp<Self> {
let kind = Sp::new(ParserKind::TryFromStr, span);
let func = quote_spanned!(span=> ::std::str::FromStr::from_str);
Sp::new(Parser { kind, func }, span)
}
fn from_spec(parse_ident: Ident, spec: ParserSpec) -> Sp<Self> {
use self::ParserKind::*;
let kind = match &*spec.kind.to_string() {
"from_str" => FromStr,
"try_from_str" => TryFromStr,
"from_os_str" => FromOsStr,
"try_from_os_str" => TryFromOsStr,
"from_occurrences" => FromOccurrences,
"from_flag" => FromFlag,
s => abort!(spec.kind.span(), "unsupported parser `{}`", s),
};
let func = match spec.parse_func {
None => match kind {
FromStr | FromOsStr => {
quote_spanned!(spec.kind.span()=> ::std::convert::From::from)
}
TryFromStr => quote_spanned!(spec.kind.span()=> ::std::str::FromStr::from_str),
TryFromOsStr => abort!(
spec.kind.span(),
"you must set parser for `try_from_os_str` explicitly"
),
FromOccurrences => quote_spanned!(spec.kind.span()=> { |v| v as _ }),
FromFlag => quote_spanned!(spec.kind.span()=> ::std::convert::From::from),
},
Some(func) => match func {
Expr::Path(_) => quote!(#func),
_ => abort!(func, "`parse` argument must be a function path"),
},
};
let kind = Sp::new(kind, spec.kind.span());
let parser = Parser { kind, func };
Sp::new(parser, parse_ident.span())
}
}
impl CasingStyle {
fn from_lit(name: LitStr) -> Sp<Self> {
use self::CasingStyle::*;
let normalized = name.value().to_camel_case().to_lowercase();
let cs = |kind| Sp::new(kind, name.span());
match normalized.as_ref() {
"camel" | "camelcase" => cs(Camel),
"kebab" | "kebabcase" => cs(Kebab),
"pascal" | "pascalcase" => cs(Pascal),
"screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake),
"snake" | "snakecase" => cs(Snake),
"lower" | "lowercase" => cs(Lower),
"upper" | "uppercase" => cs(Upper),
"verbatim" | "verbatimcase" => cs(Verbatim),
s => abort!(name, "unsupported casing: `{}`", s),
}
}
}
impl Name {
pub fn translate(self, style: CasingStyle) -> TokenStream {
use CasingStyle::*;
match self {
Name::Assigned(tokens) => tokens,
Name::Derived(ident) => {
let s = ident.unraw().to_string();
let s = match style {
Pascal => s.to_camel_case(),
Kebab => s.to_kebab_case(),
Camel => s.to_mixed_case(),
ScreamingSnake => s.to_shouty_snake_case(),
Snake => s.to_snake_case(),
Lower => s.to_snake_case().replace("_", ""),
Upper => s.to_shouty_snake_case().replace("_", ""),
Verbatim => s,
};
quote_spanned!(ident.span()=> #s)
}
}
}
pub fn translate_char(self, style: CasingStyle) -> TokenStream {
use CasingStyle::*;
match self {
Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ),
Name::Derived(ident) => {
let s = ident.unraw().to_string();
let s = match style {
Pascal => s.to_camel_case(),
Kebab => s.to_kebab_case(),
Camel => s.to_mixed_case(),
ScreamingSnake => s.to_shouty_snake_case(),
Snake => s.to_snake_case(),
Lower => s.to_snake_case(),
Upper => s.to_shouty_snake_case(),
Verbatim => s,
};
let s = s.chars().next().unwrap();
quote_spanned!(ident.span()=> #s)
}
}
}
}
impl Attrs {
fn new(
default_span: Span,
name: Name,
ty: Option<Type>,
casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
) -> Self {
Self {
name,
ty,
casing,
env_casing,
doc_comment: vec![],
methods: vec![],
parser: Parser::default_spanned(default_span),
author: None,
version: None,
verbatim_doc_comment: None,
help_heading: None,
is_enum: false,
has_custom_parser: false,
kind: Sp::new(Kind::Arg(Sp::new(Ty::Other, default_span)), default_span),
}
}
fn push_method(&mut self, name: Ident, arg: impl ToTokens) {
if name == "name" {
self.name = Name::Assigned(quote!(#arg));
} else if name == "version" {
self.version = Some(Method::new(name, quote!(#arg)));
} else {
self.methods.push(Method::new(name, quote!(#arg)))
}
}
fn push_attrs(&mut self, attrs: &[Attribute]) {
use ClapAttr::*;
for attr in parse_clap_attributes(attrs) {
match attr {
Short(ident) => {
self.push_method(ident, self.name.clone().translate_char(*self.casing));
}
Long(ident) => {
self.push_method(ident, self.name.clone().translate(*self.casing));
}
Env(ident) => {
self.push_method(ident, self.name.clone().translate(*self.env_casing));
}
ArgEnum(_) => self.is_enum = true,
FromGlobal(ident) => {
let ty = Sp::call_site(Ty::Other);
let kind = Sp::new(Kind::FromGlobal(ty), ident.span());
self.set_kind(kind);
}
Subcommand(ident) => {
let ty = Sp::call_site(Ty::Other);
let kind = Sp::new(Kind::Subcommand(ty), ident.span());
self.set_kind(kind);
}
ExternalSubcommand(ident) => {
let kind = Sp::new(Kind::ExternalSubcommand, ident.span());
self.set_kind(kind);
}
Flatten(ident) => {
let kind = Sp::new(Kind::Flatten, ident.span());
self.set_kind(kind);
}
Skip(ident, expr) => {
let kind = Sp::new(Kind::Skip(expr), ident.span());
self.set_kind(kind);
}
VerbatimDocComment(ident) => self.verbatim_doc_comment = Some(ident),
DefaultValueT(ident, expr) => {
let ty = if let Some(ty) = self.ty.as_ref() {
ty
} else {
abort!(
ident,
"#[clap(default_value_t)] (without an argument) can be used \
only on field level";
note = "see \
https://docs.rs/structopt/0.3.5/structopt/#magical-methods")
};
let val = if let Some(expr) = expr {
quote!(#expr)
} else {
quote!(<#ty as ::std::default::Default>::default())
};
let val = quote_spanned!(ident.span()=> {
clap::lazy_static::lazy_static! {
static ref DEFAULT_VALUE: &'static str = {
let val: #ty = #val;
let s = ::std::string::ToString::to_string(&val);
::std::boxed::Box::leak(s.into_boxed_str())
};
}
*DEFAULT_VALUE
});
let raw_ident = Ident::new("default_value", ident.span());
self.methods.push(Method::new(raw_ident, val));
}
HelpHeading(ident, expr) => {
self.help_heading = Some(Method::new(ident, quote!(#expr)));
}
About(ident, about) => {
let method = Method::from_lit_or_env(ident, about, "CARGO_PKG_DESCRIPTION");
self.methods.push(method);
}
Author(ident, author) => {
self.author = Some(Method::from_lit_or_env(ident, author, "CARGO_PKG_AUTHORS"));
}
Version(ident, version) => {
self.version =
Some(Method::from_lit_or_env(ident, version, "CARGO_PKG_VERSION"));
}
NameLitStr(name, lit) => {
self.push_method(name, lit);
}
NameExpr(name, expr) => {
self.push_method(name, expr);
}
MethodCall(name, args) => self.push_method(name, quote!(#(#args),*)),
RenameAll(_, casing_lit) => {
self.casing = CasingStyle::from_lit(casing_lit);
}
RenameAllEnv(_, casing_lit) => {
self.env_casing = CasingStyle::from_lit(casing_lit);
}
Parse(ident, spec) => {
self.has_custom_parser = true;
self.parser = Parser::from_spec(ident, spec);
}
}
}
}
fn push_doc_comment(&mut self, attrs: &[Attribute], name: &str) {
use syn::Lit::*;
use syn::Meta::*;
let comment_parts: Vec<_> = attrs
.iter()
.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
}
})
.collect();
self.doc_comment =
process_doc_comment(comment_parts, name, self.verbatim_doc_comment.is_none());
}
pub fn from_struct(
span: Span,
attrs: &[Attribute],
@ -591,7 +193,7 @@ impl Attrs {
env_casing,
);
res.push_attrs(&variant.attrs);
res.push_doc_comment(&variant.attrs, "about");
res.push_doc_comment(&variant.attrs, "help");
if res.has_custom_parser {
abort!(
@ -626,7 +228,7 @@ impl Attrs {
env_casing,
);
res.push_attrs(&field.attrs);
res.push_doc_comment(&field.attrs, "about");
res.push_doc_comment(&field.attrs, "help");
match &*res.kind {
Kind::Flatten => {
@ -759,6 +361,191 @@ impl Attrs {
res
}
fn new(
default_span: Span,
name: Name,
ty: Option<Type>,
casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
) -> Self {
Self {
name,
ty,
casing,
env_casing,
doc_comment: vec![],
methods: vec![],
parser: Parser::default_spanned(default_span),
author: None,
version: None,
verbatim_doc_comment: None,
help_heading: None,
is_enum: false,
has_custom_parser: false,
kind: Sp::new(Kind::Arg(Sp::new(Ty::Other, default_span)), default_span),
}
}
fn push_method(&mut self, name: Ident, arg: impl ToTokens) {
if name == "name" {
self.name = Name::Assigned(quote!(#arg));
} else if name == "version" {
self.version = Some(Method::new(name, quote!(#arg)));
} else {
self.methods.push(Method::new(name, quote!(#arg)))
}
}
fn push_attrs(&mut self, attrs: &[Attribute]) {
use ClapAttr::*;
for attr in parse_clap_attributes(attrs) {
match attr {
Short(ident) => {
self.push_method(ident, self.name.clone().translate_char(*self.casing));
}
Long(ident) => {
self.push_method(ident, self.name.clone().translate(*self.casing));
}
Env(ident) => {
self.push_method(ident, self.name.clone().translate(*self.env_casing));
}
ArgEnum(_) => self.is_enum = true,
FromGlobal(ident) => {
let ty = Sp::call_site(Ty::Other);
let kind = Sp::new(Kind::FromGlobal(ty), ident.span());
self.set_kind(kind);
}
Subcommand(ident) => {
let ty = Sp::call_site(Ty::Other);
let kind = Sp::new(Kind::Subcommand(ty), ident.span());
self.set_kind(kind);
}
ExternalSubcommand(ident) => {
let kind = Sp::new(Kind::ExternalSubcommand, ident.span());
self.set_kind(kind);
}
Flatten(ident) => {
let kind = Sp::new(Kind::Flatten, ident.span());
self.set_kind(kind);
}
Skip(ident, expr) => {
let kind = Sp::new(Kind::Skip(expr), ident.span());
self.set_kind(kind);
}
VerbatimDocComment(ident) => self.verbatim_doc_comment = Some(ident),
DefaultValueT(ident, expr) => {
let ty = if let Some(ty) = self.ty.as_ref() {
ty
} else {
abort!(
ident,
"#[clap(default_value_t)] (without an argument) can be used \
only on field level";
note = "see \
https://docs.rs/structopt/0.3.5/structopt/#magical-methods")
};
let val = if let Some(expr) = expr {
quote!(#expr)
} else {
quote!(<#ty as ::std::default::Default>::default())
};
let val = quote_spanned!(ident.span()=> {
clap::lazy_static::lazy_static! {
static ref DEFAULT_VALUE: &'static str = {
let val: #ty = #val;
let s = ::std::string::ToString::to_string(&val);
::std::boxed::Box::leak(s.into_boxed_str())
};
}
*DEFAULT_VALUE
});
let raw_ident = Ident::new("default_value", ident.span());
self.methods.push(Method::new(raw_ident, val));
}
HelpHeading(ident, expr) => {
self.help_heading = Some(Method::new(ident, quote!(#expr)));
}
About(ident, about) => {
if let Some(method) =
Method::from_lit_or_env(ident, about, "CARGO_PKG_DESCRIPTION")
{
self.methods.push(method);
}
}
Author(ident, author) => {
self.author = Method::from_lit_or_env(ident, author, "CARGO_PKG_AUTHORS");
}
Version(ident, version) => {
self.version = Method::from_lit_or_env(ident, version, "CARGO_PKG_VERSION");
}
NameLitStr(name, lit) => {
self.push_method(name, lit);
}
NameExpr(name, expr) => {
self.push_method(name, expr);
}
MethodCall(name, args) => self.push_method(name, quote!(#(#args),*)),
RenameAll(_, casing_lit) => {
self.casing = CasingStyle::from_lit(casing_lit);
}
RenameAllEnv(_, casing_lit) => {
self.env_casing = CasingStyle::from_lit(casing_lit);
}
Parse(ident, spec) => {
self.has_custom_parser = true;
self.parser = Parser::from_spec(ident, spec);
}
}
}
}
fn push_doc_comment(&mut self, attrs: &[Attribute], name: &str) {
use syn::Lit::*;
use syn::Meta::*;
let comment_parts: Vec<_> = attrs
.iter()
.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
}
})
.collect();
self.doc_comment =
process_doc_comment(comment_parts, name, self.verbatim_doc_comment.is_none());
}
fn set_kind(&mut self, kind: Sp<Kind>) {
if let Kind::Arg(_) = *self.kind {
self.kind = kind;
@ -794,10 +581,10 @@ impl Attrs {
}
/// generate methods on top of a field
pub fn field_methods(&self, supports_long_about: bool) -> proc_macro2::TokenStream {
pub fn field_methods(&self, supports_long_help: bool) -> proc_macro2::TokenStream {
let methods = &self.methods;
let help_heading = self.help_heading.as_ref().into_iter();
match supports_long_about {
match supports_long_help {
true => {
let doc_comment = &self.doc_comment;
quote!( #(#doc_comment)* #(#help_heading)* #(#methods)* )
@ -806,7 +593,7 @@ impl Attrs {
let doc_comment = self
.doc_comment
.iter()
.filter(|mth| mth.name != "long_about");
.filter(|mth| mth.name != "long_help");
quote!( #(#doc_comment)* #(#help_heading)* #(#methods)* )
}
}
@ -837,8 +624,8 @@ impl Attrs {
self.is_enum
}
pub fn case_insensitive(&self) -> TokenStream {
let method = self.find_method("case_insensitive");
pub fn ignore_case(&self) -> TokenStream {
let method = self.find_method("ignore_case");
if let Some(method) = method {
method.args.clone()
@ -864,7 +651,69 @@ impl Attrs {
pub fn has_explicit_methods(&self) -> bool {
self.methods
.iter()
.any(|m| m.name != "about" && m.name != "long_about")
.any(|m| m.name != "help" && m.name != "long_help")
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone)]
pub enum Kind {
Arg(Sp<Ty>),
FromGlobal(Sp<Ty>),
Subcommand(Sp<Ty>),
Flatten,
Skip(Option<Expr>),
ExternalSubcommand,
}
#[derive(Clone)]
pub struct Method {
name: Ident,
args: TokenStream,
}
impl Method {
pub fn new(name: Ident, args: TokenStream) -> Self {
Method { name, args }
}
fn from_lit_or_env(ident: Ident, lit: Option<LitStr>, env_var: &str) -> Option<Self> {
let mut lit = match lit {
Some(lit) => lit,
None => match env::var(env_var) {
Ok(val) => {
if val.is_empty() {
return 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;
);
}
},
};
if ident == "author" {
let edited = process_author_str(&lit.value());
lit = LitStr::new(&edited, lit.span());
}
Some(Method::new(ident, quote!(#lit)))
}
}
impl ToTokens for Method {
fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
let Method { ref name, ref args } = self;
let tokens = quote!( .#name(#args) );
tokens.to_tokens(ts);
}
}
@ -892,3 +741,161 @@ fn process_author_str(author: &str) -> String {
res
}
#[derive(Clone)]
pub struct Parser {
pub kind: Sp<ParserKind>,
pub func: TokenStream,
}
impl Parser {
fn default_spanned(span: Span) -> Sp<Self> {
let kind = Sp::new(ParserKind::TryFromStr, span);
let func = quote_spanned!(span=> ::std::str::FromStr::from_str);
Sp::new(Parser { kind, func }, span)
}
fn from_spec(parse_ident: Ident, spec: ParserSpec) -> Sp<Self> {
use self::ParserKind::*;
let kind = match &*spec.kind.to_string() {
"from_str" => FromStr,
"try_from_str" => TryFromStr,
"from_os_str" => FromOsStr,
"try_from_os_str" => TryFromOsStr,
"from_occurrences" => FromOccurrences,
"from_flag" => FromFlag,
s => abort!(spec.kind.span(), "unsupported parser `{}`", s),
};
let func = match spec.parse_func {
None => match kind {
FromStr | FromOsStr => {
quote_spanned!(spec.kind.span()=> ::std::convert::From::from)
}
TryFromStr => quote_spanned!(spec.kind.span()=> ::std::str::FromStr::from_str),
TryFromOsStr => abort!(
spec.kind.span(),
"you must set parser for `try_from_os_str` explicitly"
),
FromOccurrences => quote_spanned!(spec.kind.span()=> { |v| v as _ }),
FromFlag => quote_spanned!(spec.kind.span()=> ::std::convert::From::from),
},
Some(func) => match func {
Expr::Path(_) => quote!(#func),
_ => abort!(func, "`parse` argument must be a function path"),
},
};
let kind = Sp::new(kind, spec.kind.span());
let parser = Parser { kind, func };
Sp::new(parser, parse_ident.span())
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum ParserKind {
FromStr,
TryFromStr,
FromOsStr,
TryFromOsStr,
FromOccurrences,
FromFlag,
}
/// Defines the casing for the attributes long representation.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum CasingStyle {
/// Indicate word boundaries with uppercase letter, excluding the first word.
Camel,
/// Keep all letters lowercase and indicate word boundaries with hyphens.
Kebab,
/// Indicate word boundaries with uppercase letter, including the first word.
Pascal,
/// Keep all letters uppercase and indicate word boundaries with underscores.
ScreamingSnake,
/// Keep all letters lowercase and indicate word boundaries with underscores.
Snake,
/// Keep all letters lowercase and remove word boundaries.
Lower,
/// Keep all letters uppercase and remove word boundaries.
Upper,
/// Use the original attribute name defined in the code.
Verbatim,
}
impl CasingStyle {
fn from_lit(name: LitStr) -> Sp<Self> {
use self::CasingStyle::*;
let normalized = name.value().to_camel_case().to_lowercase();
let cs = |kind| Sp::new(kind, name.span());
match normalized.as_ref() {
"camel" | "camelcase" => cs(Camel),
"kebab" | "kebabcase" => cs(Kebab),
"pascal" | "pascalcase" => cs(Pascal),
"screamingsnake" | "screamingsnakecase" => cs(ScreamingSnake),
"snake" | "snakecase" => cs(Snake),
"lower" | "lowercase" => cs(Lower),
"upper" | "uppercase" => cs(Upper),
"verbatim" | "verbatimcase" => cs(Verbatim),
s => abort!(name, "unsupported casing: `{}`", s),
}
}
}
#[derive(Clone)]
pub enum Name {
Derived(Ident),
Assigned(TokenStream),
}
impl Name {
pub fn translate(self, style: CasingStyle) -> TokenStream {
use CasingStyle::*;
match self {
Name::Assigned(tokens) => tokens,
Name::Derived(ident) => {
let s = ident.unraw().to_string();
let s = match style {
Pascal => s.to_camel_case(),
Kebab => s.to_kebab_case(),
Camel => s.to_mixed_case(),
ScreamingSnake => s.to_shouty_snake_case(),
Snake => s.to_snake_case(),
Lower => s.to_snake_case().replace("_", ""),
Upper => s.to_shouty_snake_case().replace("_", ""),
Verbatim => s,
};
quote_spanned!(ident.span()=> #s)
}
}
}
pub fn translate_char(self, style: CasingStyle) -> TokenStream {
use CasingStyle::*;
match self {
Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ),
Name::Derived(ident) => {
let s = ident.unraw().to_string();
let s = match style {
Pascal => s.to_camel_case(),
Kebab => s.to_kebab_case(),
Camel => s.to_mixed_case(),
ScreamingSnake => s.to_shouty_snake_case(),
Snake => s.to_snake_case(),
Lower => s.to_snake_case(),
Upper => s.to_shouty_snake_case(),
Verbatim => s,
};
let s = s.chars().next().unwrap();
quote_spanned!(ident.span()=> #s)
}
}
}
}

View file

@ -302,8 +302,7 @@ pub fn gen_augment(
Ty::OptionVec => quote_spanned! { ty.span()=>
.takes_value(true)
.value_name(#value_name)
.multiple_values(true)
.min_values(0)
.multiple_occurrences(true)
#possible_values
#validator
#allow_invalid_utf8
@ -313,7 +312,7 @@ pub fn gen_augment(
quote_spanned! { ty.span()=>
.takes_value(true)
.value_name(#value_name)
.multiple_values(true)
.multiple_occurrences(true)
#possible_values
#validator
#allow_invalid_utf8
@ -326,7 +325,6 @@ pub fn gen_augment(
Ty::Other if flag => quote_spanned! { ty.span()=>
.takes_value(false)
.multiple_values(false)
},
Ty::Other => {
@ -558,7 +556,7 @@ fn gen_parsers(
FromFlag => (quote!(), quote!(), func.clone()),
};
if attrs.is_enum() {
let ci = attrs.case_insensitive();
let ci = attrs.ignore_case();
parse = quote_spanned! { convert_type.span()=>
|s| <#convert_type as clap::ArgEnum>::from_str(s, #ci).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #name, err)))

View file

@ -15,35 +15,14 @@
use std::env;
use proc_macro2::{Span, TokenStream};
use proc_macro_error::abort_call_site;
use quote::quote;
use syn::{Attribute, Data, DataStruct, DeriveInput, Fields, Generics, Ident};
use syn::{Attribute, Generics, Ident};
use crate::{
attrs::{Attrs, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
dummies,
utils::Sp,
};
pub fn derive_into_app(input: &DeriveInput) -> TokenStream {
let ident = &input.ident;
dummies::into_app(ident);
match input.data {
Data::Struct(DataStruct {
fields: Fields::Named(_),
..
}) => gen_for_struct(ident, &input.generics, &input.attrs),
Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => gen_for_struct(ident, &input.generics, &input.attrs),
Data::Enum(_) => gen_for_enum(ident, &input.generics, &input.attrs),
_ => abort_call_site!("`#[derive(IntoApp)]` only supports non-tuple structs and enums"),
}
}
pub fn gen_for_struct(
struct_name: &Ident,
generics: &Generics,

View file

@ -20,5 +20,4 @@ mod subcommand;
pub use self::parser::derive_parser;
pub use arg_enum::derive_arg_enum;
pub use args::derive_args;
pub use into_app::derive_into_app;
pub use subcommand::derive_subcommand;

View file

@ -79,7 +79,7 @@ pub fn arg_enum(name: &Ident) {
fn value_variants<'a>() -> &'a [Self]{
unimplemented!()
}
fn from_str(_input: &str, _case_insensitive: bool) -> Result<Self, String> {
fn from_str(_input: &str, _ignore_case: bool) -> Result<Self, String> {
unimplemented!()
}
fn to_possible_value<'a>(&self) -> Option<clap::PossibleValue<'a>>{

View file

@ -12,8 +12,6 @@
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
#![doc(html_logo_url = "https://clap.rs/images/media/clap.png")]
#![doc(html_root_url = "https://docs.rs/clap_derive/3.0.0-beta.5")]
#![doc = include_str!("../README.md")]
#![forbid(unsafe_code)]
@ -43,21 +41,13 @@ pub fn arg_enum(input: TokenStream) -> TokenStream {
/// receiving an instance of `clap::ArgMatches` from conducting parsing, and then
/// implementing a conversion code to instantiate an instance of the user
/// context struct.
#[proc_macro_derive(Parser, attributes(clap))]
#[proc_macro_derive(Parser, attributes(clap, structopt))]
#[proc_macro_error]
pub fn parser(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
derives::derive_parser(&input).into()
}
/// Generates the `IntoApp` impl.
#[proc_macro_derive(IntoApp, attributes(clap))]
#[proc_macro_error]
pub fn into_app(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
derives::derive_into_app(&input).into()
}
/// Generates the `Subcommand` impl.
#[proc_macro_derive(Subcommand, attributes(clap))]
#[proc_macro_error]

View file

@ -9,6 +9,17 @@ use syn::{
Attribute, Expr, ExprLit, Ident, Lit, LitBool, LitStr, Token,
};
pub fn parse_clap_attributes(all_attrs: &[Attribute]) -> Vec<ClapAttr> {
all_attrs
.iter()
.filter(|attr| attr.path.is_ident("clap") || attr.path.is_ident("structopt"))
.flat_map(|attr| {
attr.parse_args_with(Punctuated::<ClapAttr, Token![,]>::parse_terminated)
.unwrap_or_abort()
})
.collect()
}
#[allow(clippy::large_enum_variant)]
pub enum ClapAttr {
// single-identifier attributes
@ -270,14 +281,3 @@ fn raw_method_suggestion(ts: ParseBuffer) -> String {
.into()
}
}
pub fn parse_clap_attributes(all_attrs: &[Attribute]) -> Vec<ClapAttr> {
all_attrs
.iter()
.filter(|attr| attr.path.is_ident("clap"))
.flat_map(|attr| {
attr.parse_args_with(Punctuated::<ClapAttr, Token![,]>::parse_terminated)
.unwrap_or_abort()
})
.collect()
}

View file

@ -2,10 +2,6 @@
name = "clap_generate"
version = "3.0.0-beta.5"
edition = "2018"
authors = [
"Kevin K. <kbknapp@gmail.com>",
"Clap Maintainers"
]
include = [
"src/**/*",
"Cargo.toml",
@ -15,7 +11,6 @@ include = [
description = "A generator library used with clap for shell completion scripts, manpage, etc."
repository = "https://github.com/clap-rs/clap/tree/master/clap_generate"
documentation = "https://docs.rs/clap_generate"
homepage = "https://clap.rs/"
keywords = [
"clap",
"cli",
@ -34,8 +29,7 @@ bench = false
clap = { path = "../", version = "=3.0.0-beta.5", default-features = false, features = ["std"] }
[dev-dependencies]
pretty_assertions = "0.7"
version-sync = "0.9"
pretty_assertions = "1.0"
clap = { path = "../", version = "=3.0.0-beta.5", default-features = false, features = ["std", "derive"] }
[features]

View file

@ -9,4 +9,3 @@ Generates completions (and other things) for [`clap`](https://github.com/clap-rs
* [Documentation](https://docs.rs/clap_generate)
* [Questions & Discussions](https://github.com/clap-rs/clap/discussions)
* [Website](https://clap.rs/)

View file

@ -84,7 +84,7 @@ fn generate_inner<'help>(
for option in p.get_opts() {
if let Some(shorts) = option.get_short_and_visible_aliases() {
let tooltip = get_tooltip(option.get_about(), shorts[0]);
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());
@ -92,7 +92,7 @@ fn generate_inner<'help>(
}
if let Some(longs) = option.get_long_and_visible_aliases() {
let tooltip = get_tooltip(option.get_about(), longs[0]);
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());
@ -102,7 +102,7 @@ fn generate_inner<'help>(
for flag in utils::flags(p) {
if let Some(shorts) = flag.get_short_and_visible_aliases() {
let tooltip = get_tooltip(flag.get_about(), shorts[0]);
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());
@ -110,7 +110,7 @@ fn generate_inner<'help>(
}
if let Some(longs) = flag.get_long_and_visible_aliases() {
let tooltip = get_tooltip(flag.get_about(), longs[0]);
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());

View file

@ -87,7 +87,7 @@ fn gen_fish_inner(root_command: &str, parent_commands: &[&str], app: &App, buffe
}
}
if let Some(data) = option.get_about() {
if let Some(data) = option.get_help() {
template.push_str(format!(" -d '{}'", escape_string(data)).as_str());
}
@ -112,7 +112,7 @@ fn gen_fish_inner(root_command: &str, parent_commands: &[&str], app: &App, buffe
}
}
if let Some(data) = flag.get_about() {
if let Some(data) = flag.get_help() {
template.push_str(format!(" -d '{}'", escape_string(data)).as_str());
}
@ -159,7 +159,7 @@ fn value_completion(option: &Arg) -> String {
Some(format!(
"{}\t{}",
value.get_name(),
value.get_about().unwrap_or_default()
value.get_help().unwrap_or_default()
))
})
.collect::<Vec<_>>()

View file

@ -88,7 +88,7 @@ fn generate_inner<'help>(
for option in p.get_opts() {
if let Some(shorts) = option.get_short_and_visible_aliases() {
let tooltip = get_tooltip(option.get_about(), shorts[0]);
let tooltip = get_tooltip(option.get_help(), shorts[0]);
for short in shorts {
completions.push_str(&preamble);
completions.push_str(
@ -102,7 +102,7 @@ fn generate_inner<'help>(
}
if let Some(longs) = option.get_long_and_visible_aliases() {
let tooltip = get_tooltip(option.get_about(), longs[0]);
let tooltip = get_tooltip(option.get_help(), longs[0]);
for long in longs {
completions.push_str(&preamble);
completions.push_str(
@ -118,7 +118,7 @@ fn generate_inner<'help>(
for flag in utils::flags(p) {
if let Some(shorts) = flag.get_short_and_visible_aliases() {
let tooltip = get_tooltip(flag.get_about(), shorts[0]);
let tooltip = get_tooltip(flag.get_help(), shorts[0]);
for short in shorts {
completions.push_str(&preamble);
completions.push_str(
@ -132,7 +132,7 @@ fn generate_inner<'help>(
}
if let Some(longs) = flag.get_long_and_visible_aliases() {
let tooltip = get_tooltip(flag.get_about(), longs[0]);
let tooltip = get_tooltip(flag.get_help(), longs[0]);
for long in longs {
completions.push_str(&preamble);
completions.push_str(

View file

@ -347,7 +347,7 @@ fn value_completion(arg: &Arg) -> Option<String> {
if let Some(values) = &arg.get_possible_values() {
if values
.iter()
.any(|value| !value.is_hidden() && value.get_about().is_some())
.any(|value| !value.is_hidden() && value.get_help().is_some())
{
Some(format!(
"(({}))",
@ -358,9 +358,9 @@ fn value_completion(arg: &Arg) -> Option<String> {
None
} else {
Some(format!(
r#"{name}\:"{about}""#,
r#"{name}\:"{tooltip}""#,
name = escape_value(value.get_name()),
about = value.get_about().map(escape_help).unwrap_or_default()
tooltip = value.get_help().map(escape_help).unwrap_or_default()
))
}
})
@ -429,7 +429,7 @@ fn write_opts_of(p: &App, p_global: Option<&App>) -> String {
for o in p.get_opts() {
debug!("write_opts_of:iter: o={}", o.get_name());
let help = o.get_about().map_or(String::new(), escape_help);
let help = o.get_help().map_or(String::new(), escape_help);
let conflicts = arg_conflicts(p, o, p_global);
let multiple = if o.is_set(ArgSettings::MultipleOccurrences) {
@ -532,7 +532,7 @@ fn write_flags_of(p: &App, p_global: Option<&App>) -> String {
for f in utils::flags(p) {
debug!("write_flags_of:iter: f={}", f.get_name());
let help = f.get_about().map_or(String::new(), escape_help);
let help = f.get_help().map_or(String::new(), escape_help);
let conflicts = arg_conflicts(p, &f, p_global);
let multiple = if f.is_set(ArgSettings::MultipleOccurrences) {
@ -628,7 +628,7 @@ fn write_positionals_of(p: &App) -> String {
cardinality = cardinality,
name = arg.get_name(),
help = arg
.get_about()
.get_help()
.map_or("".to_owned(), |v| " -- ".to_owned() + v)
.replace("[", "\\[")
.replace("]", "\\]")

View file

@ -5,10 +5,8 @@
// See the [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) files in this repository
// for more information.
#![doc(html_logo_url = "https://clap.rs/images/media/clap.png")]
#![doc(html_root_url = "https://docs.rs/clap_generate/3.0.0-beta.5")]
#![doc = include_str!("../README.md")]
#![deny(missing_docs, trivial_casts, unused_allocation, trivial_numeric_casts)]
#![warn(missing_docs, trivial_casts, unused_allocation, trivial_numeric_casts)]
#![forbid(unsafe_code)]
#![allow(clippy::needless_doctest_main)]
@ -30,7 +28,7 @@
//! fn build_cli() -> App<'static> {
//! App::new("example")
//! .arg(Arg::new("file")
//! .about("some input file")
//! .help("some input file")
//! .value_hint(ValueHint::AnyPath),
//! )
//! .arg(
@ -104,13 +102,13 @@ pub use shell::Shell;
/// App::new("compl")
/// .about("Tests completions")
/// .arg(Arg::new("file")
/// .about("some input file"))
/// .help("some input file"))
/// .subcommand(App::new("test")
/// .about("tests things")
/// .arg(Arg::new("case")
/// .long("case")
/// .takes_value(true)
/// .about("the case to test")))
/// .help("the case to test")))
/// }
/// ```
///

View file

@ -12,7 +12,7 @@ fn build_app_with_name(s: &'static str) -> App<'static> {
.arg(
Arg::new("file")
.value_hint(ValueHint::FilePath)
.about("some input file"),
.help("some input file"),
)
.arg(Arg::new("choice").possible_values(["first", "second"]))
.subcommand(
@ -20,7 +20,7 @@ fn build_app_with_name(s: &'static str) -> App<'static> {
Arg::new("case")
.long("case")
.takes_value(true)
.about("the case to test"),
.help("the case to test"),
),
)
}
@ -122,7 +122,7 @@ fn build_app_special_commands() -> App<'static> {
Arg::new("config")
.long("--config")
.takes_value(true)
.about("the other case to test"),
.help("the other case to test"),
),
)
.subcommand(App::new("some-cmd-with-hyphens").alias("hyphen"))
@ -260,7 +260,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('F')
.long("flag")
.visible_alias("flg")
.about("cmd flag"),
.help("cmd flag"),
)
.arg(
Arg::new("option")
@ -268,7 +268,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('O')
.long("option")
.visible_alias("opt")
.about("cmd option")
.help("cmd option")
.takes_value(true),
)
.arg(Arg::new("positional"))

View file

@ -12,14 +12,14 @@ fn build_app_with_name(s: &'static str) -> App<'static> {
.arg(
Arg::new("file")
.value_hint(ValueHint::FilePath)
.about("some input file"),
.help("some input file"),
)
.subcommand(
App::new("test").about("tests things").arg(
Arg::new("case")
.long("case")
.takes_value(true)
.about("the case to test"),
.help("the case to test"),
),
)
}
@ -86,7 +86,7 @@ fn build_app_special_commands() -> App<'static> {
Arg::new("config")
.long("--config")
.takes_value(true)
.about("the other case to test"),
.help("the other case to test"),
),
)
.subcommand(App::new("some-cmd-with-hyphens").alias("hyphen"))
@ -166,7 +166,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('F')
.long("flag")
.visible_alias("flg")
.about("cmd flag"),
.help("cmd flag"),
)
.arg(
Arg::new("option")
@ -174,7 +174,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('O')
.long("option")
.visible_alias("opt")
.about("cmd option")
.help("cmd option")
.takes_value(true),
)
.arg(Arg::new("positional"))

View file

@ -12,14 +12,14 @@ fn build_app_with_name(s: &'static str) -> App<'static> {
.arg(
Arg::new("file")
.value_hint(ValueHint::FilePath)
.about("some input file"),
.help("some input file"),
)
.subcommand(
App::new("test").about("tests things").arg(
Arg::new("case")
.long("case")
.takes_value(true)
.about("the case to test"),
.help("the case to test"),
),
)
}
@ -53,7 +53,7 @@ fn build_app_special_commands() -> App<'static> {
Arg::new("config")
.long("--config")
.takes_value(true)
.about("the other case to test"),
.help("the other case to test"),
),
)
.subcommand(App::new("some-cmd-with-hyphens").alias("hyphen"))
@ -88,28 +88,28 @@ fn build_app_special_help() -> App<'static> {
.arg(
Arg::new("single-quotes")
.long("single-quotes")
.about("Can be 'always', 'auto', or 'never'"),
.help("Can be 'always', 'auto', or 'never'"),
)
.arg(
Arg::new("double-quotes")
.long("double-quotes")
.about("Can be \"always\", \"auto\", or \"never\""),
.help("Can be \"always\", \"auto\", or \"never\""),
)
.arg(
Arg::new("backticks")
.long("backticks")
.about("For more information see `echo test`"),
.help("For more information see `echo test`"),
)
.arg(Arg::new("backslash").long("backslash").about("Avoid '\\n'"))
.arg(Arg::new("backslash").long("backslash").help("Avoid '\\n'"))
.arg(
Arg::new("brackets")
.long("brackets")
.about("List packages [filter]"),
.help("List packages [filter]"),
)
.arg(
Arg::new("expansions")
.long("expansions")
.about("Execute the shell command with $SHELL"),
.help("Execute the shell command with $SHELL"),
)
}
@ -139,7 +139,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('F')
.long("flag")
.visible_alias("flg")
.about("cmd flag"),
.help("cmd flag"),
)
.arg(
Arg::new("option")
@ -147,7 +147,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('O')
.long("option")
.visible_alias("opt")
.about("cmd option")
.help("cmd option")
.takes_value(true),
)
.arg(Arg::new("positional"))
@ -174,7 +174,7 @@ fn build_app_sub_subcommands() -> App<'static> {
Arg::new("config")
.long("--config")
.takes_value(true)
.about("the other case to test"),
.help("the other case to test"),
),
),
)

View file

@ -12,11 +12,11 @@ fn build_app_with_name(s: &'static str) -> App<'static> {
.arg(
Arg::new("file")
.value_hint(ValueHint::FilePath)
.about("some input file"),
.help("some input file"),
)
.arg(
Arg::new("config")
.about("some config file")
.help("some config file")
.short('c')
.visible_short_alias('C')
.long("config")
@ -27,7 +27,7 @@ fn build_app_with_name(s: &'static str) -> App<'static> {
Arg::new("case")
.long("case")
.takes_value(true)
.about("the case to test"),
.help("the case to test"),
),
)
}
@ -105,7 +105,7 @@ fn build_app_special_commands() -> App<'static> {
Arg::new("config")
.long("--config")
.takes_value(true)
.about("the other case to test"),
.help("the other case to test"),
),
)
.subcommand(App::new("some-cmd-with-hyphens").alias("hyphen"))
@ -198,7 +198,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('F')
.long("flag")
.visible_alias("flg")
.about("cmd flag"),
.help("cmd flag"),
)
.arg(
Arg::new("option")
@ -206,7 +206,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('O')
.long("option")
.visible_alias("opt")
.about("cmd option")
.help("cmd option")
.takes_value(true),
)
.arg(Arg::new("positional"))

View file

@ -12,14 +12,14 @@ fn build_app_with_name(s: &'static str) -> App<'static> {
.arg(
Arg::new("file")
.value_hint(ValueHint::FilePath)
.about("some input file"),
.help("some input file"),
)
.subcommand(
App::new("test").about("tests things").arg(
Arg::new("case")
.long("case")
.takes_value(true)
.about("the case to test"),
.help("the case to test"),
),
)
}
@ -115,7 +115,7 @@ fn build_app_special_commands() -> App<'static> {
Arg::new("config")
.long("--config")
.takes_value(true)
.about("the other case to test"),
.help("the other case to test"),
),
)
.subcommand(App::new("some-cmd-with-hypens").alias("hyphen"))
@ -252,28 +252,28 @@ fn build_app_special_help() -> App<'static> {
.arg(
Arg::new("single-quotes")
.long("single-quotes")
.about("Can be 'always', 'auto', or 'never'"),
.help("Can be 'always', 'auto', or 'never'"),
)
.arg(
Arg::new("double-quotes")
.long("double-quotes")
.about("Can be \"always\", \"auto\", or \"never\""),
.help("Can be \"always\", \"auto\", or \"never\""),
)
.arg(
Arg::new("backticks")
.long("backticks")
.about("For more information see `echo test`"),
.help("For more information see `echo test`"),
)
.arg(Arg::new("backslash").long("backslash").about("Avoid '\\n'"))
.arg(Arg::new("backslash").long("backslash").help("Avoid '\\n'"))
.arg(
Arg::new("brackets")
.long("brackets")
.about("List packages [filter]"),
.help("List packages [filter]"),
)
.arg(
Arg::new("expansions")
.long("expansions")
.about("Execute the shell command with $SHELL"),
.help("Execute the shell command with $SHELL"),
)
}
@ -450,7 +450,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('F')
.long("flag")
.visible_alias("flg")
.about("cmd flag"),
.help("cmd flag"),
)
.arg(
Arg::new("option")
@ -458,7 +458,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('O')
.long("option")
.visible_alias("opt")
.about("cmd option")
.help("cmd option")
.takes_value(true),
)
.arg(Arg::new("positional"))
@ -518,7 +518,7 @@ fn build_app_with_files_and_dirs() -> App<'static> {
.arg(
Arg::new("directory")
.long("dir")
.about("specify a directory")
.help("specify a directory")
.value_name("DIR")
.number_of_values(3)
.value_hint(ValueHint::DirPath),
@ -528,7 +528,7 @@ fn build_app_with_files_and_dirs() -> App<'static> {
.long("inputfiles")
.value_name("FILE")
.multiple_occurrences(true)
.about("specify a file")
.help("specify a file")
.value_hint(ValueHint::FilePath),
)
}

View file

@ -1,6 +0,0 @@
use version_sync::assert_html_root_url_updated;
#[test]
fn test_html_root_url() {
assert_html_root_url_updated!("src/lib.rs");
}

View file

@ -2,9 +2,6 @@
name = "clap_generate_fig"
version = "3.0.0-beta.5"
edition = "2018"
authors = [
"Clap Maintainers"
]
include = [
"src/**/*",
"Cargo.toml",
@ -14,7 +11,6 @@ include = [
description = "A generator library used with clap for Fig completion scripts"
repository = "https://github.com/clap-rs/clap/tree/master/clap_generate_fig"
documentation = "https://docs.rs/clap_generate_fig"
homepage = "https://clap.rs/"
keywords = [
"clap",
"cli",
@ -35,7 +31,7 @@ clap = { path = "../", version = "=3.0.0-beta.5", default-features = false, feat
clap_generate = { path = "../clap_generate", version = "=3.0.0-beta.5" }
[dev-dependencies]
pretty_assertions = "0.7"
pretty_assertions = "1.0"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View file

@ -4,6 +4,5 @@ Generates [Fig](https://github.com/withfig/autocomplete) completions for [`clap`
<!-- * [Documentation][docs] -->
* [Questions & Discussions](https://github.com/clap-rs/clap/discussions)
* [Website](https://clap.rs/)
<!-- [docs]: https://docs.rs/clap_generate_fig -->

View file

@ -144,7 +144,7 @@ fn gen_options(app: &App, indent: usize) -> String {
));
}
if let Some(data) = option.get_about() {
if let Some(data) = option.get_help() {
buffer.push_str(&format!(
"{:indent$}description: \"{}\",\n",
"",
@ -194,7 +194,7 @@ fn gen_options(app: &App, indent: usize) -> String {
));
}
if let Some(data) = flag.get_about() {
if let Some(data) = flag.get_help() {
buffer.push_str(&format!(
"{:indent$}description: \"{}\",\n",
"",
@ -257,11 +257,11 @@ fn gen_args(arg: &Arg, indent: usize) -> String {
indent = indent + 4,
));
if let Some(about) = value.get_about() {
if let Some(help) = value.get_help() {
buffer.push_str(&format!(
"{:indent$}description: \"{}\",\n",
"",
escape_string(about),
escape_string(help),
indent = indent + 4
));
}

View file

@ -5,9 +5,7 @@
//! Generates [Fig](https://github.com/withfig/autocomplete) completions for [`clap`](https://github.com/clap-rs/clap) based CLIs
#![doc(html_logo_url = "https://clap.rs/images/media/clap.png")]
#![doc(html_root_url = "https://docs.rs/clap_generate_fig/3.0.0-beta.5")]
#![deny(missing_docs, trivial_casts, unused_allocation, trivial_numeric_casts)]
#![warn(missing_docs, trivial_casts, unused_allocation, trivial_numeric_casts)]
#![forbid(unsafe_code)]
#![allow(clippy::needless_doctest_main)]

View file

@ -13,14 +13,14 @@ fn build_app_with_name(s: &'static str) -> App<'static> {
.arg(
Arg::new("file")
.value_hint(ValueHint::FilePath)
.about("some input file"),
.help("some input file"),
)
.subcommand(
App::new("test").about("tests things").arg(
Arg::new("case")
.long("case")
.takes_value(true)
.about("the case to test"),
.help("the case to test"),
),
)
}
@ -101,7 +101,7 @@ fn build_app_special_commands() -> App<'static> {
Arg::new("config")
.long("--config")
.takes_value(true)
.about("the other case to test"),
.help("the other case to test"),
),
)
.subcommand(App::new("some-cmd-with-hyphens").alias("hyphen"))
@ -211,28 +211,28 @@ fn build_app_special_help() -> App<'static> {
.arg(
Arg::new("single-quotes")
.long("single-quotes")
.about("Can be 'always', 'auto', or 'never'"),
.help("Can be 'always', 'auto', or 'never'"),
)
.arg(
Arg::new("double-quotes")
.long("double-quotes")
.about("Can be \"always\", \"auto\", or \"never\""),
.help("Can be \"always\", \"auto\", or \"never\""),
)
.arg(
Arg::new("backticks")
.long("backticks")
.about("For more information see `echo test`"),
.help("For more information see `echo test`"),
)
.arg(Arg::new("backslash").long("backslash").about("Avoid '\\n'"))
.arg(Arg::new("backslash").long("backslash").help("Avoid '\\n'"))
.arg(
Arg::new("brackets")
.long("brackets")
.about("List packages [filter]"),
.help("List packages [filter]"),
)
.arg(
Arg::new("expansions")
.long("expansions")
.about("Execute the shell command with $SHELL"),
.help("Execute the shell command with $SHELL"),
)
}
@ -294,7 +294,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('F')
.long("flag")
.visible_alias("flg")
.about("cmd flag"),
.help("cmd flag"),
)
.arg(
Arg::new("option")
@ -302,7 +302,7 @@ fn build_app_with_aliases() -> App<'static> {
.visible_short_alias('O')
.long("option")
.visible_alias("opt")
.about("cmd option")
.help("cmd option")
.takes_value(true),
)
.arg(Arg::new("positional"))
@ -357,7 +357,7 @@ fn build_app_sub_subcommands() -> App<'static> {
Arg::new("config")
.long("--config")
.takes_value(true)
.about("the other case to test"),
.help("the other case to test"),
),
),
)

View file

@ -1,32 +0,0 @@
[package]
name = "clap_up"
version = "0.0.0"
edition = "2018"
authors = [
"Pavan Kumar Sunkara <pavan.sss1991@gmail.com>",
"Clap Maintainers",
]
description = "Automatic code upgrader for Clap"
repository = "https://github.com/clap-rs/clap/tree/master/clap_up"
homepage = "https://clap.rs/"
keywords = [
"clap",
"cli",
"upgrader",
"cargo-up",
]
categories = []
license = "MIT OR Apache-2.0"
readme = "README.md"
[lib]
bench = false
[dependencies]
cargo-up = { version = "=0.0.4" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[package.metadata.workspaces]
independent = true

View file

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View file

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015-2016 Kevin B. Knapp
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,13 +0,0 @@
# clap_up
Automatic code upgrader for [clap](https://docs.rs/clap) (using tool [cargo-up](https://github.com/pksunkara/cargo-up))
### Usage
```
cargo install cargo-up --features cli --no-default-features
```
```
cargo up dep clap
```

View file

@ -1,166 +0,0 @@
use cargo_up::{
ra_ap_syntax::{
ast::{ArgListOwner, CallExpr},
AstNode,
},
Runner, Version,
};
pub fn runner() -> Runner {
Runner::new()
.minimum("2.33.0")
.unwrap()
.version(
Version::new("3.0.0-rc.0")
.unwrap()
.peers(&["structopt"])
// .replace_dep("structopt", "clap", features = ["derive"])
.rename_structs("clap::args::subcommand", &[["SubCommand", "App"]])
.rename_methods(
"structopt::StructOpt",
&[
["from_args", "parse"],
["from_iter", "parse_from"],
["from_iter_safe", "try_parse_from"],
["from_clap", "from_arg_matches"],
["clap", "into_app"],
],
)
.rename_variants(
"clap::errors::ErrorKind",
&[
["HelpDisplayed", "DisplayHelp"],
["VersionDisplayed", "DisplayVersion"],
[
"MissingArgumentOrSubcommand",
"DisplayHelpOnMissingArgumentOrSubcommand",
],
],
)
.rename_variants(
"clap::app::settings::AppSettings",
&[
["DisableHelpFlags", "DisableHelpFlag"],
["DisableVersion", "DisableVersionFlag"],
// @TODO @v3 should be removed, not renamed
["VersionlessSubcommands", "DisableVersionForSubcommands"],
],
)
.rename_variants(
"clap::args::settings::ArgSettings",
&[
["CaseInsensitive", "IgnoreCase"],
["AllowLeadingHyphen", "AllowHyphenValues"],
["EmptyValues", "AllowEmptyValues"],
],
)
.rename_methods(
"clap::app::App",
&[
["from_yaml", "from"],
["arg_from_usage", "arg"],
["args_from_usage", "args"],
["help", "override_help"],
["usage", "override_usage"],
["template", "help_template"],
["get_matches_safe", "try_get_matches"],
["get_matches_from_safe", "try_get_matches_from"],
["get_matches_from_safe_borrow", "try_get_matches_from_mut"],
["set_term_width", "term_width"],
["with_defaults", "new"],
["version_message", "mut_arg"],
["version_short", "mut_arg"],
["help_message", "mut_arg"],
["help_short", "mut_arg"],
["generate_usage", "render_usage"],
],
)
.rename_methods(
"clap::args::arg::Arg",
&[
["help", "about"],
["from_usage", "from"],
["set", "setting"],
["unset", "unset_setting"],
["from_yaml", "from"],
["with_name", "new"],
["required_if", "required_if_eq"],
["required_ifs", "required_if_eq_any"],
["required_unless", "required_unless_present"],
["required_unless_one", "required_unless_present_any"],
["required_unless_all", "required_unless_present_all"],
],
)
.rename_methods(
"clap::args::group::ArgGroup",
&[["from_yaml", "from"], ["with_name", "new"]],
)
.rename_methods(
"clap::args::subcommand::SubCommand",
&[["from_yaml", "from"], ["with_name", "new"]],
)
.rename_members("clap::errors::Error", &[["message", "cause"]]) // TODO: check
.hook_method_call_expr_on("clap::app::App", "args_from_usage", |u, n, _| {
let arg = n.arg_list().unwrap().args().last();
u.insert(
arg.unwrap().syntax().text_range().end(),
".lines().map(|l| l.trim()).filter(|l| !l.is_empty())",
);
})
.hook_path_expr_on("clap::app::App", "with_defaults", |u, n, _| {
if let Some(parent) = n.syntax().parent() {
if let Some(call_expr) = CallExpr::cast(parent) {
// TODO: Add full path
u.insert(
call_expr.syntax().text_range().end(),
".author(crate_authors!()).version(crate_version!())",
);
}
}
})
.hook_method_call_expr_on("clap::app::App", "version_message", |u, n, _| {
let arg_list = n.arg_list().unwrap();
u.insert(
arg_list.l_paren_token().unwrap().text_range().end(),
"\"version\", |a| a.about(",
);
u.insert(arg_list.r_paren_token().unwrap().text_range().start(), ")")
})
.hook_method_call_expr_on("clap::app::App", "version_short", |u, n, _| {
let arg_list = n.arg_list().unwrap();
u.insert(
arg_list.l_paren_token().unwrap().text_range().end(),
"\"version\", |a| a.short(",
);
u.insert(
arg_list.r_paren_token().unwrap().text_range().start(),
".trim_start_matches(|c| c == '-').chars().nth(0).unwrap_or('V'))",
)
})
.hook_method_call_expr_on("clap::app::App", "help_message", |u, n, _| {
let arg_list = n.arg_list().unwrap();
u.insert(
arg_list.l_paren_token().unwrap().text_range().end(),
"\"help\", |a| a.about(",
);
u.insert(arg_list.r_paren_token().unwrap().text_range().start(), ")")
})
.hook_method_call_expr_on("clap::app::App", "help_short", |u, n, _| {
let arg_list = n.arg_list().unwrap();
u.insert(
arg_list.l_paren_token().unwrap().text_range().end(),
"\"help\", |a| a.short(",
);
u.insert(
arg_list.r_paren_token().unwrap().text_range().start(),
".trim_start_matches(|c| c == '-').chars().nth(0).unwrap_or('h'))",
)
}),
)
.version(Version::new("3.0.0").unwrap())
}

3
committed.toml Normal file
View file

@ -0,0 +1,3 @@
style="conventional"
ignore_author_re="dependabot"
merge_commit = false

View file

@ -1,17 +0,0 @@
#!/bin/bash
IFS=$'\n'
touch .tmp.out
echo -n "Testing"
for TEST in $(find tests/ -type f -name "*.rs" -exec basename {} .rs \;); do
echo -n "."
echo -n -e "$TEST:\t" >> .tmp.out
cargo test --test $TEST 2>&1 | grep -o -e '[0-9]* failed;' >> .tmp.out
done
echo "Done"
column -t < .tmp.out
rm .tmp.out
unset IFS

73
docs/FAQ.md Normal file
View file

@ -0,0 +1,73 @@
1. [Comparisons](#comparisons)
1. [How does `clap` compare to structopt?](#how-does-clap-compare-to-structopt)
2. [How does `clap` compare to getopts?](#how-does-clap-compare-to-getopts)
3. [How does `clap` compare to docopt.rs?](#how-does-clap-compare-to-docoptrs)
4. [What are some reasons to use `clap`? (The Pitch)](#what-are-some-reasons-to-use-clap-the-pitch)
5. [What are some reasons *not* to use `clap`? (The Anti Pitch)](#what-are-some-reasons-not-to-use-clap-the-anti-pitch)
6. [Reasons to use `clap`](#reasons-to-use-clap)
7. [Reasons to `docopt`](#reasons-to-docopt)
8. [Reasons to use `getopts`](#reasons-to-use-getopts)
2. [How many methods are there to create an App/Arg?](#how-many-methods-are-there-to-create-an-apparg)
3. [Why is there a default subcommand of help?](#why-is-there-a-default-subcommand-of-help)
### Comparisons
First, let me say that these comparisons are highly subjective, and not meant
in a critical or harsh manner. All the argument parsing libraries out there (to
include `clap`) have their own strengths and weaknesses. Sometimes it just
comes down to personal taste when all other factors are equal. When in doubt,
try them all and pick one that you enjoy :) There's plenty of room in the Rust
community for multiple implementations!
For less detailed but more broad comparisons, see
[argparse-benchmarks](https://github.com/rust-cli/argparse-benchmarks-rs).
#### How does `clap` compare to [structopt](https://github.com/TeXitoi/structopt)?
Simple! `clap` *is* `structopt`. `structopt` started as a derive API built on
top of clap v2. With clap v3, we've forked structopt and integrated it
directly into clap.
The benefits of integrating `structopt` and `clap` are:
- Easier cross-linking in documentation
- [Documentation parity](../examples)
- Tighter design feedback loop, ensuring all new features are designed with
derives in mind and easier to change `clap` in response to `structopt` bugs.
- Clearer endorsement of `structopt`
For more details on what has changed and how to migrate, see the [CHANGELOG](../CHANGELOG.md)
#### What are some reasons to use `clap`? (The Pitch)
`clap` is as fast, and as lightweight as possible while still giving all the features you'd expect from a modern argument parser. In fact, for the amount and type of features `clap` offers it remains about as fast as `getopts`. If you use `clap` when just need some simple arguments parsed, you'll find it's a walk in the park. `clap` also makes it possible to represent extremely complex, and advanced requirements, without too much thought. `clap` aims to be intuitive, easy to use, and fully capable for wide variety use cases and needs.
#### What are some reasons *not* to use `clap`? (The Anti Pitch)
Depending on the style in which you choose to define the valid arguments, `clap` can be very verbose. `clap` also offers so many finetuning knobs and dials, that learning everything can seem overwhelming. I strive to keep the simple cases simple, but when turning all those custom dials it can get complex. `clap` is also opinionated about parsing. Even though so much can be tweaked and tuned with `clap` (and I'm adding more all the time), there are still certain features which `clap` implements in specific ways which may be contrary to some users use-cases.
#### Reasons to use `clap`
* You want all the nice CLI features your users may expect, yet you don't want to implement them all yourself. You'd like to focus your application, not argument parsing.
* In addition to the point above; you don't want to sacrifice performance to get all those nice features
* You have complex requirements/conflicts between your various valid args.
* You want to use subcommands (although other libraries also support subcommands, they are not nearly as feature rich as those provided by `clap`)
* You want some sort of custom validation built into the argument parsing process, instead of as part of your application (which allows for earlier failures, better error messages, more cohesive experience, etc.)
### How many approaches are there to create an App/Arg?
To build an `App` there are three:
- Derive Macros
- Builder Pattern
To build an `Arg` there are four:
- Derive Macros
- Builder Pattern
### Why is there a default subcommand of help?
There is only a default subcommand of `help` when other subcommands have been defined manually. So it's opt-in(ish), being that you only get a `help` subcommand if you're actually using subcommands.
Also, if the user defined a `help` subcommand themselves, the auto-generated one wouldn't be added (meaning it's only generated if the user hasn't defined one themselves).

View file

@ -1,77 +0,0 @@
use clap::App;
fn main() {
// This example shows how to create an application with several arguments using usage strings, which can be
// far less verbose that shown in 01b_QuickExample.rs, but is more readable. The downside is you cannot set
// the more advanced configuration options using this method (well...actually you can, you'll see ;) )
//
// The example below is functionally identical to the 01b_quick_example.rs and 01c_quick_example.rs
//
// Create an application with 5 possible arguments (2 auto generated) and 2 subcommands (1 auto generated)
// - A config file
// + Uses "-c filename" or "--config filename"
// - An output file
// + A positional argument (i.e. "$ myapp output_filename")
// - A debug flag
// + Uses "-d" or "--debug"
// + Allows multiple occurrences of such as "-dd" (for vary levels of debugging, as an example)
// - A help flag (automatically generated by clap)
// + Uses "-h" or "--help" (Only autogenerated if you do NOT specify your own "-h" or "--help")
// - A version flag (automatically generated by clap)
// + Uses "-V" or "--version" (Only autogenerated if you do NOT specify your own "-V" or "--version")
// - A subcommand "test" (subcommands behave like their own apps, with their own arguments
// + Used by "$ myapp test" with the following arguments
// > A list flag
// = Uses "-l" (usage is "$ myapp test -l"
// > A help flag (automatically generated by clap
// = Uses "-h" or "--help" (full usage "$ myapp test -h" or "$ myapp test --help")
// > A version flag (automatically generated by clap
// = Uses "-V" or "--version" (full usage "$ myapp test -V" or "$ myapp test --version")
// - A subcommand "help" (automatically generated by clap because we specified a subcommand of our own)
// + Used by "$ myapp help" (same functionality as "-h" or "--help")
let matches = App::new("MyApp")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.arg("-c, --config=[FILE] 'Sets a custom config file'")
.arg("<output> 'Sets an optional output file'")
.arg("-d..., --debug... 'Turn debugging information on'")
.subcommand(
App::new("test")
.about("does testing things")
.arg("-l, --list 'lists test values'"),
)
.get_matches();
// You can check the value provided by positional arguments, or option arguments
if let Some(o) = matches.value_of("output") {
println!("Value for output: {}", o);
}
if let Some(c) = matches.value_of("config") {
println!("Value for config: {}", c);
}
// You can see how many times a particular flag or argument occurred
// Note, only flags can have multiple occurrences
match matches.occurrences_of("debug") {
0 => println!("Debug mode is off"),
1 => println!("Debug mode is kind of on"),
2 => println!("Debug mode is on"),
_ => println!("Don't be crazy"),
}
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level app
if let Some(matches) = matches.subcommand_matches("test") {
// "$ myapp test" was run
if matches.is_present("list") {
// "$ myapp test -l" was run
println!("Printing testing lists...");
} else {
println!("Not printing testing lists...");
}
}
// Continued program logic goes here...
}

View file

@ -1,96 +0,0 @@
use clap::{App, Arg};
fn main() {
// This method shows the traditional, and slightly more configurable way to set up arguments. This method is
// more verbose, but allows setting more configuration options, and even supports easier dynamic generation.
//
// The example below is functionally identical to the 01a_quick_example.rs and 01c_quick_example.rs
//
// *NOTE:* You can actually achieve the best of both worlds by using Arg::from() (instead of Arg::new())
// and *then* setting any additional properties.
//
// Create an application with 5 possible arguments (2 auto generated) and 2 subcommands (1 auto generated)
// - A config file
// + Uses "-c filename" or "--config filename"
// - An output file
// + A positional argument (i.e. "$ myapp output_filename")
// - A debug flag
// + Uses "-d" or "--debug"
// + Allows multiple occurrences of such as "-dd" (for vary levels of debugging, as an example)
// - A help flag (automatically generated by clap)
// + Uses "-h" or "--help" (Only autogenerated if you do NOT specify your own "-h" or "--help")
// - A version flag (automatically generated by clap)
// + Uses "-V" or "--version" (Only autogenerated if you do NOT specify your own "-V" or "--version")
// - A subcommand "test" (subcommands behave like their own apps, with their own arguments
// + Used by "$ myapp test" with the following arguments
// > A list flag
// = Uses "-l" (usage is "$ myapp test -l"
// > A help flag (automatically generated by clap
// = Uses "-h" or "--help" (full usage "$ myapp test -h" or "$ myapp test --help")
// > A version flag (automatically generated by clap
// = Uses "-V" or "--version" (full usage "$ myapp test -V" or "$ myapp test --version")
// - A subcommand "help" (automatically generated by clap because we specified a subcommand of our own)
// + Used by "$ myapp help" (same functionality as "-h" or "--help")
let matches = App::new("MyApp")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.arg(
Arg::new("config")
.short('c')
.long("config")
.value_name("FILE")
.about("Sets a custom config file")
.takes_value(true),
)
.arg(
Arg::new("output")
.about("Sets an optional output file")
.index(1),
)
.arg(
Arg::new("debug")
.short('d')
.long("debug")
.multiple_occurrences(true)
.about("Turn debugging information on"),
)
.subcommand(
App::new("test")
.about("does testing things")
.arg(Arg::new("list").short('l').about("lists test values")),
)
.get_matches();
// You can check the value provided by positional arguments, or option arguments
if let Some(o) = matches.value_of("output") {
println!("Value for output: {}", o);
}
if let Some(c) = matches.value_of("config") {
println!("Value for config: {}", c);
}
// You can see how many times a particular flag or argument occurred
// Note, only flags can have multiple occurrences
match matches.occurrences_of("debug") {
0 => println!("Debug mode is off"),
1 => println!("Debug mode is kind of on"),
2 => println!("Debug mode is on"),
_ => println!("Don't be crazy"),
}
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level app
if let Some(matches) = matches.subcommand_matches("test") {
// "$ myapp test" was run
if matches.is_present("list") {
// "$ myapp test -l" was run
println!("Printing testing lists...");
} else {
println!("Not printing testing lists...");
}
}
// Continued program logic goes here...
}

View file

@ -1,28 +0,0 @@
use clap::App;
fn main() {
// Apps describe the top level application
//
// You create an App and set various options on that App using the "builder pattern"
//
// The options (version(), author(), about()) aren't mandatory, but recommended. There is
// another option, usage(), which is an exception to the rule. This should only be used when
// the default usage string automatically generated by clap doesn't suffice.
//
// You also set all the valid arguments your App should accept via the arg(), args(), arg()
// and args_from_usage() (as well as subcommands via the subcommand() and subcommands() methods) which
// will be covered later.
//
// Once all options have been set, call one of the .get_matches* family of methods in order to
// start the parsing and find all valid command line arguments that supplied by the user at
// runtime. The name given to new() will be displayed when the version or help flags are used.
App::new("MyApp")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.get_matches();
// This example doesn't do much, but it *does* give automatic -h, --help, -V, and --version functionality ;)
// Continued program logic goes here...
}

View file

@ -1,86 +0,0 @@
use clap::{App, Arg};
fn main() {
// Args describe a possible valid argument which may be supplied by the user at runtime. There
// are three different types of arguments (flags, options, and positional) as well as a fourth
// special type of argument, called Subcommands (which will be discussed separately).
//
// Args are described in the same manner as Apps using the "builder pattern" with multiple
// methods describing various settings for the individual arguments. Or by supplying a "usage"
// string. Both methods have their pros and cons.
//
// Arguments can be added to applications in two manners, one at a time with the arg(), and
// arg() method, or multiple arguments at once via a Vec<Arg> inside the args() method,
// or a single &str describing multiple Args (one per line) supplied to args_from_usage().
//
// There are various options which can be set for a given argument, some apply to any of the
// three types of arguments, some only apply one or two of the types. *NOTE* if you set
// incompatible options on a single argument, clap will panic! at runtime. This is by design,
// so that you know right away an error was made by the developer, not the end user.
//
// # Help and Version
// clap automatically generates a help and version flag for you, unless you specify your
// own. By default help uses "-h" and "--help", and version uses "-V" and "--version". You can
// safely override "-V" and "-h" to your own arguments, and "--help" and "--version" will still
// be automatically generated for you.
let matches = App::new("MyApp")
// All application settings go here...
// A simple "Flag" argument example (i.e. "-d") using the builder pattern
.arg(
Arg::new("debug")
.about("turn on debugging information")
.short('d'),
)
// Two arguments, one "Option" argument (i.e. one that takes a value) such
// as "-c some", and one positional argument (i.e. "myapp some_file")
.args(&[
Arg::new("config")
.about("sets the config file to use")
.takes_value(true)
.short('c')
.long("config"),
Arg::new("input")
.about("the input file to use")
.required(true),
])
// *Note* the following two examples are convenience methods, if you wish
// to still get the full configurability of Arg::new() and the readability
// of arg(), you can instantiate a new Arg with Arg::from() and
// still be able to set all the additional properties, just like Arg::new()
//
//
// One "Flag" using a usage string
.arg("--license 'display the license file'")
// Two args, one "Positional", and one "Option" using a usage string
.arg("[output] 'Supply an output file to use'")
.arg("-i, --int=[IFACE] 'Set an interface to use'")
.get_matches();
// Here are some examples of using the arguments defined above. Keep in mind that this is only
// an example, and may be somewhat contrived
//
// First we check if debugging should be on or not
println!(
"Debugging mode is: {}",
if matches.is_present("debug") {
"ON"
} else {
"OFF"
}
);
// Next we print the config file we're using, if any was defined with either -c <file> or
// --config <file>
if let Some(config) = matches.value_of("config") {
println!("A config file was passed in: {}", config);
}
// Let's print the <INPUT> file the user passed in. We can use .unwrap() here because the arg is
// required, and parsing would have failed if the user forgot it
println!("Using input file: {}", matches.value_of("input").unwrap());
// We could continue checking for and using arguments in this manner, such as "license",
// "output", and "interface". Keep in mind that "output" and "interface" are optional, so you
// shouldn't call .unwrap(). Instead, prefer using an 'if let' expression as we did with
// "config"
}

View file

@ -1,61 +0,0 @@
use clap::{App, Arg};
fn main() {
// Once all App settings (including all arguments) have been set, you call get_matches() which
// parses the string provided by the user, and returns all the valid matches to the ones you
// specified.
//
// You can then query the matches struct to get information about how the user ran the program
// at startup.
//
// For this example, let's assume you created an App which accepts three arguments (plus two
// generated by clap), a flag to display debugging information triggered with "-d" or
// "--debug" as well as an option argument which specifies a custom configuration file to use
// triggered with "-c file" or "--config file" or "--config=file" and finally a positional
// argument which is the input file we want to work with, this will be the only required
// argument.
let matches = App::new("MyApp")
.about("Parses an input file to do awesome things")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.arg(
Arg::new("debug")
.about("turn on debugging information")
.short('d')
.long("debug"),
)
.arg(
Arg::new("config")
.about("sets the config file to use")
.short('c')
.long("config")
.takes_value(true),
)
.arg(
Arg::new("input")
.about("the input file to use")
.index(1)
.required(true),
)
.get_matches();
// We can find out whether or not debugging was turned on
if matches.is_present("debug") {
println!("Debugging is turned on");
}
// If we wanted to do some custom initialization based off some configuration file
// provided by the user, we could get the file (A string of the file)
if let Some(ref file) = matches.value_of("config") {
println!("Using config file: {}", file);
}
// Because "input" is required we can safely call unwrap() because had the user NOT
// specified a value, clap would have explained the error the user, and exited.
println!(
"Doing real work with file: {}",
matches.value_of("input").unwrap()
);
// Continued program logic goes here...
}

View file

@ -1,53 +0,0 @@
use clap::{App, Arg};
fn main() {
// Of the three argument types, flags are the most simple. Flags are simple switches which can
// be either "on" or "off"
//
// clap also supports multiple occurrences of flags, the common example is "verbosity" where a
// user could want a little information with "-v" or tons of information with "-v -v" or "-vv"
let matches = App::new("MyApp")
// Regular App configuration goes here...
// We'll add a flag that represents an awesome meter...
//
// I'll explain each possible setting that "flags" accept. Keep in mind
// that you DO NOT need to set each of these for every flag, only the ones
// you want for your individual case.
.arg(
Arg::new("awesome")
.about("turns up the awesome") // Displayed when showing help info
.short('a') // Trigger this arg with "-a"
.long("awesome") // Trigger this arg with "--awesome"
.multiple_occurrences(true) // This flag should allow multiple
// occurrences such as "-aaa" or "-a -a"
.requires("config") // Says, "If the user uses -a, they MUST
// also use this other 'config' arg too"
// Can also specify a list using
// requires_all(Vec<&str>)
.conflicts_with("output"), // Opposite of requires(), says "if the
// user uses -a, they CANNOT use 'output'"
// also has a conflicts_with_all(Vec<&str>)
// and an exclusive(true)
)
.arg("-c, --config=[FILE] 'sets a custom config file'")
.arg("<output> 'sets an output file'")
.get_matches();
// We can find out whether or not awesome was used
if matches.is_present("awesome") {
println!("Awesomeness is turned on");
}
// If we set the multiple option of a flag we can check how many times the user specified
//
// Note: if we did not specify the multiple option, and the user used "awesome" we would get
// a 1 (no matter how many times they actually used it), or a 0 if they didn't use it at all
match matches.occurrences_of("awesome") {
0 => println!("Nothing is awesome"),
1 => println!("Some things are awesome"),
2 => println!("Lots of things are awesome"),
_ => println!("EVERYTHING is awesome!"),
}
// Continued program logic goes here...
}

View file

@ -1,54 +0,0 @@
use clap::{App, Arg};
fn main() {
// Positional arguments are those values after the program name which are not preceded by any
// identifier (such as "myapp some_file"). Positionals support many of the same options as
// flags, as well as a few additional ones.
let matches = App::new("MyApp")
// Regular App configuration goes here...
// We'll add two positional arguments, an input file, and a config file.
//
// I'll explain each possible setting that "positionals" accept. Keep in
// mind that you DO NOT need to set each of these for every flag, only the
// ones that apply to your individual case.
.arg(
Arg::new("input")
.about("the input file to use") // Displayed when showing help info
.index(1) // Set the order in which the user must
// specify this argument (Starts at 1)
.requires("config") // Says, "If the user uses "input", they MUST
// also use this other 'config' arg too"
// Can also specify a list using
// requires_all(Vec<&str>)
.conflicts_with("output") // Opposite of requires(), says "if the
// user uses -a, they CANNOT use 'output'"
// also has a conflicts_with_all(Vec<&str>)
// and an exclusive(true)
.required(true), // By default this argument MUST be present
// NOTE: mutual exclusions take precedence over
// required arguments
)
.arg(Arg::new("config").about("the config file to use").index(2)) // Note, we do not need to specify required(true)
// if we don't want to, because "input" already
// requires "config"
// Note, we also do not need to specify requires("input")
// because requires lists are automatically two-way
.arg(Arg::new("output").about("the output file to use").index(3))
.get_matches();
// We can find out whether or not "input" or "config" were used
if matches.is_present("input") {
println!("An input file was specified");
}
// We can also get the values for those arguments
if let Some(ref in_file) = matches.value_of("input") {
// It's safe to call unwrap() because of the required options we set above
println!(
"Doing work with {} and {}",
in_file,
matches.value_of("config").unwrap()
);
}
// Continued program logic goes here...
}

View file

@ -1,71 +0,0 @@
use clap::{App, Arg};
fn main() {
// Option arguments are those that take an additional value, such as "-c value". In clap they
// support three types of specification, those with short() as "-o some", or those with long()
// as "--option value" or "--option=value"
//
// Options also support a multiple setting, which is discussed in the example below.
let matches = App::new("MyApp")
// Regular App configuration goes here...
// Assume we have an application that accepts an input file via the "-i file"
// or the "--input file" (as well as "--input=file").
// Below every setting supported by option arguments is discussed.
// NOTE: You DO NOT need to specify each setting, only those which apply
// to your particular case.
.arg(
Arg::new("input")
.about("the input file to use") // Displayed when showing help info
.takes_value(true) // MUST be set to true in order to be an "option" argument
.short('i') // This argument is triggered with "-i"
.long("input") // This argument is triggered with "--input"
.multiple_occurrences(true) // Set to true if you wish to allow multiple occurrences
// such as "-i file -i other_file -i third_file"
.required(true) // By default this argument MUST be present
// NOTE: mutual exclusions take precedence over
// required arguments
.requires("config") // Says, "If the user uses "input", they MUST
// also use this other 'config' arg too"
// Can also specify a list using
// requires_all(Vec<&str>)
.conflicts_with("output"), // Opposite of requires(), says "if the
// user uses -a, they CANNOT use 'output'"
// also has a conflicts_with_all(Vec<&str>)
// and an exclusive(true)
)
.arg("-c, --config=[FILE] 'the config file to use'")
.arg("<output> 'the output file to use'")
.get_matches();
// We can find out whether or not "input" was used
if matches.is_present("input") {
println!("An input file was specified");
}
// We can also get the value for "input"
//
// NOTE: If we specified multiple_occurrences(), this will only return the _FIRST_
// occurrence
if let Some(ref in_file) = matches.value_of("input") {
println!("An input file: {}", in_file);
}
// If we specified the multiple_occurrences() setting we can get all the values
if let Some(in_v) = matches.values_of("input") {
for in_file in in_v {
println!("An input file: {}", in_file);
}
}
// We can see how many times the option was used with the occurrences_of() method
//
// NOTE: Just like with flags, if we did not specify the multiple_occurrences() setting this will only
// return 1 no matter how many times the argument was used (unless it wasn't used at all, in
// in which case 0 is returned)
println!(
"The \"input\" argument was used {} times",
matches.occurrences_of("input")
);
// Continued program logic goes here...
}

View file

@ -1,57 +0,0 @@
use clap::{App, Arg};
fn main() {
// Subcommands function exactly like sub-Apps, because that's exactly what they are. Each
// instance of a Subcommand can have its own version, author(s), Args, and even its own
// subcommands.
//
// # Help and Version
// Just like Apps, each subcommand will get its own "help" and "version" flags automatically
// generated. Also, like Apps, you can override "-V" or "-h" safely and still get "--help" and
// "--version" auto generated.
//
// NOTE: If you specify a subcommand for your App, clap will also autogenerate a "help"
// subcommand along with "-h" and "--help" (applies to sub-subcommands as well).
//
// Just like arg() and args(), subcommands can be specified one at a time via subcommand() or
// multiple ones at once with a Vec<App> provided to subcommands().
let matches = App::new("MyApp")
// Normal App and Arg configuration goes here...
// In the following example assume we wanted an application which
// supported an "add" subcommand, this "add" subcommand also took
// one positional argument of a file to add:
.subcommand(
App::new("add") // The name we call argument with
.about("Adds files to myapp") // The message displayed in "myapp -h"
// or "myapp help"
.version("0.1") // Subcommands can have independent version
.author("Kevin K.") // And authors
.arg(
Arg::new("input") // And their own arguments
.about("the file to add")
.index(1)
.required(true),
),
)
.get_matches();
// You can check if a subcommand was used like normal
if matches.is_present("add") {
println!("'myapp add' was run.");
}
// You can get the independent subcommand matches (which function exactly like App matches)
if let Some(matches) = matches.subcommand_matches("add") {
// Safe to use unwrap() because of the required() option
println!("Adding file: {}", matches.value_of("input").unwrap());
}
// You can also match on a subcommand's name
match matches.subcommand_name() {
Some("add") => println!("'myapp add' was used"),
None => println!("No subcommand was used"),
_ => println!("Some other subcommand was used"),
}
// Continued program logic goes here...
}

View file

@ -1,26 +0,0 @@
use clap::{crate_version, App};
fn main() {
// You can have clap pull the application version directly from your Cargo.toml starting with
// clap v0.4.14 on crates.io (or master#a81f915 on github). Using Rust's env! macro like this:
//
// let version = format!("{}.{}.{}{}",
// env!("CARGO_PKG_VERSION_MAJOR"),
// env!("CARGO_PKG_VERSION_MINOR"),
// env!("CARGO_PKG_VERSION_PATCH"),
// option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""));
//
// Starting from v0.6.6 on crates.io you can also use the crate_version!() macro instead of
// manually using the env!() macros. Under the hood, the macro uses this exact method to get
// the version.
//
// Thanks to https://github.com/jhelwig for pointing this out
App::new("myapp")
.about("does awesome things")
// use crate_version! to pull the version number
.version(crate_version!())
.get_matches();
// running this app with the -V or --version will display whatever version is in your
// Cargo.toml, the default being: myapp 0.0.1
}

View file

@ -1,42 +0,0 @@
use clap::{App, Arg};
fn main() {
// There are two ways in which to get a default value, one is to use clap's Arg::default_value
// method, and the other is to use Rust's built in Option::unwrap_or method.
//
// I'll demo both here.
//
// First, we'll use clap's Arg::default_value with an "INPUT" file.
let matches = App::new("myapp")
.about("does awesome things")
.arg(
Arg::new("INPUT")
.about("The input file to use") // Note, we don't need to specify
// anything like, "Defaults to..."
// because clap will automatically
// generate that for us, and place
// it in the help text
.default_value("input.txt")
.index(1),
)
// Next we'll use the Option::unwrap_or method on this "CONFIG" option
.arg(
Arg::new("CONFIG")
// Note that we have to manually include some verbage to the user
// telling them what the default will be.
.about("The config file to use (default is \"config.json\")")
.short('c')
.takes_value(true),
)
.get_matches();
// It's safe to call unwrap because the value will either be what the user input at runtime
// or "input.txt"
let input = matches.value_of("INPUT").unwrap();
// Using Option::unwrap_or we get the same effect, but without the added help text injection
let config_file = matches.value_of("CONFIG").unwrap_or("config.json");
println!("The input file is: {}", input);
println!("The config file is: {}", config_file);
}

View file

@ -1,34 +0,0 @@
use clap::{App, Arg};
fn main() {
// If you have arguments of specific values you want to test for, you can use the
// .possible_values() method of Arg
//
// This allows you specify the valid values for that argument. If the user does not use one of
// those specific values, they will receive a graceful exit with error message informing them
// of the mistake, and what the possible valid values are
//
// For this example, assume you want one positional argument of either "fast" or "slow"
// i.e. the only possible ways to run the program are "myprog fast" or "myprog slow"
let matches = App::new("myapp")
.about("does awesome things")
.arg(
Arg::new("MODE")
.about("What mode to run the program in")
.index(1)
.possible_values(["fast", "slow"])
.required(true),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
match matches.value_of("MODE").unwrap() {
"fast" => {
// Do fast things...
}
"slow" => {
// Do slow things...
}
_ => unreachable!(),
}
}

View file

@ -1,46 +0,0 @@
use clap::App;
fn main() {
// You can use some convenience methods provided by clap to get typed values, so long as the
// type you're converting into implements std::str::FromStr
//
// This works for both single, and multiple values (multiple values returns a Vec<T>)
//
// There are also two ways in which to get types, those where failures cause the program to exit
// with an error and usage string, and those which return a Result<T,String> or Result<Vec<T>,String>
// respectively. Both methods support single and multiple values.
//
// The method which returns a Result allows you decide what to do upon a failure, exit, provide a
// default value, etc. You have control. But it also means you have to write the code or boiler plate
// to handle those instances.
//
// That is why the second method exists, so you can simply get a T or Vec<T> back, or be sure the
// program will exit gracefully. The catch is, the second method should *only* be used on required
// arguments, because if the argument isn't found, it exits. Just FYI ;)
//
// The following example shows both methods.
let matches = App::new("myapp")
// Create two arguments, a required positional which accepts multiple values
// and an optional '-l value'
.arg("<seq>... 'A sequence of whole positive numbers, i.e. 20 25 30'")
.arg("-l [len] 'A length to use, defaults to 10 when omitted'")
.get_matches();
// Here we get a value of type u32 from our optional -l argument.
// If the value provided to len fails to parse or not present, we default to 10
//
// Using other methods such as unwrap_or_else(|e| println!("{}",e))
// are possible too.
let len: u32 = matches.value_of_t("len").unwrap_or(10);
println!("len ({}) + 2 = {}", len, len + 2);
// This code loops through all the values provided to "seq" and adds 2
// If seq fails to parse, the program exits, you don't have an option
for v in matches
.values_of_t::<u32>("seq")
.unwrap_or_else(|e| e.exit())
{
println!("Sequence part {} + 2: {}", v, v + 2);
}
}

View file

@ -1,54 +0,0 @@
// In the following example we will create an enum with 4 values, assign a positional argument
// that accepts only one of those values, and use clap to parse the argument.
//
// Start with bringing the trait into scope.
use std::str::FromStr;
// Add clap like normal
use clap::{App, Arg};
// Define your enum
enum Vals {
Foo,
Bar,
Baz,
Qux,
}
// Implement the trait
impl FromStr for Vals {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Foo" => Ok(Vals::Foo),
"Bar" => Ok(Vals::Bar),
"Baz" => Ok(Vals::Baz),
"Qux" => Ok(Vals::Qux),
_ => Err("no match"),
}
}
}
fn main() {
// Create the application like normal
let m = App::new("myapp")
// Use a single positional argument that is required
.arg(
Arg::from("<type> 'The type to use'")
// Define the list of possible values
.possible_values(["Foo", "Bar", "Baz", "Qux"]),
)
.get_matches();
// Note that you don't have to specify the type since rustc can infer it for you
let t = m.value_of_t("type").unwrap_or_else(|e| e.exit());
// Now we can use our enum like normal.
match t {
Vals::Foo => println!("Found a Foo"),
Vals::Bar => println!("Found a Bar"),
Vals::Baz => println!("Found a Baz"),
Vals::Qux => println!("Found a Qux"),
}
}

Some files were not shown because too many files have changed in this diff Show more