mirror of
https://github.com/clap-rs/clap
synced 2024-11-10 23:04:23 +00:00
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:
commit
7a750a3066
385 changed files with 14935 additions and 15912 deletions
45
.github/ISSUE_TEMPLATE.md
vendored
45
.github/ISSUE_TEMPLATE.md
vendored
|
@ -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>
|
33
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
33
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -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
|
||||
|
|
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -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)
|
||||
|
|
34
.github/ISSUE_TEMPLATE/documentation_issue.yml
vendored
34
.github/ISSUE_TEMPLATE/documentation_issue.yml
vendored
|
@ -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!".
|
32
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
32
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
@ -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.
|
||||
|
|
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -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
10
.github/dependabot.yml
vendored
Normal 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
104
.github/settings.yml
vendored
Normal 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
21
.github/workflows/audit.yml
vendored
Normal 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 }}
|
87
.github/workflows/ci-pr.yml
vendored
87
.github/workflows/ci-pr.yml
vendored
|
@ -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
|
306
.github/workflows/ci.yml
vendored
306
.github/workflows/ci.yml
vendored
|
@ -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
14
.github/workflows/committed.yml
vendored
Normal 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
|
39
.github/workflows/coverage.yml
vendored
39
.github/workflows/coverage.yml
vendored
|
@ -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 }}
|
40
.github/workflows/lint.yml
vendored
40
.github/workflows/lint.yml
vendored
|
@ -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
12
.github/workflows/pre-commit.yml
vendored
Normal 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
106
.github/workflows/rust-next.yml
vendored
Normal 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
|
28
.github/workflows/site.yml
vendored
28
.github/workflows/site.yml
vendored
|
@ -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
12
.github/workflows/spelling.yml
vendored
Normal 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
35
.gitignore
vendored
|
@ -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
21
.pre-commit-config.yaml
Normal 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]
|
2087
CHANGELOG.md
2087
CHANGELOG.md
File diff suppressed because it is too large
Load diff
272
Cargo.toml
272
Cargo.toml
|
@ -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
90
FAQ.md
|
@ -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
28
Makefile
Normal 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
571
README.md
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
11
bors.toml
11
bors.toml
|
@ -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"]
|
|
@ -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 = []
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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());
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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());
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>>{
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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/)
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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<_>>()
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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("]", "\\]")
|
||||
|
|
|
@ -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")))
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
|
@ -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"]
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -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
|
||||
));
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
||||
|
|
|
@ -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"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -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
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
||||
```
|
|
@ -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
3
committed.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
style="conventional"
|
||||
ignore_author_re="dependabot"
|
||||
merge_commit = false
|
|
@ -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
73
docs/FAQ.md
Normal 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).
|
||||
|
|
@ -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...
|
||||
}
|
|
@ -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...
|
||||
}
|
|
@ -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...
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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...
|
||||
}
|
|
@ -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...
|
||||
}
|
|
@ -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...
|
||||
}
|
|
@ -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...
|
||||
}
|
|
@ -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...
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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!(),
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in a new issue