Bug 1751342 - Upgrade geckodriver to clap 3. r=webdriver-reviewers,jgraham

Differential Revision: https://phabricator.services.mozilla.com/D136569
This commit is contained in:
Mike Hommey 2022-01-22 21:45:34 +00:00
parent ea3ded4926
commit 29c3f32ae6
224 changed files with 37661 additions and 26333 deletions

52
Cargo.lock generated
View File

@ -726,15 +726,17 @@ dependencies = [
[[package]]
name = "clap"
version = "2.34.0"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
checksum = "7a30c3bf9ff12dfe5dae53f0a96e0febcd18420d1c0e7fad77796d9d5c4b5375"
dependencies = [
"bitflags",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim",
"term_size",
"terminal_size",
"textwrap",
"unicode-width",
]
[[package]]
@ -3669,6 +3671,15 @@ dependencies = [
"num-traits",
]
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "osclientcerts-static"
version = "0.1.4"
@ -4780,9 +4791,9 @@ dependencies = [
[[package]]
name = "strsim"
version = "0.8.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "style"
@ -4978,16 +4989,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "term_size"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.2"
@ -4998,13 +4999,22 @@ dependencies = [
]
[[package]]
name = "textwrap"
version = "0.11.0"
name = "terminal_size"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"term_size",
"unicode-width",
"libc",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
dependencies = [
"terminal_size",
]
[[package]]

View File

@ -12,7 +12,7 @@ edition = "2018"
[dependencies]
base64 = "0.12"
chrono = "0.4.6"
clap = { version = "^2.19", default-features = false, features = ["suggestions", "wrap_help"] }
clap = { version = "3", default-features = false, features = ["cargo", "std", "suggestions", "wrap_help"] }
hyper = "0.13"
lazy_static = "1.0"
log = { version = "0.4", features = ["std"] }

View File

@ -33,7 +33,7 @@ use std::path::PathBuf;
use std::result;
use std::str::FromStr;
use clap::{App, Arg};
use clap::{App, AppSettings, Arg};
macro_rules! try_opt {
($expr:expr, $err_type:expr, $err_msg:expr) => {{
@ -233,7 +233,7 @@ fn get_allowed_origins(allow_origins: Option<clap::Values>) -> Result<Vec<Url>,
}
fn parse_args(app: &mut App) -> ProgramResult<Operation> {
let args = app.get_matches_from_safe_borrow(env::args())?;
let args = app.try_get_matches_from_mut(env::args())?;
if args.is_present("help") {
return Ok(Operation::Help);
@ -261,7 +261,7 @@ fn parse_args(app: &mut App) -> ProgramResult<Operation> {
};
let android_storage =
value_t!(args, "android_storage", AndroidStorageInput).unwrap_or(AndroidStorageInput::Auto);
args.value_of_t::<AndroidStorageInput>("android_storage").unwrap_or(AndroidStorageInput::Auto);
let binary = args.value_of("binary").map(PathBuf::from);
@ -378,11 +378,13 @@ fn main() {
});
}
fn make_app<'a, 'b>() -> App<'a, 'b> {
fn make_app<'a>() -> App<'a> {
App::new(format!("geckodriver {}", build::build_info()))
.setting(AppSettings::NoAutoHelp)
.setting(AppSettings::NoAutoVersion)
.about("WebDriver implementation for Firefox")
.arg(
Arg::with_name("webdriver_host")
Arg::new("webdriver_host")
.long("host")
.takes_value(true)
.value_name("HOST")
@ -390,8 +392,8 @@ fn make_app<'a, 'b>() -> App<'a, 'b> {
.help("Host IP to use for WebDriver server"),
)
.arg(
Arg::with_name("webdriver_port")
.short("p")
Arg::new("webdriver_port")
.short('p')
.long("port")
.takes_value(true)
.value_name("PORT")
@ -399,15 +401,15 @@ fn make_app<'a, 'b>() -> App<'a, 'b> {
.help("Port to use for WebDriver server"),
)
.arg(
Arg::with_name("binary")
.short("b")
Arg::new("binary")
.short('b')
.long("binary")
.takes_value(true)
.value_name("BINARY")
.help("Path to the Firefox binary"),
)
.arg(
Arg::with_name("marionette_host")
Arg::new("marionette_host")
.long("marionette-host")
.takes_value(true)
.value_name("HOST")
@ -415,14 +417,14 @@ fn make_app<'a, 'b>() -> App<'a, 'b> {
.help("Host to use to connect to Gecko"),
)
.arg(
Arg::with_name("marionette_port")
Arg::new("marionette_port")
.long("marionette-port")
.takes_value(true)
.value_name("PORT")
.help("Port to use to connect to Gecko [default: system-allocated port]"),
)
.arg(
Arg::with_name("websocket_port")
Arg::new("websocket_port")
.long("websocket-port")
.takes_value(true)
.value_name("PORT")
@ -430,25 +432,25 @@ fn make_app<'a, 'b>() -> App<'a, 'b> {
.help("Port to use to connect to WebDriver BiDi [default: 9222]"),
)
.arg(
Arg::with_name("connect_existing")
Arg::new("connect_existing")
.long("connect-existing")
.requires("marionette_port")
.help("Connect to an existing Firefox instance"),
)
.arg(
Arg::with_name("jsdebugger")
Arg::new("jsdebugger")
.long("jsdebugger")
.help("Attach browser toolbox debugger for Firefox"),
)
.arg(
Arg::with_name("verbosity")
.multiple(true)
Arg::new("verbosity")
.multiple_occurrences(true)
.conflicts_with("log_level")
.short("v")
.short('v')
.help("Log level verbosity (-v for debug and -vv for trace level)"),
)
.arg(
Arg::with_name("log_level")
Arg::new("log_level")
.long("log")
.takes_value(true)
.value_name("LEVEL")
@ -456,37 +458,37 @@ fn make_app<'a, 'b>() -> App<'a, 'b> {
.help("Set Gecko log level"),
)
.arg(
Arg::with_name("help")
.short("h")
Arg::new("help")
.short('h')
.long("help")
.help("Prints this message"),
)
.arg(
Arg::with_name("version")
.short("V")
Arg::new("version")
.short('V')
.long("version")
.help("Prints version and copying information"),
)
.arg(
Arg::with_name("android_storage")
Arg::new("android_storage")
.long("android-storage")
.possible_values(&["auto", "app", "internal", "sdcard"])
.value_name("ANDROID_STORAGE")
.help("Selects storage location to be used for test data (deprecated)."),
)
.arg(
Arg::with_name("allow_hosts")
Arg::new("allow_hosts")
.long("allow-hosts")
.takes_value(true)
.multiple(true)
.multiple_values(true)
.value_name("ALLOW_HOSTS")
.help("List of hostnames to allow. By default the value of --host is allowed, and in addition if that's a well known local address, other variations on well known local addresses are allowed. If --allow-hosts is provided only exactly those hosts are allowed."),
)
.arg(
Arg::with_name("allow_origins")
Arg::new("allow_origins")
.long("allow-origins")
.takes_value(true)
.multiple(true)
.multiple_values(true)
.value_name("ALLOW_ORIGINS")
.help("List of request origins to allow. These must be formatted as scheme://host:port. By default any request with an origin header is rejected. If --allow-origins is provided then only exactly those origins are allowed."),
)

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,91 +0,0 @@
the following is a list of contributors:
[<img alt="kbknapp" src="https://avatars1.githubusercontent.com/u/6942134?v=4&s=117" width="117">](https://github.com/kbknapp) |[<img alt="homu" src="https://avatars1.githubusercontent.com/u/10212162?v=4&s=117" width="117">](https://github.com/homu) |[<img alt="Vinatorul" src="https://avatars1.githubusercontent.com/u/6770624?v=4&s=117" width="117">](https://github.com/Vinatorul) |[<img alt="tormol" src="https://avatars3.githubusercontent.com/u/10460821?v=4&s=117" width="117">](https://github.com/tormol) |[<img alt="willmurphyscode" src="https://avatars3.githubusercontent.com/u/12529630?v=4&s=117" width="117">](https://github.com/willmurphyscode) |[<img alt="little-dude" src="https://avatars2.githubusercontent.com/u/6646324?v=4&s=117" width="117">](https://github.com/little-dude) |
:---: |:---: |:---: |:---: |:---: |:---: |
[kbknapp](https://github.com/kbknapp) |[homu](https://github.com/homu) |[Vinatorul](https://github.com/Vinatorul) |[tormol](https://github.com/tormol) |[willmurphyscode](https://github.com/willmurphyscode) |[little-dude](https://github.com/little-dude) |
[<img alt="sru" src="https://avatars3.githubusercontent.com/u/2485892?v=4&s=117" width="117">](https://github.com/sru) |[<img alt="mgeisler" src="https://avatars0.githubusercontent.com/u/89623?v=4&s=117" width="117">](https://github.com/mgeisler) |[<img alt="nabijaczleweli" src="https://avatars3.githubusercontent.com/u/6709544?v=4&s=117" width="117">](https://github.com/nabijaczleweli) |[<img alt="Byron" src="https://avatars2.githubusercontent.com/u/63622?v=4&s=117" width="117">](https://github.com/Byron) |[<img alt="hgrecco" src="https://avatars0.githubusercontent.com/u/278566?v=4&s=117" width="117">](https://github.com/hgrecco) |[<img alt="bluejekyll" src="https://avatars3.githubusercontent.com/u/986845?v=4&s=117" width="117">](https://github.com/bluejekyll) |
:---: |:---: |:---: |:---: |:---: |:---: |
[sru](https://github.com/sru) |[mgeisler](https://github.com/mgeisler) |[nabijaczleweli](https://github.com/nabijaczleweli) |[Byron](https://github.com/Byron) |[hgrecco](https://github.com/hgrecco) |[bluejekyll](https://github.com/bluejekyll) |
[<img alt="segevfiner" src="https://avatars0.githubusercontent.com/u/24731903?v=4&s=117" width="117">](https://github.com/segevfiner) |[<img alt="ignatenkobrain" src="https://avatars1.githubusercontent.com/u/2866862?v=4&s=117" width="117">](https://github.com/ignatenkobrain) |[<img alt="james-darkfox" src="https://avatars3.githubusercontent.com/u/637155?v=4&s=117" width="117">](https://github.com/james-darkfox) |[<img alt="H2CO3" src="https://avatars2.githubusercontent.com/u/742370?v=4&s=117" width="117">](https://github.com/H2CO3) |[<img alt="nateozem" src="https://avatars2.githubusercontent.com/u/22719441?v=4&s=117" width="117">](https://github.com/nateozem) |[<img alt="glowing-chemist" src="https://avatars0.githubusercontent.com/u/17074682?v=4&s=117" width="117">](https://github.com/glowing-chemist) |
:---: |:---: |:---: |:---: |:---: |:---: |
[segevfiner](https://github.com/segevfiner) |[ignatenkobrain](https://github.com/ignatenkobrain) |[james-darkfox](https://github.com/james-darkfox) |[H2CO3](https://github.com/H2CO3) |[nateozem](https://github.com/nateozem) |[glowing-chemist](https://github.com/glowing-chemist) |
[<img alt="discosultan" src="https://avatars1.githubusercontent.com/u/2970736?v=4&s=117" width="117">](https://github.com/discosultan) |[<img alt="rtaycher" src="https://avatars0.githubusercontent.com/u/324733?v=4&s=117" width="117">](https://github.com/rtaycher) |[<img alt="Arnavion" src="https://avatars2.githubusercontent.com/u/1096010?v=4&s=117" width="117">](https://github.com/Arnavion) |[<img alt="japaric" src="https://avatars3.githubusercontent.com/u/5018213?v=4&s=117" width="117">](https://github.com/japaric) |[<img alt="untitaker" src="https://avatars0.githubusercontent.com/u/837573?v=4&s=117" width="117">](https://github.com/untitaker) |[<img alt="afiune" src="https://avatars0.githubusercontent.com/u/5712253?v=4&s=117" width="117">](https://github.com/afiune) |
:---: |:---: |:---: |:---: |:---: |:---: |
[discosultan](https://github.com/discosultan) |[rtaycher](https://github.com/rtaycher) |[Arnavion](https://github.com/Arnavion) |[japaric](https://github.com/japaric) |[untitaker](https://github.com/untitaker) |[afiune](https://github.com/afiune) |
[<img alt="crazymerlyn" src="https://avatars1.githubusercontent.com/u/6919679?v=4&s=117" width="117">](https://github.com/crazymerlyn) |[<img alt="SuperFluffy" src="https://avatars0.githubusercontent.com/u/701177?v=4&s=117" width="117">](https://github.com/SuperFluffy) |[<img alt="matthiasbeyer" src="https://avatars0.githubusercontent.com/u/427866?v=4&s=117" width="117">](https://github.com/matthiasbeyer) |[<img alt="malbarbo" src="https://avatars3.githubusercontent.com/u/1678126?v=4&s=117" width="117">](https://github.com/malbarbo) |[<img alt="tshepang" src="https://avatars0.githubusercontent.com/u/588486?v=4&s=117" width="117">](https://github.com/tshepang) |[<img alt="golem131" src="https://avatars3.githubusercontent.com/u/2429587?v=4&s=117" width="117">](https://github.com/golem131) |
:---: |:---: |:---: |:---: |:---: |:---: |
[crazymerlyn](https://github.com/crazymerlyn) |[SuperFluffy](https://github.com/SuperFluffy) |[matthiasbeyer](https://github.com/matthiasbeyer) |[malbarbo](https://github.com/malbarbo) |[tshepang](https://github.com/tshepang) |[golem131](https://github.com/golem131) |
[<img alt="jimmycuadra" src="https://avatars2.githubusercontent.com/u/122457?v=4&s=117" width="117">](https://github.com/jimmycuadra) |[<img alt="Nemo157" src="https://avatars1.githubusercontent.com/u/81079?v=4&s=117" width="117">](https://github.com/Nemo157) |[<img alt="severen" src="https://avatars1.githubusercontent.com/u/4061736?v=4&s=117" width="117">](https://github.com/severen) |[<img alt="Eijebong" src="https://avatars2.githubusercontent.com/u/3650385?v=4&s=117" width="117">](https://github.com/Eijebong) |[<img alt="cstorey" src="https://avatars3.githubusercontent.com/u/743059?v=4&s=117" width="117">](https://github.com/cstorey) |[<img alt="wdv4758h" src="https://avatars1.githubusercontent.com/u/2716047?v=4&s=117" width="117">](https://github.com/wdv4758h) |
:---: |:---: |:---: |:---: |:---: |:---: |
[jimmycuadra](https://github.com/jimmycuadra) |[Nemo157](https://github.com/Nemo157) |[severen](https://github.com/severen) |[Eijebong](https://github.com/Eijebong) |[cstorey](https://github.com/cstorey) |[wdv4758h](https://github.com/wdv4758h) |
[<img alt="frewsxcv" src="https://avatars2.githubusercontent.com/u/416575?v=4&s=117" width="117">](https://github.com/frewsxcv) |[<img alt="hoodie" src="https://avatars1.githubusercontent.com/u/260370?v=4&s=117" width="117">](https://github.com/hoodie) |[<img alt="huonw" src="https://avatars1.githubusercontent.com/u/1203825?v=4&s=117" width="117">](https://github.com/huonw) |[<img alt="GrappigPanda" src="https://avatars0.githubusercontent.com/u/2055372?v=4&s=117" width="117">](https://github.com/GrappigPanda) |[<img alt="shepmaster" src="https://avatars0.githubusercontent.com/u/174509?v=4&s=117" width="117">](https://github.com/shepmaster) |[<img alt="starkat99" src="https://avatars1.githubusercontent.com/u/8295111?v=4&s=117" width="117">](https://github.com/starkat99) |
:---: |:---: |:---: |:---: |:---: |:---: |
[frewsxcv](https://github.com/frewsxcv) |[hoodie](https://github.com/hoodie) |[huonw](https://github.com/huonw) |[GrappigPanda](https://github.com/GrappigPanda) |[shepmaster](https://github.com/shepmaster) |[starkat99](https://github.com/starkat99) |
[<img alt="porglezomp" src="https://avatars1.githubusercontent.com/u/1690225?v=4&s=117" width="117">](https://github.com/porglezomp) |[<img alt="kraai" src="https://avatars1.githubusercontent.com/u/552646?v=4&s=117" width="117">](https://github.com/kraai) |[<img alt="musoke" src="https://avatars0.githubusercontent.com/u/16665084?v=4&s=117" width="117">](https://github.com/musoke) |[<img alt="nelsonjchen" src="https://avatars1.githubusercontent.com/u/5363?v=4&s=117" width="117">](https://github.com/nelsonjchen) |[<img alt="pkgw" src="https://avatars0.githubusercontent.com/u/59598?v=4&s=117" width="117">](https://github.com/pkgw) |[<img alt="Deedasmi" src="https://avatars0.githubusercontent.com/u/5093293?v=4&s=117" width="117">](https://github.com/Deedasmi) |
:---: |:---: |:---: |:---: |:---: |:---: |
[porglezomp](https://github.com/porglezomp) |[kraai](https://github.com/kraai) |[musoke](https://github.com/musoke) |[nelsonjchen](https://github.com/nelsonjchen) |[pkgw](https://github.com/pkgw) |[Deedasmi](https://github.com/Deedasmi) |
[<img alt="vmchale" src="https://avatars1.githubusercontent.com/u/13259982?v=4&s=117" width="117">](https://github.com/vmchale) |[<img alt="etopiei" src="https://avatars3.githubusercontent.com/u/17671663?v=4&s=117" width="117">](https://github.com/etopiei) |[<img alt="messense" src="https://avatars0.githubusercontent.com/u/1556054?v=4&s=117" width="117">](https://github.com/messense) |[<img alt="Keats" src="https://avatars2.githubusercontent.com/u/680355?v=4&s=117" width="117">](https://github.com/Keats) |[<img alt="kieraneglin" src="https://avatars0.githubusercontent.com/u/569917?v=4&s=117" width="117">](https://github.com/kieraneglin) |[<img alt="durka" src="https://avatars3.githubusercontent.com/u/47007?v=4&s=117" width="117">](https://github.com/durka) |
:---: |:---: |:---: |:---: |:---: |:---: |
[vmchale](https://github.com/vmchale) |[etopiei](https://github.com/etopiei) |[messense](https://github.com/messense) |[Keats](https://github.com/Keats) |[kieraneglin](https://github.com/kieraneglin) |[durka](https://github.com/durka) |
[<img alt="alex-gulyas" src="https://avatars0.githubusercontent.com/u/8698329?v=4&s=117" width="117">](https://github.com/alex-gulyas) |[<img alt="cite-reader" src="https://avatars1.githubusercontent.com/u/4196987?v=4&s=117" width="117">](https://github.com/cite-reader) |[<img alt="alexbool" src="https://avatars3.githubusercontent.com/u/1283792?v=4&s=117" width="117">](https://github.com/alexbool) |[<img alt="AluisioASG" src="https://avatars2.githubusercontent.com/u/1904165?v=4&s=117" width="117">](https://github.com/AluisioASG) |[<img alt="BurntSushi" src="https://avatars3.githubusercontent.com/u/456674?v=4&s=117" width="117">](https://github.com/BurntSushi) |[<img alt="AndrewGaspar" src="https://avatars1.githubusercontent.com/u/2292643?v=4&s=117" width="117">](https://github.com/AndrewGaspar) |
:---: |:---: |:---: |:---: |:---: |:---: |
[alex-gulyas](https://github.com/alex-gulyas) |[cite-reader](https://github.com/cite-reader) |[alexbool](https://github.com/alexbool) |[AluisioASG](https://github.com/AluisioASG) |[BurntSushi](https://github.com/BurntSushi) |[AndrewGaspar](https://github.com/AndrewGaspar) |
[<img alt="nox" src="https://avatars0.githubusercontent.com/u/123095?v=4&s=117" width="117">](https://github.com/nox) |[<img alt="mitsuhiko" src="https://avatars1.githubusercontent.com/u/7396?v=4&s=117" width="117">](https://github.com/mitsuhiko) |[<img alt="pixelistik" src="https://avatars1.githubusercontent.com/u/170929?v=4&s=117" width="117">](https://github.com/pixelistik) |[<img alt="ogham" src="https://avatars3.githubusercontent.com/u/503760?v=4&s=117" width="117">](https://github.com/ogham) |[<img alt="Bilalh" src="https://avatars0.githubusercontent.com/u/171602?v=4&s=117" width="117">](https://github.com/Bilalh) |[<img alt="dotdash" src="https://avatars1.githubusercontent.com/u/230962?v=4&s=117" width="117">](https://github.com/dotdash) |
:---: |:---: |:---: |:---: |:---: |:---: |
[nox](https://github.com/nox) |[mitsuhiko](https://github.com/mitsuhiko) |[pixelistik](https://github.com/pixelistik) |[ogham](https://github.com/ogham) |[Bilalh](https://github.com/Bilalh) |[dotdash](https://github.com/dotdash) |
[<img alt="bradurani" src="https://avatars0.githubusercontent.com/u/4195952?v=4&s=117" width="117">](https://github.com/bradurani) |[<img alt="Seeker14491" src="https://avatars2.githubusercontent.com/u/6490497?v=4&s=117" width="117">](https://github.com/Seeker14491) |[<img alt="brianp" src="https://avatars1.githubusercontent.com/u/179134?v=4&s=117" width="117">](https://github.com/brianp) |[<img alt="cldershem" src="https://avatars3.githubusercontent.com/u/201608?v=4&s=117" width="117">](https://github.com/cldershem) |[<img alt="casey" src="https://avatars2.githubusercontent.com/u/1945?v=4&s=117" width="117">](https://github.com/casey) |[<img alt="volks73" src="https://avatars1.githubusercontent.com/u/1915469?v=4&s=117" width="117">](https://github.com/volks73) |
:---: |:---: |:---: |:---: |:---: |:---: |
[bradurani](https://github.com/bradurani) |[Seeker14491](https://github.com/Seeker14491) |[brianp](https://github.com/brianp) |[cldershem](https://github.com/cldershem) |[casey](https://github.com/casey) |[volks73](https://github.com/volks73) |
[<img alt="daboross" src="https://avatars1.githubusercontent.com/u/1152146?v=4&s=117" width="117">](https://github.com/daboross) |[<img alt="da-x" src="https://avatars1.githubusercontent.com/u/321273?v=4&s=117" width="117">](https://github.com/da-x) |[<img alt="mernen" src="https://avatars0.githubusercontent.com/u/6412?v=4&s=117" width="117">](https://github.com/mernen) |[<img alt="dguo" src="https://avatars0.githubusercontent.com/u/2763135?v=4&s=117" width="117">](https://github.com/dguo) |[<img alt="davidszotten" src="https://avatars3.githubusercontent.com/u/412005?v=4&s=117" width="117">](https://github.com/davidszotten) |[<img alt="drusellers" src="https://avatars1.githubusercontent.com/u/63355?v=4&s=117" width="117">](https://github.com/drusellers) |
:---: |:---: |:---: |:---: |:---: |:---: |
[daboross](https://github.com/daboross) |[da-x](https://github.com/da-x) |[mernen](https://github.com/mernen) |[dguo](https://github.com/dguo) |[davidszotten](https://github.com/davidszotten) |[drusellers](https://github.com/drusellers) |
[<img alt="eddyb" src="https://avatars2.githubusercontent.com/u/77424?v=4&s=117" width="117">](https://github.com/eddyb) |[<img alt="Enet4" src="https://avatars0.githubusercontent.com/u/4738426?v=4&s=117" width="117">](https://github.com/Enet4) |[<img alt="Fraser999" src="https://avatars3.githubusercontent.com/u/190532?v=4&s=117" width="117">](https://github.com/Fraser999) |[<img alt="birkenfeld" src="https://avatars0.githubusercontent.com/u/144359?v=4&s=117" width="117">](https://github.com/birkenfeld) |[<img alt="guanqun" src="https://avatars0.githubusercontent.com/u/53862?v=4&s=117" width="117">](https://github.com/guanqun) |[<img alt="tanakh" src="https://avatars2.githubusercontent.com/u/109069?v=4&s=117" width="117">](https://github.com/tanakh) |
:---: |:---: |:---: |:---: |:---: |:---: |
[eddyb](https://github.com/eddyb) |[Enet4](https://github.com/Enet4) |[Fraser999](https://github.com/Fraser999) |[birkenfeld](https://github.com/birkenfeld) |[guanqun](https://github.com/guanqun) |[tanakh](https://github.com/tanakh) |
[<img alt="SirVer" src="https://avatars0.githubusercontent.com/u/140115?v=4&s=117" width="117">](https://github.com/SirVer) |[<img alt="idmit" src="https://avatars1.githubusercontent.com/u/2546728?v=4&s=117" width="117">](https://github.com/idmit) |[<img alt="archer884" src="https://avatars1.githubusercontent.com/u/679494?v=4&s=117" width="117">](https://github.com/archer884) |[<img alt="jacobmischka" src="https://avatars1.githubusercontent.com/u/3939997?v=4&s=117" width="117">](https://github.com/jacobmischka) |[<img alt="jespino" src="https://avatars0.githubusercontent.com/u/290303?v=4&s=117" width="117">](https://github.com/jespino) |[<img alt="jfrankenau" src="https://avatars3.githubusercontent.com/u/2736480?v=4&s=117" width="117">](https://github.com/jfrankenau) |
:---: |:---: |:---: |:---: |:---: |:---: |
[SirVer](https://github.com/SirVer) |[idmit](https://github.com/idmit) |[archer884](https://github.com/archer884) |[jacobmischka](https://github.com/jacobmischka) |[jespino](https://github.com/jespino) |[jfrankenau](https://github.com/jfrankenau) |
[<img alt="jtdowney" src="https://avatars1.githubusercontent.com/u/44654?v=4&s=117" width="117">](https://github.com/jtdowney) |[<img alt="andete" src="https://avatars2.githubusercontent.com/u/689017?v=4&s=117" width="117">](https://github.com/andete) |[<img alt="joshtriplett" src="https://avatars2.githubusercontent.com/u/162737?v=4&s=117" width="117">](https://github.com/joshtriplett) |[<img alt="Kalwyn" src="https://avatars3.githubusercontent.com/u/22778640?v=4&s=117" width="117">](https://github.com/Kalwyn) |[<img alt="manuel-rhdt" src="https://avatars1.githubusercontent.com/u/3199013?v=4&s=117" width="117">](https://github.com/manuel-rhdt) |[<img alt="Marwes" src="https://avatars3.githubusercontent.com/u/957312?v=4&s=117" width="117">](https://github.com/Marwes) |
:---: |:---: |:---: |:---: |:---: |:---: |
[jtdowney](https://github.com/jtdowney) |[andete](https://github.com/andete) |[joshtriplett](https://github.com/joshtriplett) |[Kalwyn](https://github.com/Kalwyn) |[manuel-rhdt](https://github.com/manuel-rhdt) |[Marwes](https://github.com/Marwes) |
[<img alt="mdaffin" src="https://avatars1.githubusercontent.com/u/171232?v=4&s=117" width="117">](https://github.com/mdaffin) |[<img alt="iliekturtles" src="https://avatars3.githubusercontent.com/u/5081378?v=4&s=117" width="117">](https://github.com/iliekturtles) |[<img alt="nicompte" src="https://avatars2.githubusercontent.com/u/439369?v=4&s=117" width="117">](https://github.com/nicompte) |[<img alt="NickeZ" src="https://avatars2.githubusercontent.com/u/492753?v=4&s=117" width="117">](https://github.com/NickeZ) |[<img alt="nvzqz" src="https://avatars0.githubusercontent.com/u/10367662?v=4&s=117" width="117">](https://github.com/nvzqz) |[<img alt="nuew" src="https://avatars2.githubusercontent.com/u/26099511?v=4&s=117" width="117">](https://github.com/nuew) |
:---: |:---: |:---: |:---: |:---: |:---: |
[mdaffin](https://github.com/mdaffin) |[iliekturtles](https://github.com/iliekturtles) |[nicompte](https://github.com/nicompte) |[NickeZ](https://github.com/NickeZ) |[nvzqz](https://github.com/nvzqz) |[nuew](https://github.com/nuew) |
[<img alt="Geogi" src="https://avatars1.githubusercontent.com/u/1818316?v=4&s=117" width="117">](https://github.com/Geogi) |[<img alt="focusaurus" src="https://avatars1.githubusercontent.com/u/482377?v=4&s=117" width="117">](https://github.com/focusaurus) |[<img alt="flying-sheep" src="https://avatars0.githubusercontent.com/u/291575?v=4&s=117" width="117">](https://github.com/flying-sheep) |[<img alt="Phlosioneer" src="https://avatars2.githubusercontent.com/u/4657718?v=4&s=117" width="117">](https://github.com/Phlosioneer) |[<img alt="peppsac" src="https://avatars3.githubusercontent.com/u/2198295?v=4&s=117" width="117">](https://github.com/peppsac) |[<img alt="golddranks" src="https://avatars1.githubusercontent.com/u/2675542?v=4&s=117" width="117">](https://github.com/golddranks) |
:---: |:---: |:---: |:---: |:---: |:---: |
[Geogi](https://github.com/Geogi) |[focusaurus](https://github.com/focusaurus) |[flying-sheep](https://github.com/flying-sheep) |[Phlosioneer](https://github.com/Phlosioneer) |[peppsac](https://github.com/peppsac) |[golddranks](https://github.com/golddranks) |
[<img alt="hexjelly" src="https://avatars0.githubusercontent.com/u/435283?v=4&s=117" width="117">](https://github.com/hexjelly) |[<img alt="rom1v" src="https://avatars1.githubusercontent.com/u/543275?v=4&s=117" width="117">](https://github.com/rom1v) |[<img alt="rnelson" src="https://avatars3.githubusercontent.com/u/118361?v=4&s=117" width="117">](https://github.com/rnelson) |[<img alt="swatteau" src="https://avatars3.githubusercontent.com/u/5521255?v=4&s=117" width="117">](https://github.com/swatteau) |[<img alt="tchajed" src="https://avatars3.githubusercontent.com/u/1255037?v=4&s=117" width="117">](https://github.com/tchajed) |[<img alt="tspiteri" src="https://avatars0.githubusercontent.com/u/18604588?v=4&s=117" width="117">](https://github.com/tspiteri) |
:---: |:---: |:---: |:---: |:---: |:---: |
[hexjelly](https://github.com/hexjelly) |[rom1v](https://github.com/rom1v) |[rnelson](https://github.com/rnelson) |[swatteau](https://github.com/swatteau) |[tchajed](https://github.com/tchajed) |[tspiteri](https://github.com/tspiteri) |
[<img alt="siiptuo" src="https://avatars0.githubusercontent.com/u/10729330?v=4&s=117" width="117">](https://github.com/siiptuo) |[<img alt="vks" src="https://avatars2.githubusercontent.com/u/33460?v=4&s=117" width="117">](https://github.com/vks) |[<img alt="vsupalov" src="https://avatars2.githubusercontent.com/u/2801030?v=4&s=117" width="117">](https://github.com/vsupalov) |[<img alt="mineo" src="https://avatars1.githubusercontent.com/u/78236?v=4&s=117" width="117">](https://github.com/mineo) |[<img alt="wabain" src="https://avatars3.githubusercontent.com/u/7651435?v=4&s=117" width="117">](https://github.com/wabain) |[<img alt="grossws" src="https://avatars2.githubusercontent.com/u/171284?v=4&s=117" width="117">](https://github.com/grossws) |
:---: |:---: |:---: |:---: |:---: |:---: |
[siiptuo](https://github.com/siiptuo) |[vks](https://github.com/vks) |[vsupalov](https://github.com/vsupalov) |[mineo](https://github.com/mineo) |[wabain](https://github.com/wabain) |[grossws](https://github.com/grossws) |
[<img alt="kennytm" src="https://avatars1.githubusercontent.com/u/103023?v=4&s=117" width="117">](https://github.com/kennytm) |[<img alt="king6cong" src="https://avatars3.githubusercontent.com/u/302560?v=4&s=117" width="117">](https://github.com/king6cong) |[<img alt="mvaude" src="https://avatars1.githubusercontent.com/u/9532611?v=4&s=117" width="117">](https://github.com/mvaude) |[<img alt="panicbit" src="https://avatars2.githubusercontent.com/u/628445?v=4&s=117" width="117">](https://github.com/panicbit) |[<img alt="brennie" src="https://avatars3.githubusercontent.com/u/156585?v=4&s=117" width="117">](https://github.com/brennie) |
:---: |:---: |:---: |:---: |:---: |
[kennytm](https://github.com/kennytm) |[king6cong](https://github.com/king6cong) |[mvaude](https://github.com/mvaude) |[panicbit](https://github.com/panicbit) |[brennie](https://github.com/brennie) |
This list was generated by [mgechev/github-contributors-list](https://github.com/mgechev/github-contributors-list)

1019
third_party/rust/clap/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,130 +3,411 @@
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "clap"
version = "2.34.0"
authors = ["Kevin K. <kbknapp@gmail.com>"]
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
description = "A simple to use, efficient, and full-featured Command Line Argument Parser\n"
homepage = "https://clap.rs/"
version = "3.0.10"
include = ["build.rs", "src/**/*", "Cargo.toml", "LICENSE*", "README.md", "benches/**/*", "examples/**/*"]
description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
documentation = "https://docs.rs/clap/"
readme = "README.md"
keywords = ["argument", "cli", "arg", "parser", "parse"]
categories = ["command-line-interface"]
license = "MIT"
license = "MIT OR Apache-2.0"
repository = "https://github.com/clap-rs/clap"
[package.metadata.docs.rs]
features = ["doc"]
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples=examples"]
features = ["unstable-doc"]
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.playground]
features = ["unstable-doc"]
[package.metadata.release]
shared-version = true
tag-name = "v{{version}}"
[[package.metadata.release.pre-release-replacements]]
file = "CHANGELOG.md"
min = 1
replace = "{{version}}"
search = "Unreleased"
[[package.metadata.release.pre-release-replacements]]
exactly = 1
file = "CHANGELOG.md"
replace = "...{{tag_name}}"
search = "\\.\\.\\.HEAD"
[[package.metadata.release.pre-release-replacements]]
file = "CHANGELOG.md"
min = 1
replace = "{{date}}"
search = "ReleaseDate"
[[package.metadata.release.pre-release-replacements]]
exactly = 1
file = "CHANGELOG.md"
replace = "<!-- next-header -->\n## [Unreleased] - ReleaseDate\n"
search = "<!-- next-header -->"
[[package.metadata.release.pre-release-replacements]]
exactly = 1
file = "CHANGELOG.md"
replace = "<!-- next-url -->\n[Unreleased]: https://github.com/clap-rs/clap/compare/{{tag_name}}...HEAD"
search = "<!-- next-url -->"
[[package.metadata.release.pre-release-replacements]]
exactly = 9
file = "README.md"
prerelease = true
replace = "github.com/clap-rs/clap/blob/{{tag_name}}/"
search = "github.com/clap-rs/clap/blob/[^/]+/"
[[package.metadata.release.pre-release-replacements]]
exactly = 1
file = "README.md"
prerelease = true
replace = "version = \"{{version}}\""
search = "version = \"[a-z0-9\\.-]+\""
[[package.metadata.release.pre-release-replacements]]
exactly = 4
file = "src/derive.rs"
prerelease = true
replace = "github.com/clap-rs/clap/blob/{{tag_name}}/"
search = "github.com/clap-rs/clap/blob/[^/]+/"
[profile.bench]
opt-level = 3
lto = true
codegen-units = 1
debug = false
debug-assertions = false
rpath = false
[profile.dev]
opt-level = 0
lto = false
codegen-units = 4
debug = true
debug-assertions = true
rpath = false
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
debug = false
debug-assertions = false
rpath = false
[profile.test]
opt-level = 1
lto = false
codegen-units = 4
debug = true
debug-assertions = true
rpath = false
[lib]
bench = false
[[example]]
name = "demo"
required-features = ["derive"]
[[example]]
name = "cargo-example"
required-features = ["cargo"]
[[example]]
name = "cargo-example-derive"
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 = "keyvalue-derive"
required-features = ["derive"]
[[example]]
name = "busybox"
path = "examples/multicall-busybox.rs"
required-features = ["unstable-multicall"]
[[example]]
name = "hostname"
path = "examples/multicall-hostname.rs"
required-features = ["unstable-multicall"]
[[example]]
name = "01_quick"
path = "examples/tutorial_builder/01_quick.rs"
required-features = ["cargo"]
[[example]]
name = "02_apps"
path = "examples/tutorial_builder/02_apps.rs"
required-features = ["cargo"]
[[example]]
name = "02_crate"
path = "examples/tutorial_builder/02_crate.rs"
required-features = ["cargo"]
[[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 = "05_01_assert"
path = "examples/tutorial_builder/05_01_assert.rs"
test = true
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"]
[[example]]
name = "05_01_assert_derive"
path = "examples/tutorial_derive/05_01_assert.rs"
test = true
required-features = ["derive"]
[[example]]
name = "custom-bool"
path = "examples/derive_ref/custom-bool.rs"
required-features = ["derive"]
[[bench]]
name = "01_default"
path = "benches/01_default.rs"
harness = false
[[bench]]
name = "02_simple"
path = "benches/02_simple.rs"
harness = false
[[bench]]
name = "03_complex"
path = "benches/03_complex.rs"
harness = false
[[bench]]
name = "04_new_help"
path = "benches/04_new_help.rs"
harness = false
[[bench]]
name = "05_ripgrep"
path = "benches/05_ripgrep.rs"
harness = false
[[bench]]
name = "06_rustup"
path = "benches/06_rustup.rs"
harness = false
[dependencies.atty]
version = "0.2.2"
version = "0.2"
optional = true
[dependencies.backtrace]
version = "0.3"
optional = true
[dependencies.bitflags]
version = "1.2"
[dependencies.clap_derive]
version = "3.0.0"
optional = true
[dependencies.indexmap]
version = "1.0"
[dependencies.clippy]
version = "~0.0.166"
[dependencies.lazy_static]
version = "1"
optional = true
[dependencies.os_str_bytes]
version = "6.0"
[dependencies.regex]
version = "1.0"
optional = true
[dependencies.strsim]
version = "0.8"
version = "0.10"
optional = true
[dependencies.term_size]
version = "0.3.0"
[dependencies.termcolor]
version = "1.1.1"
optional = true
[dependencies.terminal_size]
version = "0.1.12"
optional = true
[dependencies.textwrap]
version = "0.11.0"
version = "0.14.0"
features = []
default-features = false
[dependencies.unicode-width]
version = "0.1.4"
[dependencies.vec_map]
version = "0.8"
[dependencies.unicase]
version = "2.6"
optional = true
[dependencies.yaml-rust]
version = "0.3.5"
version = "0.4.1"
optional = true
[dev-dependencies.lazy_static]
version = "1.3"
[dev-dependencies.criterion]
version = "0.3.2"
[dev-dependencies.regex]
[dev-dependencies.lazy_static]
version = "1"
[dev-dependencies.version-sync]
version = "0.8"
[dev-dependencies.regex]
version = "1.0"
[dev-dependencies.rustversion]
version = "1"
[dev-dependencies.trybuild]
version = "1.0.18"
[dev-dependencies.trycmd]
version = "0.9"
features = ["color-auto", "diff", "examples"]
default-features = false
[features]
color = ["ansi_term", "atty"]
debug = []
default = ["suggestions", "color", "vec_map"]
doc = ["yaml"]
nightly = []
no_cargo = []
cargo = ["lazy_static"]
color = ["atty", "termcolor"]
debug = ["clap_derive/debug", "backtrace"]
default = ["std", "color", "suggestions"]
derive = ["clap_derive", "lazy_static"]
env = []
std = ["indexmap/std"]
suggestions = ["strsim"]
unstable = []
wrap_help = ["term_size", "textwrap/term_size"]
unicode = ["textwrap/unicode-width", "unicase"]
unstable-doc = ["derive", "cargo", "wrap_help", "yaml", "env", "unicode", "regex", "unstable-replace", "unstable-multicall", "unstable-grouped"]
unstable-grouped = []
unstable-multicall = []
unstable-replace = []
wrap_help = ["terminal_size", "textwrap/terminal_size"]
yaml = ["yaml-rust"]
[target."cfg(not(windows))".dependencies.ansi_term]
version = "0.12"
optional = true
[badges.appveyor]
repository = "clap-rs/clap"
[badges.coveralls]
branch = "master"
repository = "clap-rs/clap"
[badges.is-it-maintained-issue-resolution]
repository = "clap-rs/clap"
[badges.is-it-maintained-open-issues]
repository = "clap-rs/clap"
[badges.maintenance]
status = "actively-developed"
[badges.travis-ci]
repository = "clap-rs/clap"

201
third_party/rust/clap/LICENSE-APACHE vendored Normal file
View File

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

View File

@ -1,542 +1,158 @@
clap
====
<!-- omit in TOC -->
# clap
[![Crates.io](https://img.shields.io/crates/v/clap.svg)](https://crates.io/crates/clap) [![Crates.io](https://img.shields.io/crates/d/clap.svg)](https://crates.io/crates/clap) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/clap-rs/clap/blob/master/LICENSE-MIT) [![Coverage Status](https://coveralls.io/repos/kbknapp/clap-rs/badge.svg?branch=master&service=github)](https://coveralls.io/github/kbknapp/clap-rs?branch=master) [![Join the chat at https://gitter.im/kbknapp/clap-rs](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kbknapp/clap-rs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
> **Command Line Argument Parser for Rust**
Linux: [![Build Status](https://travis-ci.org/clap-rs/clap.svg?branch=master)](https://travis-ci.org/clap-rs/clap)
Windows: [![Build status](https://ci.appveyor.com/api/projects/status/ejg8c33dn31nhv36/branch/master?svg=true)](https://ci.appveyor.com/project/kbknapp/clap-rs/branch/master)
[![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/v3.0.10/LICENSE-APACHE)
[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/v3.0.10/LICENSE-MIT)
[![Build Status](https://img.shields.io/github/workflow/status/clap-rs/clap/CI/staging?style=flat-square)](https://github.com/clap-rs/clap/actions/workflows/ci.yml?query=branch%3Astaging)
[![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
Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT).
It is a simple-to-use, efficient, and full-featured library for parsing command line arguments and subcommands when writing console/terminal applications.
* [documentation](https://docs.rs/clap/)
* [website](https://clap.rs/)
* [video tutorials](https://www.youtube.com/playlist?list=PLza5oFLQGTl2Z5T8g1pRkIynR3E0_pc7U)
Table of Contents
=================
* [About](#about)
* [FAQ](#faq)
* [Features](#features)
* [Quick Example](#quick-example)
* [Try it!](#try-it)
* [Pre-Built Test](#pre-built-test)
* [BYOB (Build Your Own Binary)](#byob-build-your-own-binary)
* [Usage](#usage)
* [Optional Dependencies / Features](#optional-dependencies--features)
* [Dependencies Tree](#dependencies-tree)
* [More Information](#more-information)
* [Video Tutorials](#video-tutorials)
* [How to Contribute](#how-to-contribute)
* [Compatibility Policy](#compatibility-policy)
* [Minimum Version of Rust](#minimum-version-of-rust)
* [Related Crates](#related-crates)
* [License](#license)
* [Recent Breaking Changes](#recent-breaking-changes)
* [Deprecations](#deprecations)
Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
1. [About](#about)
2. Tutorial: [Builder API](https://github.com/clap-rs/clap/blob/v3.0.10/examples/tutorial_builder/README.md), [Derive API](https://github.com/clap-rs/clap/blob/v3.0.10/examples/tutorial_derive/README.md)
3. [Examples](https://github.com/clap-rs/clap/blob/v3.0.10/examples/README.md)
4. [API Reference](https://docs.rs/clap)
- [Derive Reference](https://github.com/clap-rs/clap/blob/v3.0.10/examples/derive_ref/README.md)
- [Feature Flags](#feature-flags)
5. [CHANGELOG](https://github.com/clap-rs/clap/blob/v3.0.10/CHANGELOG.md)
6. [FAQ](https://github.com/clap-rs/clap/blob/v3.0.10/docs/FAQ.md)
7. [Questions & Discussions](https://github.com/clap-rs/clap/discussions)
8. [Contributing](https://github.com/clap-rs/clap/blob/v3.0.10/CONTRIBUTING.md)
8. [Sponsors](#sponsors)
## 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 *applications* 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
/// Simple program to greet a person
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
/// Name of the person to greet
#[clap(short, long)]
name: String,
For a full FAQ and more in depth details, see [the wiki page](https://github.com/clap-rs/clap/wiki/FAQ)
### 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 [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 parsers 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.
#### All else being equal, 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.
#### All else being equal, 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 fine-tuning 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. Finally, `clap` is "stringly typed" when referring to arguments which can cause typos in code. This particular paper-cut is being actively worked on, and should be gone in v3.x.
## Features
Below are a few of the features which `clap` supports, full descriptions and usage can be found in the [documentation](https://docs.rs/clap/) and [examples/](examples) directory
* **Auto-generated Help, Version, and Usage information**
- Can optionally be fully, or partially overridden if you want a custom help, version, or usage statements
* **Auto-generated completion scripts at compile time (Bash, Zsh, Fish, and PowerShell)**
- Even works through many 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 [13a_enum_values_automatic example](examples/13a_enum_values_automatic.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 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. There 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](https://docs.rs/clap/), [examples/](examples) directory of this repository or the [video tutorials](https://www.youtube.com/playlist?list=PLza5oFLQGTl2Z5T8g1pRkIynR3E0_pc7U).
**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.
The first example 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.
```rust
// (Full example with detailed comments in examples/01b_quick_example.rs)
//
// This example demonstrates clap's full 'builder pattern' style of creating arguments which is
// more verbose, but allows easier editing, and at times more advanced options, or the possibility
// to generate arguments dynamically.
extern crate clap;
use clap::{Arg, App, SubCommand};
/// Number of times to greet
#[clap(short, long, default_value_t = 1)]
count: u8,
}
fn main() {
let matches = App::new("My Super Program")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.arg(Arg::with_name("config")
.short("c")
.long("config")
.value_name("FILE")
.help("Sets a custom config file")
.takes_value(true))
.arg(Arg::with_name("INPUT")
.help("Sets the input file to use")
.required(true)
.index(1))
.arg(Arg::with_name("v")
.short("v")
.multiple(true)
.help("Sets the level of verbosity"))
.subcommand(SubCommand::with_name("test")
.about("controls testing features")
.version("1.3")
.author("Someone E. <someone_else@other.com>")
.arg(Arg::with_name("debug")
.short("d")
.help("print debug information verbosely")))
.get_matches();
let args = Args::parse();
// Gets a value for config if supplied by user, or defaults to "default.conf"
let config = matches.value_of("config").unwrap_or("default.conf");
println!("Value for config: {}", config);
// Calling .unwrap() is safe here because "INPUT" is required (if "INPUT" wasn't
// required we could have used an 'if let' to conditionally get the value)
println!("Using input file: {}", matches.value_of("INPUT").unwrap());
// 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 matches.occurrences_of("v") {
0 => println!("No verbose info"),
1 => println!("Some verbose info"),
2 => println!("Tons of verbose info"),
3 | _ => println!("Don't be crazy"),
for _ in 0..args.count {
println!("Hello {}!", args.name)
}
// 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
if let Some(matches) = matches.subcommand_matches("test") {
if matches.is_present("debug") {
println!("Printing debug info...");
} else {
println!("Printing normally...");
}
}
// more program logic goes here...
}
```
One could also optionally declare their CLI in YAML format and keep your Rust source tidy
or support multiple localized translations by having different YAML files for each localization.
First, create the `cli.yml` 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
help: Sets a custom config file
takes_value: true
- INPUT:
help: Sets the input file to use
required: true
index: 1
- verbose:
short: v
multiple: true
help: 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
help: print debug information
Add this to `Cargo.toml`:
```toml
[dependencies]
clap = { version = "3.0.10", features = ["derive"] }
```
```bash
$ demo --help
clap [..]
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 change your `clap = "2.34"` to `clap = {version = "2.34", features = ["yaml"]}`.
Finally we create our `main.rs` file just like we would have with the previous two examples:
```rust
// (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.
#[macro_use]
extern crate clap;
use clap::App;
fn main() {
// The YAML file is found relative to the current file, similar to how modules are found
let yaml = load_yaml!("cli.yml");
let matches = App::from_yaml(yaml).get_matches();
// Same as previous examples...
}
```
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
```sh
$ myprog --help
My Super Program 1.0
Kevin K. <kbknapp@gmail.com>
Does awesome things
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
MyApp [FLAGS] [OPTIONS] <INPUT> [SUBCOMMAND]
FLAGS:
-h, --help Prints help information
-v Sets the level of verbosity
-V, --version Prints version information
demo[EXE] [OPTIONS] --name <NAME>
OPTIONS:
-c, --config <FILE> Sets a custom config file
ARGS:
INPUT The input file to use
SUBCOMMANDS:
help Prints this message or the help of the given subcommand(s)
test Controls testing features
-c, --count <COUNT> Number of times to greet [default: 1]
-h, --help Print help information
-n, --name <NAME> Name of the person to greet
-V, --version Print version information
```
*(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
There are also two other methods to create CLIs. Which style you choose is largely a matter of personal preference. The two other methods are:
- 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_complete), 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-9 months between major breaking changes
- We will support the last two minor Rust releases (MSRV, currently 1.54.0)
* Using [usage strings (examples/01a_quick_example.rs)](examples/01a_quick_example.rs) similar to (but not exact) docopt style usage statements. This is far less verbose than the above methods, but incurs a slight runtime penalty.
* Using [a macro (examples/01c_quick_example.rs)](examples/01c_quick_example.rs) which is like a hybrid of the builder and usage string style. It's less verbose, but doesn't incur the runtime penalty of the usage string style. The downside is that it's harder to debug, and more opaque.
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.
Examples of each method can be found in the [examples/](examples) directory of this repository.
### Related Projects
## Try it!
- [wild](https://crates.io/crates/wild) for supporting wildcards (`*`) on Windows like you do Linux
- [argfile](https://crates.io/crates/argfile) for loading additional arguments from a file (aka response files)
- [clap_complete](https://crates.io/crates/clap_complete) for shell completion support
- [clap-verbosity-flag](https://crates.io/crates/clap-verbosity-flag)
- [clap-cargo](https://crates.io/crates/clap-cargo)
- [concolor-clap](https://crates.io/crates/concolor-clap)
- [Command-line Apps for Rust](https://rust-cli.github.io/book/index.html) book
- [`trycmd`](https://crates.io/crates/trycmd): Snapshot testing
- Or for more control, [`assert_cmd`](https://crates.io/crates/assert_cmd) and [`assert_fs`](https://crates.io/crates/assert_fs)
### Pre-Built Test
## Feature Flags
To try out the pre-built examples, use the following steps:
### Default Features
* Clone the repository `$ git clone https://github.com/clap-rs/clap && cd clap-rs/`
* 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]`
* **std**: _Not Currently Used._ Placeholder for supporting `no_std` environments in a backwards compatible manner.
* **color**: Turns on colored error messages.
* **suggestions**: Turns on the `Did you mean '--myoption'?` feature for when users make typos.
### BYOB (Build Your Own Binary)
#### Optional features
To test out `clap`'s default auto-generated help/version follow these steps:
* Create a new cargo project `$ cargo new fake --bin && cd fake`
* Add `clap` to your `Cargo.toml`
* **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.
* **env**: Turns on the usage of environment variables during parsing.
* **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.
```toml
[dependencies]
clap = "2"
```
#### Experimental features
* Add the following to your `src/main.rs`
**Warning:** These may contain breaking changes between minor releases.
```rust
extern crate clap;
use clap::App;
* **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)
fn main() {
App::new("fake").version("v1.0-beta").get_matches();
}
```
## Sponsors
* Build your program `$ cargo build --release`
* Run with help or version `$ ./target/release/fake --help` or `$ ./target/release/fake --version`
<!-- omit in TOC -->
### Gold
## Usage
[![](https://opencollective.com/clap/tiers/gold.svg?avatarHeight=36&width=600)](https://opencollective.com/clap)
For full usage, add `clap` as a dependency in your `Cargo.toml` () to use from crates.io:
<!-- omit in TOC -->
### Silver
```toml
[dependencies]
clap = "~2.34"
```
[![](https://opencollective.com/clap/tiers/silver.svg?avatarHeight=36&width=600)](https://opencollective.com/clap)
(**note**: If you are concerned with supporting a minimum version of Rust that is *older* than the current stable Rust minus 2 stable releases, it's recommended to use the `~major.minor.patch` style versions in your `Cargo.toml` which will only update the patch version automatically. For more information see the [Compatibility Policy](#compatibility-policy))
<!-- omit in TOC -->
### Bronze
Then add `extern crate clap;` to your crate root.
[![](https://opencollective.com/clap/tiers/bronze.svg?avatarHeight=36&width=600)](https://opencollective.com/clap)
Define a list of valid arguments for your program (see the [documentation](https://docs.rs/clap/) or [examples/](examples) directory of this repo)
<!-- omit in TOC -->
### Backer
Then run `cargo build` or `cargo update && cargo build` for your project.
### Optional Dependencies / Features
#### Features enabled by default
* **"suggestions"**: Turns on the `Did you mean '--myoption'?` feature for when users make typos. (builds dependency `strsim`)
* **"color"**: Turns on colored error messages. This feature only works on non-Windows OSs. (builds dependency `ansi-term` only on non-Windows targets)
* **"vec_map"**: Use [`VecMap`](https://crates.io/crates/vec_map) internally instead of a [`BTreeMap`](https://doc.rust-lang.org/stable/std/collections/struct.BTreeMap.html). This feature provides a _slight_ performance improvement. (builds dependency `vec_map`)
To disable these, add this to your `Cargo.toml`:
```toml
[dependencies.clap]
version = "2.34"
default-features = false
```
You can also selectively enable only the features you'd like to include, by adding:
```toml
[dependencies.clap]
version = "2.34"
default-features = false
# Cherry-pick the features you'd like to use
features = [ "suggestions", "color" ]
```
#### Opt-in features
* **"yaml"**: Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
* **"unstable"**: Enables unstable `clap` features that may change from release to release
* **"wrap_help"**: Turns on the help text wrapping feature, based on the terminal size. (builds dependency `term-size`)
### Dependencies Tree
The following graphic depicts `clap`s dependency graph (generated using [cargo-graph](https://github.com/kbknapp/cargo-graph)).
* **Dashed** Line: Optional dependency
* **Red** Color: **NOT** included by default (must use cargo `features` to enable)
* **Blue** Color: Dev dependency, only used while developing.
![clap dependencies](clap_dep_graph.png)
### More Information
You can find complete documentation on the [docs.rs](https://docs.rs/clap/) for this project.
You can also find usage examples in the [examples/](examples) directory of this repo.
#### Video Tutorials
There's also the video tutorial series [Argument Parsing with Rust v2](https://www.youtube.com/playlist?list=PLza5oFLQGTl2Z5T8g1pRkIynR3E0_pc7U).
These videos slowly trickle out as I finish them and currently a work in progress.
## How to Contribute
Details on how to contribute can be found in the [CONTRIBUTING.md](.github/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 = "~2.34"
```
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.
#### Warning about '~' Dependencies
Using `~` can cause issues in certain circumstances.
From @alexcrichton:
Right now Cargo's version resolution is pretty naive, it's just a brute-force search of the solution space, returning the first resolvable graph. This also means that it currently won't terminate until it proves there is not possible resolvable graph. This leads to situations where workspaces with multiple binaries, for example, have two different dependencies such as:
```toml,no_sync
# In one Cargo.toml
[dependencies]
clap = "~2.34.0"
# In another Cargo.toml
[dependencies]
clap = "2.34.0"
```
This is inherently an unresolvable crate graph in Cargo right now. Cargo requires there's only one major version of a crate, and being in the same workspace these two crates must share a version. This is impossible in this location, though, as these version constraints cannot be met.
#### Minimum Version of Rust
`clap` will officially support current stable Rust, minus two releases, but may work with prior releases as well. For example, current stable Rust at the time of this writing is 1.41.0, meaning `clap` is guaranteed to compile with 1.39.0 and beyond.
At the 1.42.0 stable release, `clap` will be guaranteed to compile with 1.40.0 and beyond, etc.
Upon bumping the minimum version of Rust (assuming it's within the stable-2 range), it *must* be clearly annotated in the `CHANGELOG.md`
#### 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
#### Compatibility with Wasm
A best effort is made to ensure that `clap` will work on projects targeting `wasm32-unknown-unknown`. However there is no dedicated CI build
covering this specific target.
## License
`clap` is licensed under the MIT license. Please read the [LICENSE-MIT](LICENSE-MIT) file 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!
* [`structopt`](https://github.com/TeXitoi/structopt) - This crate allows you to define a struct, and build a CLI from it! No more "stringly typed" and it uses `clap` behind the scenes! (*Note*: There is work underway to pull this crate into mainline `clap`).
* [`assert_cli`](https://github.com/assert-rs/assert_cli) - This crate allows you test your CLIs in a very intuitive and functional way!
## Recent Breaking Changes
`clap` follows semantic versioning, so breaking changes should only happen upon major version bumps. The only exception to this rule is breaking changes that happen due to implementation that was deemed to be a bug, security concerns, or it can be reasonably proved to affect no code. For the full details, see [CHANGELOG.md](./CHANGELOG.md).
As of 2.27.0:
* Argument values now take precedence over subcommand names. This only arises by using unrestrained multiple values and subcommands together where the subcommand name can coincide with one of the multiple values. Such as `$ prog <files>... <subcommand>`. The fix is to place restraints on number of values, or disallow the use of `$ prog <prog-args> <subcommand>` structure.
As of 2.0.0 (From 1.x)
* **Fewer lifetimes! Yay!**
* `App<'a, 'b, 'c, 'd, 'e, 'f>` => `App<'a, 'b>`
* `Arg<'a, 'b, 'c, 'd, 'e, 'f>` => `Arg<'a, 'b>`
* `ArgMatches<'a, 'b>` => `ArgMatches<'a>`
* **Simply Renamed**
* `App::arg_group` => `App::group`
* `App::arg_groups` => `App::groups`
* `ArgGroup::add` => `ArgGroup::arg`
* `ArgGroup::add_all` => `ArgGroup::args`
* `ClapError` => `Error`
* struct field `ClapError::error_type` => `Error::kind`
* `ClapResult` => `Result`
* `ClapErrorType` => `ErrorKind`
* **Removed Deprecated Functions and Methods**
* `App::subcommands_negate_reqs`
* `App::subcommand_required`
* `App::arg_required_else_help`
* `App::global_version(bool)`
* `App::versionless_subcommands`
* `App::unified_help_messages`
* `App::wait_on_error`
* `App::subcommand_required_else_help`
* `SubCommand::new`
* `App::error_on_no_subcommand`
* `Arg::new`
* `Arg::mutually_excludes`
* `Arg::mutually_excludes_all`
* `Arg::mutually_overrides_with`
* `simple_enum!`
* **Renamed Error Variants**
* `InvalidUnicode` => `InvalidUtf8`
* `InvalidArgument` => `UnknownArgument`
* **Usage Parser**
* Value names can now be specified inline, i.e. `-o, --option <FILE> <FILE2> 'some option which takes two files'`
* **There is now a priority of order to determine the name** - This is perhaps the biggest breaking change. See the documentation for full details. Prior to this change, the value name took precedence. **Ensure your args are using the proper names (i.e. typically the long or short and NOT the value name) throughout the code**
* `ArgMatches::values_of` returns an `Values` now which implements `Iterator` (should not break any code)
* `crate_version!` returns `&'static str` instead of `String`
### Deprecations
Old method names will be left around for several minor version bumps, or one major version bump.
As of 2.27.0:
* **AppSettings::PropagateGlobalValuesDown:** this setting deprecated and is no longer required to propagate values down or up
[![](https://opencollective.com/clap/tiers/backer.svg?avatarHeight=36&width=600)](https://opencollective.com/clap)

View File

@ -1,17 +0,0 @@
Below is a list of sponsors for the clap-rs project
If you are interested in becoming a sponsor for this project please our [sponsorship page](https://clap.rs/sponsorship/).
## Recurring Sponsors:
| [<img alt="Noelia Seva-Gonzalez" src="https://clap.rs/wp-content/uploads/2017/10/noelia_sm-1.png" width="117">](https://noeliasg.com/about/) | [<img alt="messense" src="https://clap.rs/wp-content/uploads/2018/01/messense-400x400.png" width="117">](https://github.com/messense) | [<img alt="Josh" src="https://clap.rs/wp-content/uploads/2018/11/josh_t.jpg" width="117">](https://joshtriplett.org) | <img alt="Stephen Oats" src="https://clap.rs/wp-content/uploads/2019/03/stephenoats.png" width="117"> |
|:-:|:-:|:-:|:-:|
|Noelia Seva-Gonzalez | Messense | Josh Triplett | Stephen Oats |
## Single-Donation and Former Sponsors:
| [<img alt="Rob Tsuk" src="https://clap.rs/wp-content/uploads/2017/10/robtsuk_sm.png" width="117">](https://github.com/rtsuk)| | |
|:-:|:-:|:-:|
|Rob Tsuk| | |

View File

@ -0,0 +1,15 @@
use clap::App;
use criterion::{criterion_group, criterion_main, Criterion};
pub fn build_empty(c: &mut Criterion) {
c.bench_function("build_empty", |b| b.iter(|| App::new("claptests")));
}
pub fn parse_empty(c: &mut Criterion) {
c.bench_function("parse_empty", |b| {
b.iter(|| App::new("claptests").get_matches_from(vec![""]))
});
}
criterion_group!(benches, build_empty, parse_empty);
criterion_main!(benches);

View File

@ -0,0 +1,104 @@
use clap::{arg, App, Arg};
use criterion::{criterion_group, criterion_main, Criterion};
macro_rules! create_app {
() => {{
App::new("claptests")
.version("0.1")
.about("tests clap library")
.author("Kevin K. <kbknapp@gmail.com>")
.arg(arg!(-f --flag "tests flags"))
.arg(arg!(-o --option <opt> "tests options").required(false))
.arg(arg!([positional] "tests positional"))
}};
}
pub fn build_simple(c: &mut Criterion) {
c.bench_function("build_simple", |b| b.iter(|| create_app!()));
}
pub fn build_with_flag(c: &mut Criterion) {
c.bench_function("build_with_flag", |b| {
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!(-s --some "something");
App::new("claptests").arg(&arg)
})
});
}
pub fn build_with_opt(c: &mut Criterion) {
c.bench_function("build_with_opt", |b| {
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!(-s --some <FILE> "something");
App::new("claptests").arg(&arg)
})
});
}
pub fn build_with_pos(c: &mut Criterion) {
c.bench_function("build_with_pos", |b| {
b.iter(|| App::new("claptests").arg(Arg::new("some")))
});
}
pub fn build_with_pos_ref(c: &mut Criterion) {
c.bench_function("build_with_pos_ref", |b| {
b.iter(|| {
let arg = Arg::new("some");
App::new("claptests").arg(&arg)
})
});
}
pub fn parse_simple_with_flag(c: &mut Criterion) {
c.bench_function("parse_simple_with_flag", |b| {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "-f"]))
});
}
pub fn parse_simple_with_opt(c: &mut Criterion) {
c.bench_function("parse_simple_with_opt", |b| {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "-o", "option1"]))
});
}
pub fn parse_simple_with_pos(c: &mut Criterion) {
c.bench_function("parse_simple_with_pos", |b| {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "arg1"]))
});
}
pub fn parse_simple_with_complex(c: &mut Criterion) {
c.bench_function("parse_simple_with_complex", |b| {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "-o", "option1", "-f", "arg1"]))
});
}
criterion_group!(
benches,
parse_simple_with_complex,
parse_simple_with_pos,
parse_simple_with_opt,
parse_simple_with_flag,
build_with_pos_ref,
build_with_pos,
build_with_opt_ref,
build_with_opt,
build_with_flag_ref,
build_with_flag,
build_simple
);
criterion_main!(benches);

View File

@ -0,0 +1,307 @@
use clap::{arg, App, AppSettings, Arg};
use criterion::{criterion_group, criterion_main, Criterion};
static OPT3_VALS: [&str; 2] = ["fast", "slow"];
static POS3_VALS: [&str; 2] = ["vi", "emacs"];
macro_rules! create_app {
() => {{
App::new("claptests")
.version("0.1")
.about("tests clap library")
.author("Kevin K. <kbknapp@gmail.com>")
.arg(arg!(-o --option <opt> ... "tests options").required(false))
.arg(arg!([positional] "tests positionals"))
.arg(arg!(-f --flag ... "tests flags").global(true))
.args(&[
arg!(flag2: -F "tests flags with exclusions")
.conflicts_with("flag")
.requires("option2"),
arg!(option2: --"long-option-2" <option2> "tests long options with exclusions")
.required(false)
.conflicts_with("option")
.requires("positional2"),
arg!([positional2] "tests positionals with exclusions"),
arg!(-O --Option <option3> "tests options with specific value sets")
.required(false)
.possible_values(OPT3_VALS),
arg!([positional3] ... "tests positionals with specific values")
.possible_values(POS3_VALS),
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(arg!(-o --option <scoption> ... "tests options").required(false))
.arg(arg!([scpositional] "tests positionals"))
)
}};
}
pub fn build_from_builder(c: &mut Criterion) {
c.bench_function("build_from_builder", |b| {
b.iter(|| {
App::new("claptests")
.version("0.1")
.about("tests clap library")
.author("Kevin K. <kbknapp@gmail.com>")
.arg(
Arg::new("opt")
.help("tests options")
.short('o')
.long("option")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true),
)
.arg(Arg::new("positional").help("tests positionals").index(1))
.arg(
Arg::new("flag")
.short('f')
.help("tests flags")
.long("flag")
.global(true)
.multiple_occurrences(true),
)
.arg(
Arg::new("flag2")
.short('F')
.help("tests flags with exclusions")
.conflicts_with("flag")
.requires("option2"),
)
.arg(
Arg::new("option2")
.help("tests long options with exclusions")
.conflicts_with("option")
.requires("positional2")
.takes_value(true)
.long("long-option-2"),
)
.arg(
Arg::new("positional2")
.index(3)
.help("tests positionals with exclusions"),
)
.arg(
Arg::new("option3")
.short('O')
.long("Option")
.takes_value(true)
.help("tests options with specific value sets")
.possible_values(OPT3_VALS),
)
.arg(
Arg::new("positional3")
.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")
.help("Tests multiple values, not mult occs")
.value_names(&["one", "two"]),
)
.arg(
Arg::new("multvalsmo")
.long("multvalsmo")
.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")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.help("Tests 2 min vals")
.min_values(2),
)
.arg(
Arg::new("maxvals")
.long("maxvals3")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.help("Tests 3 max vals")
.max_values(3),
)
.subcommand(
App::new("subcmd")
.about("tests subcommands")
.version("0.1")
.author("Kevin K. <kbknapp@gmail.com>")
.arg(
Arg::new("scoption")
.short('o')
.long("option")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.help("tests options"),
)
.arg(Arg::new("scpositional").index(1).help("tests positionals")),
)
})
});
}
pub fn parse_complex(c: &mut Criterion) {
c.bench_function("parse_complex", |b| {
b.iter(|| create_app!().get_matches_from(vec![""]))
});
}
pub fn parse_complex_with_flag(c: &mut Criterion) {
c.bench_function("parse_complex_with_flag", |b| {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "-f"]))
});
}
pub fn parse_complex_with_opt(c: &mut Criterion) {
c.bench_function("parse_complex_with_opt", |b| {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "-o", "option1"]))
});
}
pub fn parse_complex_with_pos(c: &mut Criterion) {
c.bench_function("parse_complex_with_pos", |b| {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "arg1"]))
});
}
pub fn parse_complex_with_sc(c: &mut Criterion) {
c.bench_function("parse_complex_with_sc", |b| {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "subcmd"]))
});
}
pub fn parse_complex_with_sc_flag(c: &mut Criterion) {
c.bench_function("parse_complex_with_sc_flag", |b| {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "subcmd", "-f"]))
});
}
pub fn parse_complex_with_sc_opt(c: &mut Criterion) {
c.bench_function("parse_complex_with_sc_opt", |b| {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "subcmd", "-o", "option1"]))
});
}
pub fn parse_complex_with_sc_pos(c: &mut Criterion) {
c.bench_function("parse_complex_with_sc_pos", |b| {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "subcmd", "arg1"]))
});
}
pub fn parse_complex1(c: &mut Criterion) {
c.bench_function("parse_complex1", |b| {
b.iter(|| {
create_app!().get_matches_from(vec![
"myprog",
"-ff",
"-o",
"option1",
"arg1",
"-O",
"fast",
"arg2",
"--multvals",
"one",
"two",
"emacs",
])
})
});
}
pub fn parse_complex2(c: &mut Criterion) {
c.bench_function("parse_complex2", |b| {
b.iter(|| {
create_app!().get_matches_from(vec![
"myprog",
"arg1",
"-f",
"arg2",
"--long-option-2",
"some",
"-O",
"slow",
"--multvalsmo",
"one",
"two",
"--minvals2",
"3",
"2",
"1",
])
})
});
}
pub fn parse_args_negate_scs(c: &mut Criterion) {
c.bench_function("parse_args_negate_scs", |b| {
b.iter(|| {
create_app!()
.setting(AppSettings::ArgsNegateSubcommands)
.get_matches_from(vec![
"myprog",
"arg1",
"-f",
"arg2",
"--long-option-2",
"some",
"-O",
"slow",
"--multvalsmo",
"one",
"two",
"--minvals2",
"3",
"2",
"1",
])
})
});
}
pub fn parse_complex_with_sc_complex(c: &mut Criterion) {
c.bench_function("parse_complex_with_sc_complex", |b| {
b.iter(|| {
create_app!().get_matches_from(vec!["myprog", "subcmd", "-f", "-o", "option1", "arg1"])
})
});
}
criterion_group!(
benches,
build_from_builder,
parse_complex,
parse_complex_with_flag,
parse_complex_with_opt,
parse_complex_with_pos,
parse_complex_with_sc,
parse_complex_with_sc_flag,
parse_complex_with_sc_opt,
parse_complex_with_sc_pos,
parse_complex1,
parse_complex2,
parse_args_negate_scs,
parse_complex_with_sc_complex
);
criterion_main!(benches);

View File

@ -0,0 +1,223 @@
use clap::App;
use clap::{arg, Arg};
use criterion::{criterion_group, criterion_main, Criterion};
use std::io::Cursor;
fn build_help(app: &mut App) -> String {
let mut buf = Cursor::new(Vec::with_capacity(50));
app.write_help(&mut buf).unwrap();
let content = buf.into_inner();
String::from_utf8(content).unwrap()
}
fn app_example1<'c>() -> App<'c> {
App::new("MyApp")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.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(arg!(-l --list "lists test values")),
)
}
fn app_example2<'c>() -> App<'c> {
App::new("MyApp")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
}
fn app_example3<'c>() -> App<'c> {
App::new("MyApp")
.arg(
Arg::new("debug")
.help("turn on debugging information")
.short('d'),
)
.args(&[
Arg::new("config")
.help("sets the config file to use")
.takes_value(true)
.short('c')
.long("config"),
Arg::new("input")
.help("the input file to use")
.required(true),
])
.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> {
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")
.help("turn on debugging information")
.short('d')
.long("debug"),
)
.arg(
Arg::new("config")
.help("sets the config file to use")
.short('c')
.long("config"),
)
.arg(
Arg::new("input")
.help("the input file to use")
.index(1)
.required(true),
)
}
fn app_example5<'c>() -> App<'c> {
App::new("MyApp").arg(
Arg::new("awesome")
.help("turns up the awesome")
.short('a')
.long("awesome")
.multiple_occurrences(true),
)
}
fn app_example6<'c>() -> App<'c> {
App::new("MyApp")
.arg(
Arg::new("input")
.help("the input file to use")
.index(1)
.requires("config")
.required(true),
)
.arg(Arg::new("config").help("the config file to use").index(2))
}
fn app_example7<'c>() -> App<'c> {
App::new("MyApp")
.arg(Arg::new("config"))
.arg(Arg::new("output"))
.arg(
Arg::new("input")
.help("the input file to use")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.required(true)
.short('i')
.long("input")
.requires("config")
.conflicts_with("output"),
)
}
fn app_example8<'c>() -> App<'c> {
App::new("MyApp")
.arg(Arg::new("config"))
.arg(Arg::new("output"))
.arg(
Arg::new("input")
.help("the input file to use")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true)
.required(true)
.short('i')
.long("input")
.requires("config")
.conflicts_with("output"),
)
}
fn app_example10<'c>() -> App<'c> {
App::new("myapp").about("does awesome things").arg(
Arg::new("CONFIG")
.help("The config file to use (default is \"config.json\")")
.short('c')
.takes_value(true),
)
}
pub fn example1(c: &mut Criterion) {
let mut app = app_example1();
c.bench_function("example1", |b| b.iter(|| build_help(&mut app)));
}
pub fn example2(c: &mut Criterion) {
let mut app = app_example2();
c.bench_function("example2", |b| b.iter(|| build_help(&mut app)));
}
pub fn example3(c: &mut Criterion) {
let mut app = app_example3();
c.bench_function("example3", |b| b.iter(|| build_help(&mut app)));
}
pub fn example4(c: &mut Criterion) {
let mut app = app_example4();
c.bench_function("example4", |b| b.iter(|| build_help(&mut app)));
}
pub fn example5(c: &mut Criterion) {
let mut app = app_example5();
c.bench_function("example5", |b| b.iter(|| build_help(&mut app)));
}
pub fn example6(c: &mut Criterion) {
let mut app = app_example6();
c.bench_function("example6", |b| b.iter(|| build_help(&mut app)));
}
pub fn example7(c: &mut Criterion) {
let mut app = app_example7();
c.bench_function("example7", |b| b.iter(|| build_help(&mut app)));
}
pub fn example8(c: &mut Criterion) {
let mut app = app_example8();
c.bench_function("example8", |b| b.iter(|| build_help(&mut app)));
}
pub fn example10(c: &mut Criterion) {
let mut app = app_example10();
c.bench_function("example10", |b| b.iter(|| build_help(&mut app)));
}
pub fn example4_template(c: &mut Criterion) {
let mut app = app_example4().help_template("{bin} {version}\n{author}\n{about}\n\nUSAGE:\n {usage}\n\nOPTIONS:\n{options}\n\nARGS:\n{args}\n");
c.bench_function("example4_template", |b| b.iter(|| build_help(&mut app)));
}
criterion_group!(
benches,
example1,
example2,
example3,
example4,
example5,
example6,
example7,
example8,
example10,
example4_template
);
criterion_main!(benches);

View File

@ -0,0 +1,952 @@
// Used to simulate a fairly large number of options/flags and parsing with thousands of positional
// args
//
// CLI used is adapted from ripgrep 48a8a3a691220f9e5b2b08f4051abe8655ea7e8a
use clap::{App, Arg};
use criterion::{criterion_group, criterion_main, Criterion};
use std::collections::HashMap;
use std::io::Cursor;
use lazy_static::lazy_static;
pub fn build_rg_with_short_help(c: &mut Criterion) {
c.bench_function("build_rg_with_short_help", |b| b.iter(app_short));
}
pub fn build_rg_with_long_help(c: &mut Criterion) {
c.bench_function("build_rg_with_long_help", |b| b.iter(app_long));
}
pub fn write_rg_short_help(c: &mut Criterion) {
let mut app = app_short();
c.bench_function("write_rg_short_help", |b| b.iter(|| build_help(&mut app)));
}
pub fn write_rg_long_help(c: &mut Criterion) {
let mut app = app_long();
c.bench_function("write_rg_long_help", |b| b.iter(|| build_help(&mut app)));
}
pub fn parse_rg(c: &mut Criterion) {
c.bench_function("parse_rg", |b| {
b.iter(|| app_short().get_matches_from(vec!["rg", "pat"]))
});
}
pub fn parse_rg_with_complex(c: &mut Criterion) {
c.bench_function("parse_rg_with_complex", |b| {
b.iter(|| {
app_short().get_matches_from(vec![
"rg",
"pat",
"-cFlN",
"-pqr=some",
"--null",
"--no-filename",
"--no-messages",
"-SH",
"-C5",
"--follow",
"-e some",
])
})
});
}
pub fn parse_rg_with_lots(c: &mut Criterion) {
c.bench_function("parse_rg_with_lots", |b| {
b.iter(|| {
app_short().get_matches_from(vec![
"rg", "pat", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
"some", "some", "some", "some", "some", "some",
])
})
});
}
const ABOUT: &str = "
ripgrep (rg) recursively searches your current directory for a regex pattern.
ripgrep's regex engine uses finite automata and guarantees linear time
searching. Because of this, features like backreferences and arbitrary
lookaround are not supported.
Project home page: https://github.com/BurntSushi/ripgrep
Use -h for short descriptions and --help for more details.";
const USAGE: &str = "
rg [OPTIONS] <pattern> [<path> ...]
rg [OPTIONS] [-e PATTERN | -f FILE ]... [<path> ...]
rg [OPTIONS] --files [<path> ...]
rg [OPTIONS] --type-list";
const TEMPLATE: &str = "\
{bin} {version}
{author}
{about}
USAGE:{usage}
ARGS:
{positionals}
OPTIONS:
{options}";
/// Build a clap application with short help strings.
fn app_short() -> App<'static> {
app(false, |k| USAGES[k].short)
}
/// Build a clap application with long help strings.
fn app_long() -> App<'static> {
app(true, |k| USAGES[k].long)
}
/// Build the help text of an application.
fn build_help(app: &mut App) -> String {
let mut buf = Cursor::new(Vec::with_capacity(50));
app.write_help(&mut buf).unwrap();
let content = buf.into_inner();
String::from_utf8(content).unwrap()
}
/// Build a clap application parameterized by usage strings.
///
/// The function given should take a clap argument name and return a help
/// string. `app` will panic if a usage string is not defined.
///
/// This is an intentionally stand-alone module so that it can be used easily
/// in a `build.rs` script to build shell completion files.
fn app<F>(_next_line_help: bool, doc: F) -> App<'static>
where
F: Fn(&'static str) -> &'static str,
{
let arg = |name| Arg::new(name).help(doc(name));
let flag = |name| arg(name).long(name);
App::new("ripgrep")
.author("BurntSushi") // simulating since it's only a bench
.version("0.4.0") // Simulating
.about(ABOUT)
.max_term_width(100)
.override_usage(USAGE)
.help_template(TEMPLATE)
// Handle help/version manually to make their output formatting
// consistent with short/long views.
.arg(arg("help-short").short('h'))
.arg(flag("help"))
.arg(flag("version").short('V'))
// First, set up primary positional/flag arguments.
.arg(arg("pattern").required_unless_present_any(&[
"file",
"files",
"help-short",
"help",
"regexp",
"type-list",
"version",
]))
.arg(
arg("path")
.takes_value(true)
.multiple_values(true)
.multiple_occurrences(true),
)
.arg(
flag("regexp")
.short('e')
.allow_hyphen_values(true)
.multiple_occurrences(true)
.takes_value(true)
.value_name("pattern"),
)
.arg(
flag("files")
// This should also conflict with `pattern`, but the first file
// path will actually be in `pattern`.
.conflicts_with_all(&["file", "regexp", "type-list"]),
)
.arg(flag("type-list").conflicts_with_all(&["file", "files", "pattern", "regexp"]))
// Second, set up common flags.
.arg(flag("text").short('a'))
.arg(flag("count").short('c'))
.arg(
flag("color")
.value_name("WHEN")
.takes_value(true)
.hide_possible_values(true)
.possible_values(["never", "auto", "always", "ansi"]),
)
.arg(
flag("colors")
.value_name("SPEC")
.multiple_occurrences(true)
.takes_value(true),
)
.arg(flag("fixed-strings").short('F'))
.arg(
flag("glob")
.short('g')
.multiple_occurrences(true)
.takes_value(true)
.value_name("GLOB"),
)
.arg(flag("ignore-case").short('i'))
.arg(flag("line-number").short('n'))
.arg(flag("no-line-number").short('N'))
.arg(flag("quiet").short('q'))
.arg(
flag("type")
.short('t')
.multiple_occurrences(true)
.takes_value(true)
.value_name("TYPE"),
)
.arg(
flag("type-not")
.short('T')
.multiple_occurrences(true)
.takes_value(true)
.value_name("TYPE"),
)
.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.
.arg(
flag("after-context")
.short('A')
.value_name("NUM")
.validator(validate_number),
)
.arg(
flag("before-context")
.short('B')
.value_name("NUM")
.validator(validate_number),
)
.arg(
flag("context")
.short('C')
.value_name("NUM")
.validator(validate_number),
)
.arg(flag("column"))
.arg(flag("context-separator").value_name("SEPARATOR"))
.arg(flag("debug"))
.arg(
flag("file")
.short('f')
.value_name("FILE")
.multiple_occurrences(true),
)
.arg(flag("files-with-matches").short('l'))
.arg(flag("files-without-match"))
.arg(flag("with-filename").short('H'))
.arg(flag("no-filename"))
.arg(flag("heading").overrides_with("no-heading"))
.arg(flag("no-heading").overrides_with("heading"))
.arg(flag("hidden"))
.arg(
flag("ignore-file")
.value_name("FILE")
.multiple_occurrences(true),
)
.arg(flag("follow").short('L'))
.arg(
flag("max-count")
.short('m')
.value_name("NUM")
.validator(validate_number),
)
.arg(
flag("maxdepth")
.value_name("NUM")
.validator(validate_number),
)
.arg(flag("mmap"))
.arg(flag("no-messages"))
.arg(flag("no-mmap"))
.arg(flag("no-ignore"))
.arg(flag("no-ignore-parent"))
.arg(flag("no-ignore-vcs"))
.arg(flag("null"))
.arg(flag("path-separator").value_name("SEPARATOR"))
.arg(flag("pretty").short('p'))
.arg(flag("replace").short('r').value_name("ARG"))
.arg(flag("case-sensitive").short('s'))
.arg(flag("smart-case").short('S'))
.arg(flag("sort-files"))
.arg(
flag("threads")
.short('j')
.value_name("ARG")
.validator(validate_number),
)
.arg(flag("vimgrep"))
.arg(
flag("type-add")
.value_name("TYPE")
.multiple_occurrences(true),
)
.arg(
flag("type-clear")
.value_name("TYPE")
.multiple_occurrences(true),
)
}
struct Usage {
short: &'static str,
long: &'static str,
}
macro_rules! doc {
($map:expr, $name:expr, $short:expr) => {
doc!($map, $name, $short, $short)
};
($map:expr, $name:expr, $short:expr, $long:expr) => {
$map.insert(
$name,
Usage {
short: $short,
long: concat!($long, "\n "),
},
);
};
}
lazy_static! {
static ref USAGES: HashMap<&'static str, Usage> = {
let mut h = HashMap::new();
doc!(
h,
"help-short",
"Show short help output.",
"Show short help output. Use --help to show more details."
);
doc!(
h,
"help",
"Show verbose help output.",
"When given, more details about flags are provided."
);
doc!(h, "version", "Print version information.");
doc!(
h,
"pattern",
"A regular expression used for searching.",
"A regular expression used for searching. Multiple patterns \
may be given. To match a pattern beginning with a -, use [-]."
);
doc!(
h,
"regexp",
"A regular expression used for searching.",
"A regular expression used for searching. Multiple patterns \
may be given. To match a pattern beginning with a -, use [-]."
);
doc!(
h,
"path",
"A file or directory to search.",
"A file or directory to search. Directories are searched \
recursively."
);
doc!(
h,
"files",
"Print each file that would be searched.",
"Print each file that would be searched without actually \
performing the search. This is useful to determine whether a \
particular file is being searched or not."
);
doc!(
h,
"type-list",
"Show all supported file types.",
"Show all supported file types and their corresponding globs."
);
doc!(h, "text", "Search binary files as if they were text.");
doc!(h, "count", "Only show count of matches for each file.");
doc!(
h,
"color",
"When to use color. [default: auto]",
"When to use color in the output. The possible values are \
never, auto, always or ansi. The default is auto. When always \
is used, coloring is attempted based on your environment. When \
ansi used, coloring is forcefully done using ANSI escape color \
codes."
);
doc!(
h,
"colors",
"Configure color settings and styles.",
"This flag specifies color settings for use in the output. \
This flag may be provided multiple times. Settings are applied \
iteratively. Colors are limited to one of eight choices: \
red, blue, green, cyan, magenta, yellow, white and black. \
Styles are limited to nobold, bold, nointense or intense.\n\n\
The format of the flag is {type}:{attribute}:{value}. {type} \
should be one of path, line or match. {attribute} can be fg, bg \
or style. {value} is either a color (for fg and bg) or a text \
style. A special format, {type}:none, will clear all color \
settings for {type}.\n\nFor example, the following command will \
change the match color to magenta and the background color for \
line numbers to yellow:\n\n\
rg --colors 'match:fg:magenta' --colors 'line:bg:yellow' foo."
);
doc!(
h,
"fixed-strings",
"Treat the pattern as a literal string.",
"Treat the pattern as a literal string instead of a regular \
expression. When this flag is used, special regular expression \
meta characters such as (){}*+. do not need to be escaped."
);
doc!(
h,
"glob",
"Include or exclude files/directories.",
"Include or exclude files/directories for searching that \
match the given glob. This always overrides any other \
ignore logic. Multiple glob flags may be used. Globbing \
rules match .gitignore globs. Precede a glob with a ! \
to exclude it."
);
doc!(
h,
"ignore-case",
"Case insensitive search.",
"Case insensitive search. This is overridden by \
--case-sensitive."
);
doc!(
h,
"line-number",
"Show line numbers.",
"Show line numbers (1-based). This is enabled by default when \
searching in a tty."
);
doc!(
h,
"no-line-number",
"Suppress line numbers.",
"Suppress line numbers. This is enabled by default when NOT \
searching in a tty."
);
doc!(
h,
"quiet",
"Do not print anything to stdout.",
"Do not print anything to stdout. If a match is found in a file, \
stop searching. This is useful when ripgrep is used only for \
its exit code."
);
doc!(
h,
"type",
"Only search files matching TYPE.",
"Only search files matching TYPE. Multiple type flags may be \
provided. Use the --type-list flag to list all available \
types."
);
doc!(
h,
"type-not",
"Do not search files matching TYPE.",
"Do not search files matching TYPE. Multiple type-not flags may \
be provided. Use the --type-list flag to list all available \
types."
);
doc!(
h,
"unrestricted",
"Reduce the level of \"smart\" searching.",
"Reduce the level of \"smart\" searching. A single -u \
won't respect .gitignore (etc.) files. Two -u flags will \
additionally search hidden files and directories. Three \
-u flags will additionally search binary files. -uu is \
roughly equivalent to grep -r and -uuu is roughly \
equivalent to grep -a -r."
);
doc!(
h,
"invert-match",
"Invert matching.",
"Invert matching. Show lines that don't match given patterns."
);
doc!(
h,
"word-regexp",
"Only show matches surrounded by word boundaries.",
"Only show matches surrounded by word boundaries. This is \
equivalent to putting \\b before and after all of the search \
patterns."
);
doc!(h, "after-context", "Show NUM lines after each match.");
doc!(h, "before-context", "Show NUM lines before each match.");
doc!(h, "context", "Show NUM lines before and after each match.");
doc!(
h,
"column",
"Show column numbers",
"Show column numbers (1-based). This only shows the column \
numbers for the first match on each line. This does not try \
to account for Unicode. One byte is equal to one column. This \
implies --line-number."
);
doc!(
h,
"context-separator",
"Set the context separator string. [default: --]",
"The string used to separate non-contiguous context lines in the \
output. Escape sequences like \\x7F or \\t may be used. The \
default value is --."
);
doc!(
h,
"debug",
"Show debug messages.",
"Show debug messages. Please use this when filing a bug report."
);
doc!(
h,
"file",
"Search for patterns from the given file.",
"Search for patterns from the given file, with one pattern per \
line. When this flag is used or multiple times or in \
combination with the -e/--regexp flag, then all patterns \
provided are searched. Empty pattern lines will match all input \
lines, and the newline is not counted as part of the pattern."
);
doc!(
h,
"files-with-matches",
"Only show the path of each file with at least one match."
);
doc!(
h,
"files-without-match",
"Only show the path of each file that contains zero matches."
);
doc!(
h,
"with-filename",
"Show file name for each match.",
"Prefix each match with the file name that contains it. This is \
the default when more than one file is searched."
);
doc!(
h,
"no-filename",
"Never show the file name for a match.",
"Never show the file name for a match. This is the default when \
one file is searched."
);
doc!(
h,
"heading",
"Show matches grouped by each file.",
"This shows the file name above clusters of matches from each \
file instead of showing the file name for every match. This is \
the default mode at a tty."
);
doc!(
h,
"no-heading",
"Don't group matches by each file.",
"Don't group matches by each file. If -H/--with-filename is \
enabled, then file names will be shown for every line matched. \
This is the default mode when not at a tty."
);
doc!(
h,
"hidden",
"Search hidden files and directories.",
"Search hidden files and directories. By default, hidden files \
and directories are skipped."
);
doc!(
h,
"ignore-file",
"Specify additional ignore files.",
"Specify additional ignore files for filtering file paths. \
Ignore files should be in the gitignore format and are matched \
relative to the current working directory. These ignore files \
have lower precedence than all other ignore files. When \
specifying multiple ignore files, earlier files have lower \
precedence than later files."
);
doc!(h, "follow", "Follow symbolic links.");
doc!(
h,
"max-count",
"Limit the number of matches.",
"Limit the number of matching lines per file searched to NUM."
);
doc!(
h,
"maxdepth",
"Descend at most NUM directories.",
"Limit the depth of directory traversal to NUM levels beyond \
the paths given. A value of zero only searches the \
starting-points themselves.\n\nFor example, \
'rg --maxdepth 0 dir/' is a no-op because dir/ will not be \
descended into. 'rg --maxdepth 1 dir/' will search only the \
direct children of dir/."
);
doc!(
h,
"mmap",
"Searching using memory maps when possible.",
"Search using memory maps when possible. This is enabled by \
default when ripgrep thinks it will be faster. Note that memory \
map searching doesn't currently support all options, so if an \
incompatible option (e.g., --context) is given with --mmap, \
then memory maps will not be used."
);
doc!(
h,
"no-messages",
"Suppress all error messages.",
"Suppress all error messages. This is equivalent to redirecting \
stderr to /dev/null."
);
doc!(
h,
"no-mmap",
"Never use memory maps.",
"Never use memory maps, even when they might be faster."
);
doc!(
h,
"no-ignore",
"Don't respect ignore files.",
"Don't respect ignore files (.gitignore, .ignore, etc.). This \
implies --no-ignore-parent and --no-ignore-vcs."
);
doc!(
h,
"no-ignore-parent",
"Don't respect ignore files in parent directories.",
"Don't respect ignore files (.gitignore, .ignore, etc.) in \
parent directories."
);
doc!(
h,
"no-ignore-vcs",
"Don't respect VCS ignore files",
"Don't respect version control ignore files (.gitignore, etc.). \
This implies --no-ignore-parent. Note that .ignore files will \
continue to be respected."
);
doc!(
h,
"null",
"Print NUL byte after file names",
"Whenever a file name is printed, follow it with a NUL byte. \
This includes printing file names before matches, and when \
printing a list of matching files such as with --count, \
--files-with-matches and --files. This option is useful for use \
with xargs."
);
doc!(
h,
"path-separator",
"Path separator to use when printing file paths.",
"The path separator to use when printing file paths. This \
defaults to your platform's path separator, which is / on Unix \
and \\ on Windows. This flag is intended for overriding the \
default when the environment demands it (e.g., cygwin). A path \
separator is limited to a single byte."
);
doc!(h, "pretty", "Alias for --color always --heading -n.");
doc!(
h,
"replace",
"Replace matches with string given.",
"Replace every match with the string given when printing \
results. Neither this flag nor any other flag will modify your \
files.\n\nCapture group indices (e.g., $5) and names \
(e.g., $foo) are supported in the replacement string.\n\n\
Note that the replacement by default replaces each match, and \
NOT the entire line. To replace the entire line, you should \
match the entire line."
);
doc!(
h,
"case-sensitive",
"Search case sensitively.",
"Search case sensitively. This overrides -i/--ignore-case and \
-S/--smart-case."
);
doc!(
h,
"smart-case",
"Smart case search.",
"Searches case insensitively if the pattern is all lowercase. \
Search case sensitively otherwise. This is overridden by \
either -s/--case-sensitive or -i/--ignore-case."
);
doc!(
h,
"sort-files",
"Sort results by file path. Implies --threads=1.",
"Sort results by file path. Note that this currently \
disables all parallelism and runs search in a single thread."
);
doc!(
h,
"threads",
"The approximate number of threads to use.",
"The approximate number of threads to use. A value of 0 (which \
is the default) causes ripgrep to choose the thread count \
using heuristics."
);
doc!(
h,
"vimgrep",
"Show results in vim compatible format.",
"Show results with every match on its own line, including \
line numbers and column numbers. With this option, a line with \
more than one match will be printed more than once."
);
doc!(
h,
"type-add",
"Add a new glob for a file type.",
"Add a new glob for a particular file type. Only one glob can be \
added at a time. Multiple --type-add flags can be provided. \
Unless --type-clear is used, globs are added to any existing \
globs defined inside of ripgrep.\n\nNote that this MUST be \
passed to every invocation of ripgrep. Type settings are NOT \
persisted.\n\nExample: \
rg --type-add 'foo:*.foo' -tfoo PATTERN.\n\n\
--type-add can also be used to include rules from other types \
with the special include directive. The include directive \
permits specifying one or more other type names (separated by a \
comma) that have been defined and its rules will automatically \
be imported into the type specified. For example, to create a \
type called src that matches C++, Python and Markdown files, one \
can use:\n\n\
--type-add 'src:include:cpp,py,md'\n\n\
Additional glob rules can still be added to the src type by \
using the --type-add flag again:\n\n\
--type-add 'src:include:cpp,py,md' --type-add 'src:*.foo'\n\n\
Note that type names must consist only of Unicode letters or \
numbers. Punctuation characters are not allowed."
);
doc!(
h,
"type-clear",
"Clear globs for given file type.",
"Clear the file type globs previously defined for TYPE. This \
only clears the default type definitions that are found inside \
of ripgrep.\n\nNote that this MUST be passed to every \
invocation of ripgrep. Type settings are NOT persisted."
);
h
};
}
fn validate_number(s: &str) -> Result<(), String> {
s.parse::<usize>()
.map(|_| ())
.map_err(|err| err.to_string())
}
criterion_group!(
benches,
build_rg_with_short_help,
build_rg_with_long_help,
write_rg_short_help,
write_rg_long_help,
parse_rg,
parse_rg_with_complex,
parse_rg_with_lots
);
criterion_main!(benches);

View File

@ -0,0 +1,408 @@
// Used to simulate a fairly large number of subcommands
//
// CLI used is from rustup 408ed84f0e50511ed44a405dd91365e5da588790
use clap::{App, AppSettings, Arg, ArgGroup};
use criterion::{criterion_group, criterion_main, Criterion};
pub fn build_rustup(c: &mut Criterion) {
c.bench_function("build_rustup", |b| b.iter(build_cli));
}
pub fn parse_rustup(c: &mut Criterion) {
c.bench_function("parse_rustup", |b| {
b.iter(|| build_cli().get_matches_from(vec![""]))
});
}
pub fn parse_rustup_with_sc(c: &mut Criterion) {
c.bench_function("parse_rustup_with_sc", |b| {
b.iter(|| build_cli().get_matches_from(vec!["rustup override add stable"]))
});
}
fn build_cli() -> App<'static> {
App::new("rustup")
.version("0.9.0") // Simulating
.about("The Rust toolchain installer")
.after_help(RUSTUP_HELP)
.setting(AppSettings::DeriveDisplayOrder)
// .setting(AppSettings::SubcommandRequiredElseHelp)
.arg(
Arg::new("verbose")
.help("Enable verbose output")
.short('v')
.long("verbose"),
)
.subcommand(
App::new("show")
.about("Show the active and installed toolchains")
.after_help(SHOW_HELP),
)
.subcommand(
App::new("install")
.about("Update Rust toolchains")
.after_help(TOOLCHAIN_INSTALL_HELP)
.setting(AppSettings::Hidden) // synonym for 'toolchain install'
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("update")
.about("Update Rust toolchains")
.after_help(UPDATE_HELP)
.arg(Arg::new("toolchain").required(true))
.arg(
Arg::new("no-self-update")
.help("Don't perform self update when running the `rustup` command")
.long("no-self-update")
.hide(true),
),
)
.subcommand(
App::new("default")
.about("Set the default toolchain")
.after_help(DEFAULT_HELP)
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("toolchain")
.about("Modify or query the installed toolchains")
.after_help(TOOLCHAIN_HELP)
.setting(AppSettings::DeriveDisplayOrder)
// .setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(App::new("list").about("List installed toolchains"))
.subcommand(
App::new("install")
.about("Install or update a given toolchain")
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("uninstall")
.about("Uninstall a toolchain")
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("link")
.about("Create a custom toolchain by symlinking to a directory")
.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").required(true)),
)
.subcommand(
App::new("add")
.setting(AppSettings::Hidden) // synonym for 'install'
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("remove")
.setting(AppSettings::Hidden) // synonym for 'uninstall'
.arg(Arg::new("toolchain").required(true)),
),
)
.subcommand(
App::new("target")
.about("Modify a toolchain's supported targets")
.setting(AppSettings::DeriveDisplayOrder)
// .setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
App::new("list")
.about("List installed and available targets")
.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").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").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").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").required(true))
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
),
)
.subcommand(
App::new("component")
.about("Modify a toolchain's installed components")
.setting(AppSettings::DeriveDisplayOrder)
// .setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
App::new("list")
.about("List installed and available components")
.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").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").required(true))
.arg(Arg::new("toolchain").long("toolchain").takes_value(true))
.arg(Arg::new("target").long("target").takes_value(true)),
),
)
.subcommand(
App::new("override")
.about("Modify directory toolchain overrides")
.after_help(OVERRIDE_HELP)
.setting(AppSettings::DeriveDisplayOrder)
// .setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(App::new("list").about("List directory toolchain overrides"))
.subcommand(
App::new("set")
.about("Set the override toolchain for a directory")
.arg(Arg::new("toolchain").required(true)),
)
.subcommand(
App::new("unset")
.about("Remove the override toolchain for a directory")
.after_help(OVERRIDE_UNSET_HELP)
.arg(
Arg::new("path")
.long("path")
.takes_value(true)
.help("Path to the directory"),
)
.arg(
Arg::new("nonexistent")
.long("nonexistent")
.help("Remove override toolchain for all nonexistent directories"),
),
)
.subcommand(
App::new("add")
.setting(AppSettings::Hidden) // synonym for 'set'
.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").takes_value(true))
.arg(
Arg::new("nonexistent")
.long("nonexistent")
.help("Remove override toolchain for all nonexistent directories"),
),
),
)
.subcommand(
App::new("run")
.about("Run a command with an environment configured for a given toolchain")
.after_help(RUN_HELP)
.setting(AppSettings::TrailingVarArg)
.arg(Arg::new("toolchain").required(true))
.arg(
Arg::new("command")
.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").required(true)),
)
.subcommand(
App::new("doc")
.about("Open the documentation for the current toolchain")
.after_help(DOC_HELP)
.arg(
Arg::new("book")
.long("book")
.help("The Rust Programming Language book"),
)
.arg(
Arg::new("std")
.long("std")
.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").required(true))
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
)
.subcommand(
App::new("self")
.about("Modify the rustup installation")
.setting(AppSettings::DeriveDisplayOrder)
.subcommand(App::new("update").about("Download and install updates to rustup"))
.subcommand(
App::new("uninstall")
.about("Uninstall rustup.")
.arg(Arg::new("no-prompt").short('y')),
)
.subcommand(App::new("upgrade-data").about("Upgrade the internal data format.")),
)
.subcommand(
App::new("telemetry")
.about("rustup telemetry commands")
.setting(AppSettings::Hidden)
.setting(AppSettings::DeriveDisplayOrder)
.subcommand(App::new("enable").about("Enable rustup telemetry"))
.subcommand(App::new("disable").about("Disable rustup telemetry"))
.subcommand(App::new("analyze").about("Analyze stored telemetry")),
)
.subcommand(
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").required(true)),
),
)
}
static RUSTUP_HELP: &str = r"
rustup installs The Rust Programming Language from the official
release channels, enabling you to easily switch between stable, beta,
and nightly compilers and keep them updated. It makes cross-compiling
simpler with binary builds of the standard library for common platforms.
If you are new to Rust consider running `rustup doc --book`
to learn Rust.";
static SHOW_HELP: &str = r"
Shows the name of the active toolchain and the version of `rustc`.
If the active toolchain has installed support for additional
compilation targets, then they are listed as well.
If there are multiple toolchains installed then all installed
toolchains are listed as well.";
static UPDATE_HELP: &str = r"
With no toolchain specified, the `update` command updates each of the
installed toolchains from the official release channels, then updates
rustup itself.
If given a toolchain argument then `update` updates that toolchain,
the same as `rustup toolchain install`.
'toolchain' specifies a toolchain name, such as 'stable', 'nightly',
or '1.8.0'. For more information see `rustup help toolchain`.";
static TOOLCHAIN_INSTALL_HELP: &str = r"
Installs a specific rust toolchain.
The 'install' command is an alias for 'rustup update <toolchain>'.
'toolchain' specifies a toolchain name, such as 'stable', 'nightly',
or '1.8.0'. For more information see `rustup help toolchain`.";
static DEFAULT_HELP: &str = r"
Sets the default toolchain to the one specified. If the toolchain is
not already installed then it is installed first.";
static TOOLCHAIN_HELP: &str = r"
Many `rustup` commands deal with *toolchains*, a single installation
of the Rust compiler. `rustup` supports multiple types of
toolchains. The most basic track the official release channels:
'stable', 'beta' and 'nightly'; but `rustup` can also install
toolchains from the official archives, for alternate host platforms,
and from local builds.
Standard release channel toolchain names have the following form:
<channel>[-<date>][-<host>]
<channel> = stable|beta|nightly|<version>
<date> = YYYY-MM-DD
<host> = <target-triple>
'channel' is either a named release channel or an explicit version
number, such as '1.8.0'. Channel names can be optionally appended with
an archive date, as in 'nightly-2014-12-18', in which case the
toolchain is downloaded from the archive for that date.
Finally, the host may be specified as a target triple. This is most
useful for installing a 32-bit compiler on a 64-bit platform, or for
installing the [MSVC-based toolchain] on Windows. For example:
rustup toolchain install stable-x86_64-pc-windows-msvc
For convenience, elements of the target triple that are omitted will be
inferred, so the above could be written:
$ rustup default stable-msvc
Toolchain names that don't name a channel instead can be used to name
custom toolchains with the `rustup toolchain link` command.";
static OVERRIDE_HELP: &str = r"
Overrides configure rustup to use a specific toolchain when
running in a specific directory.
Directories can be assigned their own Rust toolchain with
`rustup override`. When a directory has an override then
any time `rustc` or `cargo` is run inside that directory,
or one of its child directories, the override toolchain
will be invoked.
To pin to a specific nightly:
rustup override set nightly-2014-12-18
Or a specific stable release:
rustup override set 1.0.0
To see the active toolchain use `rustup show`. To remove the override
and use the default toolchain again, `rustup override unset`.";
static OVERRIDE_UNSET_HELP: &str = r"
If `--path` argument is present, removes the override toolchain for
the specified directory. If `--nonexistent` argument is present, removes
the override toolchain for all nonexistent directories. Otherwise,
removes the override toolchain for the current directory.";
static RUN_HELP: &str = r"
Configures an environment to use the given toolchain and then runs
the specified program. The command may be any program, not just
rustc or cargo. This can be used for testing arbitrary toolchains
without setting an override.
Commands explicitly proxied by `rustup` (such as `rustc` and `cargo`)
also have a shorthand for this available. The toolchain can be set by
using `+toolchain` as the first argument. These are equivalent:
cargo +nightly build
rustup run nightly cargo build";
static DOC_HELP: &str = r"
Opens the documentation for the currently active toolchain with the
default browser.
By default, it opens the documentation index. Use the various flags to
open specific pieces of documentation.";
criterion_group!(benches, build_rustup, parse_rustup, parse_rustup_with_sc);
criterion_main!(benches);

View File

@ -1,86 +0,0 @@
#[allow(unused_imports, dead_code)]
mod test {
use std::str;
use std::io::{Cursor, Write};
use regex::Regex;
use clap::{App, Arg, SubCommand, ArgGroup};
fn compare<S, S2>(l: S, r: S2) -> bool
where S: AsRef<str>,
S2: AsRef<str>
{
let re = Regex::new("\x1b[^m]*m").unwrap();
// Strip out any mismatching \r character on windows that might sneak in on either side
let ls = l.as_ref().trim().replace("\r", "");
let rs = r.as_ref().trim().replace("\r", "");
let left = re.replace_all(&*ls, "");
let right = re.replace_all(&*rs, "");
let b = left == right;
if !b {
println!();
println!("--> left");
println!("{}", left);
println!("--> right");
println!("{}", right);
println!("--")
}
b
}
pub fn compare_output(l: App, args: &str, right: &str, stderr: bool) -> bool {
let mut buf = Cursor::new(Vec::with_capacity(50));
let res = l.get_matches_from_safe(args.split(' ').collect::<Vec<_>>());
let err = res.unwrap_err();
err.write_to(&mut buf).unwrap();
let content = buf.into_inner();
let left = String::from_utf8(content).unwrap();
assert_eq!(stderr, err.use_stderr());
compare(left, right)
}
pub fn compare_output2(l: App, args: &str, right1: &str, right2: &str, stderr: bool) -> bool {
let mut buf = Cursor::new(Vec::with_capacity(50));
let res = l.get_matches_from_safe(args.split(' ').collect::<Vec<_>>());
let err = res.unwrap_err();
err.write_to(&mut buf).unwrap();
let content = buf.into_inner();
let left = String::from_utf8(content).unwrap();
assert_eq!(stderr, err.use_stderr());
compare(&*left, right1) || compare(&*left, right2)
}
// Legacy tests from the Python script days
pub fn complex_app() -> App<'static, 'static> {
let args = "-o --option=[opt]... 'tests options'
[positional] 'tests positionals'";
let opt3_vals = ["fast", "slow"];
let pos3_vals = ["vi", "emacs"];
App::new("clap-test")
.version("v1.4.8")
.about("tests clap library")
.author("Kevin K. <kbknapp@gmail.com>")
.args_from_usage(args)
.arg(Arg::from_usage("-f --flag... 'tests flags'")
.global(true))
.args(&[
Arg::from_usage("[flag2] -F 'tests flags with exclusions'").conflicts_with("flag").requires("long-option-2"),
Arg::from_usage("--long-option-2 [option2] 'tests long options with exclusions'").conflicts_with("option").requires("positional2"),
Arg::from_usage("[positional2] 'tests positionals with exclusions'"),
Arg::from_usage("-O --Option [option3] 'specific vals'").possible_values(&opt3_vals),
Arg::from_usage("[positional3]... 'tests specific values'").possible_values(&pos3_vals),
Arg::from_usage("--multvals [one] [two] 'Tests multiple values, not mult occs'"),
Arg::from_usage("--multvalsmo... [one] [two] 'Tests multiple values, and mult occs'"),
Arg::from_usage("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2),
Arg::from_usage("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3)
])
.subcommand(SubCommand::with_name("subcmd")
.about("tests subcommands")
.version("0.1")
.author("Kevin K. <kbknapp@gmail.com>")
.arg_from_usage("-o --option [scoption]... 'tests options'")
.arg_from_usage("-s --subcmdarg [subcmdarg] 'tests other args'")
.arg_from_usage("[scpositional] 'tests positionals'"))
}
}

View File

@ -0,0 +1,20 @@
# Examples
- Basic demo: [derive](demo.md)
- Key-value pair arguments: [derive](keyvalue-derive.md)
- Custom cargo command: [builder](cargo-example.md), [derive](cargo-example-derive.md)
- git-like interface: [builder](git.md), [derive](git-derive.md)
- pacman-like interface: [builder](pacman.md)
- Escaped positionals with `--`: [builder](escaped-positional.md), [derive](escaped-positional-derive.md)
- Multi-call
- busybox: [builder](multicall-busybox.md)
- hostname: [builder](multicall-hostname.md)
## Contributing
New examples:
- Building: They must be added to [Cargo.toml](../../Cargo.toml) with the appropriate `required-features`.
- Testing: Ensure there is a markdown file with [trycmd](https://docs.rs/trycmd) syntax
- Link the `.md` file from here
See also the general [CONTRIBUTING](../../CONTRIBUTING.md).

View File

@ -0,0 +1,45 @@
*Jump to [source](cargo-example-derive.rs)*
For more on creating a custom subcommand, see [the cargo
book](https://doc.rust-lang.org/cargo/reference/external-tools.html#custom-subcommands).
The crate [`clap-cargo`](https://github.com/crate-ci/clap-cargo) can help in
mimicking cargo's interface.
The help looks like:
```console
$ cargo-example-derive --help
cargo
USAGE:
cargo <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
SUBCOMMANDS:
example-derive A simple to use, efficient, and full-featured Command Line Argument Parser
help Print this message or the help of the given subcommand(s)
$ cargo-example-derive example-derive --help
cargo-example-derive [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
cargo example-derive [OPTIONS]
OPTIONS:
-h, --help Print help information
--manifest-path <MANIFEST_PATH>
-V, --version Print version information
```
Then to directly invoke the command, run:
```console
$ cargo-example-derive example-derive
None
$ cargo-example-derive example-derive --manifest-path Cargo.toml
Some("Cargo.toml")
```

View File

@ -0,0 +1,20 @@
use clap::Parser;
#[derive(Parser)]
#[clap(name = "cargo")]
#[clap(bin_name = "cargo")]
enum Cargo {
ExampleDerive(ExampleDerive),
}
#[derive(clap::Args)]
#[clap(author, version, about, long_about = None)]
struct ExampleDerive {
#[clap(long, parse(from_os_str))]
manifest_path: Option<std::path::PathBuf>,
}
fn main() {
let Cargo::ExampleDerive(args) = Cargo::parse();
println!("{:?}", args.manifest_path);
}

View File

@ -0,0 +1,45 @@
*Jump to [source](cargo-example.rs)*
For more on creating a custom subcommand, see [the cargo
book](https://doc.rust-lang.org/cargo/reference/external-tools.html#custom-subcommands).
The crate [`clap-cargo`](https://github.com/crate-ci/clap-cargo) can help in
mimicking cargo's interface.
The help looks like:
```console
$ cargo-example --help
cargo
USAGE:
cargo <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
SUBCOMMANDS:
example A simple to use, efficient, and full-featured Command Line Argument Parser
help Print this message or the help of the given subcommand(s)
$ cargo-example example --help
cargo-example [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
cargo example [OPTIONS]
OPTIONS:
-h, --help Print help information
--manifest-path <PATH>
-V, --version Print version information
```
Then to directly invoke the command, run:
```console
$ cargo-example example
None
$ cargo-example example --manifest-path Cargo.toml
Some("Cargo.toml")
```

View File

@ -0,0 +1,21 @@
fn main() {
let app = clap::App::new("cargo")
.bin_name("cargo")
.setting(clap::AppSettings::SubcommandRequired)
.subcommand(
clap::app_from_crate!().name("example").arg(
clap::arg!(--"manifest-path" <PATH>)
.required(false)
.allow_invalid_utf8(true),
),
);
let matches = app.get_matches();
let matches = match matches.subcommand() {
Some(("example", matches)) => matches,
_ => unreachable!("clap should ensure we don't get here"),
};
let manifest_path = matches
.value_of_os("manifest-path")
.map(std::path::PathBuf::from);
println!("{:?}", manifest_path);
}

20
third_party/rust/clap/examples/demo.md vendored Normal file
View File

@ -0,0 +1,20 @@
*Jump to [source](demo.rs)*
**This requires enabling the `derive` feature flag.**
Used to validate README.md's content
```console
$ demo --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
demo[EXE] [OPTIONS] --name <NAME>
OPTIONS:
-c, --count <COUNT> Number of times to greet [default: 1]
-h, --help Print help information
-n, --name <NAME> Name of the person to greet
-V, --version Print version information
```

24
third_party/rust/clap/examples/demo.rs vendored Normal file
View File

@ -0,0 +1,24 @@
// Note: this requires the `derive` feature
use clap::Parser;
/// Simple program to greet a person
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
/// Name of the person to greet
#[clap(short, long)]
name: String,
/// Number of times to greet
#[clap(short, long, default_value_t = 1)]
count: u8,
}
fn main() {
let args = Args::parse();
for _ in 0..args.count {
println!("Hello {}!", args.name)
}
}

View File

@ -0,0 +1,351 @@
# Derive Reference
1. [Overview](#overview)
2. [Raw Attributes](#raw-attributes)
3. [Magic Attributes](#magic-attributes)
1. [App Attributes](#app-attributes)
2. [Arg Attributes](#arg-attributes)
3. [Arg Types](#arg-types)
4. [Arg Enum Attributes](#arg-enum-attributes)
5. [Possible Value Attributes](#possible-value-attributes)
6. [Doc Comments](#doc-comments)
## Overview
To derive `clap` types, you need to enable the `derive` feature flag.
See [demo.rs](../demo.rs) and [demo.md](../demo.md) for a brief example.
Let's start by breaking down what can go where:
```rust
use clap::{Parser, Args, Subcommand, ArgEnum};
/// Doc comment
#[derive(Parser)]
#[clap(APP ATTRIBUTE)]
struct Cli {
/// Doc comment
#[clap(ARG ATTRIBUTE)]
field: Type,
#[clap(flatten)]
delegate: Struct,
#[clap(subcommand)]
command: Command,
}
/// Doc comment
#[derive(Args)]
#[clap(PARENT APP ATTRIBUTE)]
struct Struct {
/// Doc comment
#[clap(ARG ATTRIBUTE)]
field: Type,
}
/// Doc comment
#[derive(Subcommand)]
#[clap(PARENT APP ATTRIBUTE)]
enum Command {
/// Doc comment
#[clap(APP ATTRIBUTE)]
Variant1(Struct),
/// Doc comment
#[clap(APP ATTRIBUTE)]
Variant2 {
/// Doc comment
#[clap(ARG ATTRIBUTE)]
field: Type,
}
}
/// Doc comment
#[derive(ArgEnum)]
#[clap(ARG ENUM ATTRIBUTE)]
enum Mode {
/// Doc comment
#[clap(POSSIBLE VALUE ATTRIBUTE)]
Variant1,
}
fn main() {
let cli = Cli::parse();
}
```
- `Parser` parses arguments into a `struct` (arguments) or `enum` (subcommands).
- `Args` allows defining a set of re-usable arguments that get merged into their parent container.
- `Subcommand` defines available subcommands.
- `ArgEnum` allows parsing a value directly into an `enum`, erroring on unsupported values.
See also the [tutorial](../tutorial_derive/README.md) and [examples](../README.md).
## Raw Attributes
**Raw attributes** are forwarded directly to the underlying `clap` builder. Any
`App`, `Arg`, or `PossibleValue` method can be used as an attribute.
Raw attributes come in two different syntaxes:
```rust
#[clap(
global = true, // name = arg form, neat for one-arg methods
required_if_eq("out", "file") // name(arg1, arg2, ...) form.
)]
```
- `method = arg` can only be used for methods which take only one argument.
- `method(arg1, arg2)` can be used with any method.
As long as `method_name` is not one of the magical methods - it will be
translated into a mere method call.
## Magic Attributes
**Magic attributes** have post-processing done to them, whether that is
- Providing of defaults
- Special behavior is triggered off of it
### App Attributes
These correspond to a `clap::App` which is used for both top-level parsers and
when defining subcommands.
In addition to the raw attributes, the following magic attributes are supported:
- `name = <expr>`: `clap::App::name`
- When not present: [crate `name`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-name-field) (`Parser` container), variant name (`Subcommand` variant)
- `version [= <expr>]`: `clap::App::version`
- When not present: no version set
- Without `<expr>`: defaults to [crate `version`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field)
- `author [= <expr>]`: `clap::App::author`
- When not present: no author set
- Without `<expr>`: defaults to [crate `authors`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-authors-field)
- `about [= <expr>]`: `clap::App::about`
- When not present: [Doc comment summary](#doc-comments)
- Without `<expr>`: [crate `description`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-description-field) (`Parser` container)
- **TIP:** When a doc comment is also present, you most likely want to add
`#[clap(long_about = None)]` to clear the doc comment so only `about`
gets shown with both `-h` and `--help`.
- `long_about = <expr>`: `clap::App::long_about`
- When not present: [Doc comment](#doc-comments) if there is a blank line, else nothing
- `verbatim_doc_comment`: Minimizes pre-processing when converting doc comments to `about` / `long_about`
- `help_heading`: `clap::App::help_heading`
- When `flatten`ing `Args`, this is scoped to just the args in this struct and any struct `flatten`ed into it
- `rename_all = <expr>`: Override default field / variant name case conversion for `App::name` / `Arg::name`
- When not present: `kebab-case`
- Available values: `camelCase`, `kebab-case`, `PascalCase`, `SCREAMING_SNAKE_CASE`, `snake_case`, `lower`, `UPPER`, `verbatim`
- `rename_all_env = <expr>`: Override default field name case conversion for env variables for `clap::Arg::env`
- When not present: `SCREAMING_SNAKE_CASE`
- Available values: `camelCase`, `kebab-case`, `PascalCase`, `SCREAMING_SNAKE_CASE`, `snake_case`, `lower`, `UPPER`, `verbatim`
And for `Subcommand` variants:
- `skip`: Ignore this variant
- `flatten`: Delegates to the variant for more subcommands (must implement `Subcommand`)
- `subcommand`: Nest subcommands under the current set of subcommands (must implement `Subcommand`)
- `external_subcommand`: `clap::AppSettings::AllowExternalSubcommand`
- Variant must be either `Variant(Vec<String>)` or `Variant(Vec<OsString>)`
### Arg Attributes
These correspond to a `clap::Arg`.
In addition to the raw attributes, the following magic attributes are supported:
- `name = <expr>`: `clap::Arg::new`
- When not present: case-converted field name is used
- `help = <expr>`: `clap::Arg::help`
- When not present: [Doc comment summary](#doc-comments)
- `long_help = <expr>`: `clap::Arg::long_help`
- When not present: [Doc comment](#doc-comments) if there is a blank line, else nothing
- `verbatim_doc_comment`: Minimizes pre-processing when converting doc comments to `help` / `long_help`
- `short [= <char>]`: `clap::Arg::short`
- When not present: no short set
- Without `<char>`: defaults to first character in the case-converted field name
- `long [= <str>]`: `clap::Arg::long`
- When not present: no long set
- Without `<str>`: defaults to the case-converted field name
- `env [= <str>]`: `clap::Arg::env`
- When not present: no env set
- Without `<str>`: defaults to the case-converted field name
- `flatten`: Delegates to the field for more arguments (must implement `Args`)
- Only `help_heading` can be used with `flatten`. See
[clap-rs/clap#3269](https://github.com/clap-rs/clap/issues/3269) for why
arg attributes are not generally supported.
- **Tip:** Though we do apply a flattened `Args`'s Parent App Attributes, this
makes reuse harder. Generally prefer putting the app attributes on the `Parser`
or on the flattened field.
- `subcommand`: Delegates definition of subcommands to the field (must implement `Subcommand`)
- When `Option<T>`, the subcommand becomes optional
- `from_global`: Read a `clap::Arg::global` argument (raw attribute), regardless of what subcommand you are in
- `parse(<kind> [= <function>])` `clap::Arg::validator`
- `arg_enum`: Parse the value using the `ArgEnum` trait
- `skip [= <expr>]`: Ignore this field, filling in with `<expr>`
- Without `<expr>`: fills the field with `Default::default()`
- `default_value = <str>`: `clap::Arg::default_value` and `clap::Arg::required(false)`
- `default_value_t [= <expr>]`: `clap::Arg::default_value` and `clap::Arg::required(false)`
- Requires `std::fmt::Display` or `#[clap(arg_enum)]`
- Without `<expr>`, relies on `Default::default()`
### Arg Types
`clap` assumes some intent based on the type used:
| Type | Effect | Implies |
|---------------------|--------------------------------------|------------------------------------------------------------------|
| `bool` | flag | `#[clap(parser(from_flag))]` |
| `Option<T>` | optional argument | `.takes_value(true).required(false)` |
| `Option<Option<T>>` | optional value for optional argument | `.takes_value(true).required(false).min_values(0).max_values(1)` |
| `T` | required argument | `.takes_value(true).required(!has_default)` |
| `Vec<T>` | `0..` occurrences of argument | `.takes_value(true).required(false).multiple_occurrences(true)` |
| `Option<Vec<T>>` | `0..` occurrences of argument | `.takes_value(true).required(false).multiple_occurrences(true)` |
Notes:
- For custom type behavior, you can override the implied attributes/settings and/or set additional ones
- For example, see [custom-bool](./custom-bool.md)
- `Option<Vec<T>>` will be `None` instead of `vec![]` if no arguments are provided.
- This gives the user some flexibility in designing their argument, like with `min_values(0)`
You can then support your custom type with `#[clap(parse(<kind> [= <function>]))]`:
| `<kind>` | Signature | Default `<function>` |
|--------------------------|---------------------------------------|---------------------------------|
| `from_str` | `fn(&str) -> T` | `::std::convert::From::from` |
| `try_from_str` (default) | `fn(&str) -> Result<T, E>` | `::std::str::FromStr::from_str` |
| `from_os_str` | `fn(&OsStr) -> T` | `::std::convert::From::from` |
| `try_from_os_str` | `fn(&OsStr) -> Result<T, OsString>` | (no default function) |
| `from_occurrences` | `fn(u64) -> T` | `value as T` |
| `from_flag` | `fn(bool) -> T` | `::std::convert::From::from` |
Notes:
- `from_os_str`:
- Implies `arg.takes_value(true).allow_invalid_utf8(true)`
- `try_from_os_str`:
- Implies `arg.takes_value(true).allow_invalid_utf8(true)`
- `from_occurrences`:
- Implies `arg.takes_value(false).multiple_occurrences(true)`
- Reads from `clap::ArgMatches::occurrences_of` rather than a `value_of` function
- `from_flag`
- Implies `arg.takes_value(false)`
- Reads from `clap::ArgMatches::is_present` rather than a `value_of` function
**Warning:**
- To support non-UTF8 paths, you must use `parse(from_os_str)`, otherwise
`clap` will use `clap::ArgMatches::value_of` with `PathBuf::FromStr`.
### Arg Enum Attributes
- `rename_all = <expr>`: Override default field / variant name case conversion for `PossibleValue::new`
- When not present: `kebab-case`
- Available values: `camelCase`, `kebab-case`, `PascalCase`, `SCREAMING_SNAKE_CASE`, `snake_case`, `lower`, `UPPER`, `verbatim`
### Possible Value Attributes
These correspond to a `clap::PossibleValue`.
- `name = <expr>`: `clap::PossibleValue::new`
- When not present: case-converted field name is used
- `help = <expr>`: `clap::PossibleValue::help`
- When not present: [Doc comment summary](#doc-comments)
### Doc Comments
In clap, help messages for the whole binary can be specified
via [`App::about`] and [`App::long_about`] while help messages
for individual arguments can be specified via [`Arg::help`] and [`Arg::long_help`]".
`long_*` variants are used when user calls the program with
`--help` and "short" variants are used with `-h` flag.
```rust
# use clap::Parser;
#[derive(Parser)]
#[clap(about = "I am a program and I work, just pass `-h`", long_about = None)]
struct Foo {
#[clap(short, help = "Pass `-h` and you'll see me!")]
bar: String,
}
```
For convenience, doc comments can be used instead of raw methods
(this example works exactly like the one above):
```rust
# use clap::Parser;
#[derive(Parser)]
/// I am a program and I work, just pass `-h`
struct Foo {
/// Pass `-h` and you'll see me!
bar: String,
}
```
**NOTE:** Attributes have priority over doc comments!
**Top level doc comments always generate `App::about/long_about` calls!**
If you really want to use the `App::about/long_about` methods (you likely don't),
use the `about` / `long_about` attributes to override the calls generated from
the doc comment. To clear `long_about`, you can use
`#[clap(long_about = None)]`.
**TIP:** Set `#![deny(missing_docs)]` to catch missing `--help` documentation at compile time.
#### Pre-processing
```rust
# use clap::Parser;
#[derive(Parser)]
/// Hi there, I'm Robo!
///
/// I like beeping, stumbling, eating your electricity,
/// and making records of you singing in a shower.
/// Pay up, or I'll upload it to youtube!
struct Robo {
/// Call my brother SkyNet.
///
/// I am artificial superintelligence. I won't rest
/// until I'll have destroyed humanity. Enjoy your
/// pathetic existence, you mere mortals.
#[clap(long)]
kill_all_humans: bool,
}
```
A doc comment consists of three parts:
- Short summary
- A blank line (whitespace only)
- Detailed description, all the rest
The summary corresponds with `App::about` / `Arg::help`. When a blank line is
present, the whole doc comment will be passed to `App::long_about` /
`Arg::long_help`. Or in other words, a doc may result in just a `App::about` /
`Arg::help` or `App::about` / `Arg::help` and `App::long_about` /
`Arg::long_help`
In addition, when `verbatim_doc_comment` is not present, `clap` applies some preprocessing, including:
- Strip leading and trailing whitespace from every line, if present.
- Strip leading and trailing blank lines, if present.
- Interpret each group of non-empty lines as a word-wrapped paragraph.
We replace newlines within paragraphs with spaces to allow the output
to be re-wrapped to the terminal width.
- Strip any excess blank lines so that there is exactly one per paragraph break.
- If the first paragraph ends in exactly one period,
remove the trailing period (i.e. strip trailing periods but not trailing ellipses).
Sometimes you don't want this preprocessing to apply, for example the comment contains
some ASCII art or markdown tables, you would need to preserve LFs along with
blank lines and the leading/trailing whitespace. When you pass use the
`verbatim_doc_comment` magic attribute, you preserve
them.
**Note:** Keep in mind that `verbatim_doc_comment` will *still*
- Remove one leading space from each line, even if this attribute is present,
to allow for a space between `///` and the content.
- Remove leading and trailing blank lines

View File

@ -0,0 +1,47 @@
*Jump to [source](custom-bool.rs)*
Example of overriding the magic `bool` behavior
```console
$ custom-bool --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
custom-bool[EXE] [OPTIONS] --foo <FOO> <BOOM>
ARGS:
<BOOM>
OPTIONS:
--bar <BAR> [default: false]
--foo <FOO>
-h, --help Print help information
-V, --version Print version information
$ custom-bool
? failed
error: The following required arguments were not provided:
--foo <FOO>
<BOOM>
USAGE:
custom-bool[EXE] [OPTIONS] --foo <FOO> <BOOM>
For more information try --help
$ custom-bool --foo true false
[examples/derive_ref/custom-bool.rs:31] opt = Opt {
foo: true,
bar: false,
boom: false,
}
$ custom-bool --foo true --bar true false
[examples/derive_ref/custom-bool.rs:31] opt = Opt {
foo: true,
bar: true,
boom: false,
}
```

View File

@ -0,0 +1,32 @@
use clap::Parser;
#[derive(Parser, Debug, PartialEq)]
#[clap(author, version, about, long_about = None)]
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), default_value_t)]
bar: bool,
// `bool` can be positional only with explicit `parse(...)` annotation
#[clap(parse(try_from_str))]
boom: bool,
}
fn true_or_false(s: &str) -> Result<bool, &'static str> {
match s {
"true" => Ok(true),
"false" => Ok(false),
_ => Err("expected `true` or `false`"),
}
}
fn main() {
let opt = Opt::parse();
dbg!(opt);
}

View File

@ -0,0 +1,65 @@
*Jump to [source](escaped-positional-derive.rs)*
**This requires enabling the `derive` feature flag.**
You can use `--` to escape further arguments.
Let's see what this looks like in the help:
```console
$ escaped-positional-derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
escaped-positional-derive[EXE] [OPTIONS] [-- <SLOP>...]
ARGS:
<SLOP>...
OPTIONS:
-f
-h, --help Print help information
-p <PEAR>
-V, --version Print version information
```
Here is a baseline without any arguments:
```console
$ escaped-positional-derive
-f used: false
-p's value: None
'slops' values: []
```
Notice that we can't pass positional arguments before `--`:
```console
$ escaped-positional-derive foo bar
? failed
error: Found argument 'foo' which wasn't expected, or isn't valid in this context
USAGE:
escaped-positional-derive[EXE] [OPTIONS] [-- <SLOP>...]
For more information try --help
```
But you can after:
```console
$ escaped-positional-derive -f -p=bob -- sloppy slop slop
-f used: true
-p's value: Some("bob")
'slops' values: ["sloppy", "slop", "slop"]
```
As mentioned, the parser will directly pass everything through:
```console
$ escaped-positional-derive -- -f -p=bob sloppy slop slop
-f used: false
-p's value: None
'slops' values: ["-f", "-p=bob", "sloppy", "slop", "slop"]
```

View File

@ -0,0 +1,27 @@
// Note: this requires the `derive` feature
use clap::Parser;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
#[clap(short = 'f')]
eff: bool,
#[clap(short = 'p', value_name = "PEAR")]
pea: Option<String>,
#[clap(last = true)]
slop: Vec<String>,
}
fn main() {
let args = Cli::parse();
// This is what will happen with `myprog -f -p=bob -- sloppy slop slop`...
println!("-f used: {:?}", args.eff); // -f used: true
println!("-p's value: {:?}", args.pea); // -p's value: Some("bob")
println!("'slops' values: {:?}", args.slop); // 'slops' values: Some(["sloppy", "slop", "slop"])
// Continued program logic goes here...
}

View File

@ -0,0 +1,65 @@
*Jump to [source](escaped-positional.rs)*
**This requires enabling the `cargo` feature flag.**
You can use `--` to escape further arguments.
Let's see what this looks like in the help:
```console
$ escaped-positional --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
escaped-positional[EXE] [OPTIONS] [-- <SLOP>...]
ARGS:
<SLOP>...
OPTIONS:
-f
-h, --help Print help information
-p <PEAR>
-V, --version Print version information
```
Here is a baseline without any arguments:
```console
$ escaped-positional
-f used: false
-p's value: None
'slops' values: []
```
Notice that we can't pass positional arguments before `--`:
```console
$ escaped-positional foo bar
? failed
error: Found argument 'foo' which wasn't expected, or isn't valid in this context
USAGE:
escaped-positional[EXE] [OPTIONS] [-- <SLOP>...]
For more information try --help
```
But you can after:
```console
$ escaped-positional -f -p=bob -- sloppy slop slop
-f used: true
-p's value: Some("bob")
'slops' values: ["sloppy", "slop", "slop"]
```
As mentioned, the parser will directly pass everything through:
```console
$ escaped-positional -- -f -p=bob sloppy slop slop
-f used: false
-p's value: None
'slops' values: ["-f", "-p=bob", "sloppy", "slop", "slop"]
```

View File

@ -0,0 +1,26 @@
// Note: this requires the `cargo` feature
use clap::{app_from_crate, arg};
fn main() {
let matches = app_from_crate!()
.arg(arg!(eff: -f))
.arg(arg!(pea: -p <PEAR>).required(false))
.arg(
arg!(slop: [SLOP]).multiple_occurrences(true).last(true), // Indicates that `slop` is only accessible after `--`.
)
.get_matches();
// This is what will happen with `myprog -f -p=bob -- sloppy slop slop`...
println!("-f used: {:?}", matches.is_present("eff")); // -f used: true
println!("-p's value: {:?}", matches.value_of("pea")); // -p's value: Some("bob")
println!(
"'slops' values: {:?}",
matches
.values_of("slop")
.map(|vals| vals.collect::<Vec<_>>())
.unwrap_or_default()
); // 'slops' values: Some(["sloppy", "slop", "slop"])
// Continued program logic goes here...
}

View File

@ -0,0 +1,83 @@
*Jump to [source](git-derive.rs)*
**This requires enabling the `derive` feature flag.**
Git is an example of several common subcommand patterns.
Help:
```console
$ git-derive
? failed
git
A fictional versioning CLI
USAGE:
git-derive[EXE] <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
SUBCOMMANDS:
add adds things
clone Clones repos
help Print this message or the help of the given subcommand(s)
push pushes things
$ git-derive help
git
A fictional versioning CLI
USAGE:
git-derive[EXE] <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
SUBCOMMANDS:
add adds things
clone Clones repos
help Print this message or the help of the given subcommand(s)
push pushes things
$ git-derive help add
git-derive[EXE]-add
adds things
USAGE:
git-derive[EXE] add <PATH>...
ARGS:
<PATH>... Stuff to add
OPTIONS:
-h, --help Print help information
```
A basic argument:
```console
$ git-derive add
? failed
git-derive[EXE]-add
adds things
USAGE:
git-derive[EXE] add <PATH>...
ARGS:
<PATH>... Stuff to add
OPTIONS:
-h, --help Print help information
$ git-derive add Cargo.toml Cargo.lock
Adding ["Cargo.toml", "Cargo.lock"]
```
External subcommands:
```console
$ git-derive custom-tool arg1 --foo bar
Calling out to "custom-tool" with ["arg1", "--foo", "bar"]
```

View File

@ -0,0 +1,61 @@
// Note: this requires the `derive` feature
use std::ffi::OsString;
use std::path::PathBuf;
use clap::{AppSettings, Parser, Subcommand};
/// A fictional versioning CLI
#[derive(Parser)]
#[clap(name = "git")]
#[clap(about = "A fictional versioning CLI", long_about = None)]
struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Clones repos
#[clap(setting(AppSettings::ArgRequiredElseHelp))]
Clone {
/// The remote to clone
remote: String,
},
/// pushes things
#[clap(setting(AppSettings::ArgRequiredElseHelp))]
Push {
/// The remote to target
remote: String,
},
/// adds things
#[clap(setting(AppSettings::ArgRequiredElseHelp))]
Add {
/// Stuff to add
#[clap(required = true, parse(from_os_str))]
path: Vec<PathBuf>,
},
#[clap(external_subcommand)]
External(Vec<OsString>),
}
fn main() {
let args = Cli::parse();
match &args.command {
Commands::Clone { remote } => {
println!("Cloning {}", remote);
}
Commands::Push { remote } => {
println!("Pushing to {}", remote);
}
Commands::Add { path } => {
println!("Adding {:?}", path);
}
Commands::External(args) => {
println!("Calling out to {:?} with {:?}", &args[0], &args[1..]);
}
}
// Continued program logic goes here...
}

81
third_party/rust/clap/examples/git.md vendored Normal file
View File

@ -0,0 +1,81 @@
*Jump to [source](git.rs)*
Git is an example of several common subcommand patterns.
Help:
```console
$ git
? failed
git
A fictional versioning CLI
USAGE:
git[EXE] <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
SUBCOMMANDS:
add adds things
clone Clones repos
help Print this message or the help of the given subcommand(s)
push pushes things
$ git help
git
A fictional versioning CLI
USAGE:
git[EXE] <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
SUBCOMMANDS:
add adds things
clone Clones repos
help Print this message or the help of the given subcommand(s)
push pushes things
$ git help add
git[EXE]-add
adds things
USAGE:
git[EXE] add <PATH>...
ARGS:
<PATH>... Stuff to add
OPTIONS:
-h, --help Print help information
```
A basic argument:
```console
$ git add
? failed
git[EXE]-add
adds things
USAGE:
git[EXE] add <PATH>...
ARGS:
<PATH>... Stuff to add
OPTIONS:
-h, --help Print help information
$ git add Cargo.toml Cargo.lock
Adding ["Cargo.toml", "Cargo.lock"]
```
External subcommands:
```console
$ git custom-tool arg1 --foo bar
Calling out to "custom-tool" with ["arg1", "--foo", "bar"]
```

63
third_party/rust/clap/examples/git.rs vendored Normal file
View File

@ -0,0 +1,63 @@
use std::path::PathBuf;
use clap::{arg, App, AppSettings};
fn main() {
let matches = App::new("git")
.about("A fictional versioning CLI")
.setting(AppSettings::SubcommandRequiredElseHelp)
.setting(AppSettings::AllowExternalSubcommands)
.setting(AppSettings::AllowInvalidUtf8ForExternalSubcommands)
.subcommand(
App::new("clone")
.about("Clones repos")
.arg(arg!(<REMOTE> "The remote to clone"))
.setting(AppSettings::ArgRequiredElseHelp),
)
.subcommand(
App::new("push")
.about("pushes things")
.arg(arg!(<REMOTE> "The remote to target"))
.setting(AppSettings::ArgRequiredElseHelp),
)
.subcommand(
App::new("add")
.about("adds things")
.setting(AppSettings::ArgRequiredElseHelp)
.arg(arg!(<PATH> ... "Stuff to add").allow_invalid_utf8(true)),
)
.get_matches();
match matches.subcommand() {
Some(("clone", sub_matches)) => {
println!(
"Cloning {}",
sub_matches.value_of("REMOTE").expect("required")
);
}
Some(("push", sub_matches)) => {
println!(
"Pushing to {}",
sub_matches.value_of("REMOTE").expect("required")
);
}
Some(("add", sub_matches)) => {
let paths = sub_matches
.values_of_os("PATH")
.unwrap_or_default()
.map(PathBuf::from)
.collect::<Vec<_>>();
println!("Adding {:?}", paths);
}
Some((ext, sub_matches)) => {
let args = sub_matches
.values_of_os("")
.unwrap_or_default()
.collect::<Vec<_>>();
println!("Calling out to {:?} with {:?}", ext, args);
}
_ => unreachable!(), // If all subcommands are defined above, anything else is unreachabe!()
}
// Continued program logic goes here...
}

View File

@ -0,0 +1,31 @@
*Jump to [source](keyvalue-derive.rs)*
**This requires enabling the `derive` feature flag.**
```console
$ keyvalue-derive --help
clap
USAGE:
keyvalue-derive[EXE] [OPTIONS]
OPTIONS:
-D <DEFINES>
-h, --help Print help information
$ keyvalue-derive -D Foo=10 -D Alice=30
Args { defines: [("Foo", 10), ("Alice", 30)] }
$ keyvalue-derive -D Foo
? failed
error: Invalid value for '-D <DEFINES>': invalid KEY=value: no `=` found in `Foo`
For more information try --help
$ keyvalue-derive -D Foo=Bar
? failed
error: Invalid value for '-D <DEFINES>': invalid digit found in string
For more information try --help
```

View File

@ -0,0 +1,29 @@
// Note: this requires the `derive` feature
use clap::Parser;
use std::error::Error;
#[derive(Parser, Debug)]
struct Args {
#[clap(short = 'D', parse(try_from_str = parse_key_val), multiple_occurrences(true))]
defines: Vec<(String, i32)>,
}
/// 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()?))
}
fn main() {
let args = Args::parse();
println!("{:?}", args);
}

View File

@ -0,0 +1,46 @@
*Jump to [source](multicall-busybox.rs)*
Example of a busybox-style multicall program
See the documentation for clap::AppSettings::Multicall for rationale.
This example omits every command except true and false,
which are the most trivial to implement,
```console
$ busybox true
? 0
$ busybox false
? 1
```
*Note: without the links setup, we can't demonstrate the multicall behavior*
But includes the `--install` option as an example of why it can be useful
for the main program to take arguments that aren't applet subcommands.
```console
$ busybox --install
? failed
...
```
Though users must pass something:
```console
$ busybox
? failed
busybox
USAGE:
busybox [OPTIONS] [APPLET]
OPTIONS:
-h, --help Print help information
--install <install> Install hardlinks for all subcommands in path
APPLETS:
false does nothing unsuccessfully
help Print this message or the help of the given subcommand(s)
true does nothing successfully
```

View File

@ -0,0 +1,46 @@
use std::process::exit;
use clap::{App, AppSettings, Arg};
fn applet_commands() -> [App<'static>; 2] {
[
App::new("true").about("does nothing successfully"),
App::new("false").about("does nothing unsuccessfully"),
]
}
fn main() {
let app = App::new(env!("CARGO_CRATE_NAME"))
.setting(AppSettings::Multicall)
.subcommand(
App::new("busybox")
.setting(AppSettings::ArgRequiredElseHelp)
.subcommand_value_name("APPLET")
.subcommand_help_heading("APPLETS")
.arg(
Arg::new("install")
.long("install")
.help("Install hardlinks for all subcommands in path")
.exclusive(true)
.takes_value(true)
.default_missing_value("/usr/local/bin")
.use_delimiter(false),
)
.subcommands(applet_commands()),
)
.subcommands(applet_commands());
let matches = app.get_matches();
let mut subcommand = matches.subcommand();
if let Some(("busybox", cmd)) = subcommand {
if cmd.occurrences_of("install") > 0 {
unimplemented!("Make hardlinks to the executable here");
}
subcommand = cmd.subcommand();
}
match subcommand {
Some(("false", _)) => exit(1),
Some(("true", _)) => exit(0),
_ => unreachable!("parser should ensure only valid subcommand names are used"),
}
}

View File

@ -0,0 +1,14 @@
*Jump to [source](multicall-hostname.rs)*
Example of a `hostname-style` multicall program
See the documentation for clap::AppSettings::Multicall for rationale.
This example omits the implementation of displaying address config
```console
$ hostname
www
```
*Note: without the links setup, we can't demonstrate the multicall behavior*

View File

@ -0,0 +1,18 @@
use clap::{App, AppSettings};
fn main() {
let app = App::new(env!("CARGO_CRATE_NAME"))
.setting(AppSettings::ArgRequiredElseHelp)
.subcommand_value_name("APPLET")
.subcommand_help_heading("APPLETS")
.subcommand(App::new("hostname").about("show hostname part of FQDN"))
.subcommand(App::new("dnsdomainname").about("show domain name part of FQDN"));
let app = app.setting(AppSettings::Multicall);
match app.get_matches().subcommand_name() {
Some("hostname") => println!("www"),
Some("dnsdomainname") => println!("example.com"),
_ => unreachable!("parser should ensure only valid subcommand names are used"),
}
}

View File

@ -0,0 +1,38 @@
*Jump to [source](pacman.rs)*
[`pacman`](https://wiki.archlinux.org/index.php/pacman) defines subcommands via flags.
Here, `-S` is a short flag subcommand:
```console
$ pacman -S package
Installing package...
```
Here `--sync` is a long flag subcommand:
```console
$ pacman --sync package
Installing package...
```
Now the short flag subcommand (`-S`) with a long flag:
```console
$ pacman -S --search name
Searching for name...
```
And the various forms of short flags that work:
```console
$ pacman -S -s name
Searching for name...
$ pacman -Ss name
Searching for name...
```
*(users can "stack" short subcommands with short flags or with other short flag subcommands)*
**NOTE:** Keep in mind that subcommands, flags, and long flags are *case sensitive*: `-Q` and `-q` are different flags/subcommands. For example, you can have both `-Q` subcommand and `-q` flag, and they will be properly disambiguated.
Let's make a quick program to illustrate.

101
third_party/rust/clap/examples/pacman.rs vendored Normal file
View File

@ -0,0 +1,101 @@
use clap::{App, AppSettings, Arg};
fn main() {
let matches = App::new("pacman")
.about("package manager utility")
.version("5.2.1")
.setting(AppSettings::SubcommandRequiredElseHelp)
.author("Pacman Development Team")
// Query subcommand
//
// Only a few of its arguments are implemented below.
.subcommand(
App::new("query")
.short_flag('Q')
.long_flag("query")
.about("Query the package database.")
.arg(
Arg::new("search")
.short('s')
.long("search")
.help("search locally installed packages for matching strings")
.conflicts_with("info")
.takes_value(true)
.multiple_values(true),
)
.arg(
Arg::new("info")
.long("info")
.short('i')
.conflicts_with("search")
.help("view package information")
.takes_value(true)
.multiple_values(true),
),
)
// Sync subcommand
//
// Only a few of its arguments are implemented below.
.subcommand(
App::new("sync")
.short_flag('S')
.long_flag("sync")
.about("Synchronize packages.")
.arg(
Arg::new("search")
.short('s')
.long("search")
.conflicts_with("info")
.takes_value(true)
.multiple_values(true)
.help("search remote repositories for matching strings"),
)
.arg(
Arg::new("info")
.long("info")
.conflicts_with("search")
.short('i')
.help("view package information"),
)
.arg(
Arg::new("package")
.help("packages")
.required_unless_present("search")
.takes_value(true)
.multiple_values(true),
),
)
.get_matches();
match matches.subcommand() {
Some(("sync", sync_matches)) => {
if sync_matches.is_present("search") {
let packages: Vec<_> = sync_matches.values_of("search").unwrap().collect();
let values = packages.join(", ");
println!("Searching for {}...", values);
return;
}
let packages: Vec<_> = sync_matches.values_of("package").unwrap().collect();
let values = packages.join(", ");
if sync_matches.is_present("info") {
println!("Retrieving info for {}...", values);
} else {
println!("Installing {}...", values);
}
}
Some(("query", query_matches)) => {
if let Some(packages) = query_matches.values_of("info") {
let comma_sep = packages.collect::<Vec<_>>().join(", ");
println!("Retrieving info for {}...", comma_sep);
} else if let Some(queries) = query_matches.values_of("search") {
let comma_sep = queries.collect::<Vec<_>>().join(", ");
println!("Searching Locally for {}...", comma_sep);
} else {
println!("Displaying all locally installed packages...");
}
}
_ => unreachable!(), // If all subcommands are defined above, anything else is unreachable
}
}

View File

@ -0,0 +1,58 @@
use clap::{app_from_crate, arg, App};
use std::path::Path;
fn main() {
let matches = app_from_crate!()
.arg(arg!([name] "Optional name to operate on"))
.arg(
arg!(
-c --config <FILE> "Sets a custom config file"
)
// We don't have syntax yet for optional options, so manually calling `required`
.required(false)
// Support non-UTF8 paths
.allow_invalid_utf8(true),
)
.arg(arg!(
-d --debug ... "Turn debugging information on"
))
.subcommand(
App::new("test")
.about("does testing things")
.arg(arg!(-l --list "lists test values")),
)
.get_matches();
// You can check the value provided by positional arguments, or option arguments
if let Some(name) = matches.value_of("name") {
println!("Value for name: {}", name);
}
if let Some(raw_config) = matches.value_of_os("config") {
let config_path = Path::new(raw_config);
println!("Value for config: {}", config_path.display());
}
// You can see how many times a particular flag or argument occurred
// Note, only flags can have multiple occurrences
match matches.occurrences_of("debug") {
0 => println!("Debug mode is off"),
1 => println!("Debug mode is kind of on"),
2 => println!("Debug mode is on"),
_ => println!("Don't be crazy"),
}
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level app
if let Some(matches) = matches.subcommand_matches("test") {
// "$ myapp test" was run
if matches.is_present("list") {
// "$ myapp test -l" was run
println!("Printing testing lists...");
} else {
println!("Not printing testing lists...");
}
}
// Continued program logic goes here...
}

View File

@ -0,0 +1,14 @@
use clap::{app_from_crate, arg, AppSettings};
fn main() {
let matches = app_from_crate!()
.global_setting(AppSettings::AllArgsOverrideSelf)
.global_setting(AppSettings::DeriveDisplayOrder)
.global_setting(AppSettings::AllowNegativeNumbers)
.arg(arg!(--two <VALUE>))
.arg(arg!(--one <VALUE>))
.get_matches();
println!("two: {:?}", matches.value_of("two").expect("required"));
println!("one: {:?}", matches.value_of("one").expect("required"));
}

View File

@ -0,0 +1,14 @@
use clap::{arg, App};
fn main() {
let matches = App::new("MyApp")
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.arg(arg!(--two <VALUE>))
.arg(arg!(--one <VALUE>))
.get_matches();
println!("two: {:?}", matches.value_of("two").expect("required"));
println!("one: {:?}", matches.value_of("one").expect("required"));
}

View File

@ -0,0 +1,11 @@
use clap::{app_from_crate, arg};
fn main() {
let matches = app_from_crate!()
.arg(arg!(--two <VALUE>))
.arg(arg!(--one <VALUE>))
.get_matches();
println!("two: {:?}", matches.value_of("two").expect("required"));
println!("one: {:?}", matches.value_of("one").expect("required"));
}

View File

@ -0,0 +1,7 @@
use clap::{app_from_crate, arg};
fn main() {
let matches = app_from_crate!().arg(arg!(-v - -verbose)).get_matches();
println!("verbose: {:?}", matches.is_present("verbose"));
}

View File

@ -0,0 +1,7 @@
use clap::{app_from_crate, arg};
fn main() {
let matches = app_from_crate!().arg(arg!(-v --verbose ...)).get_matches();
println!("verbose: {:?}", matches.occurrences_of("verbose"));
}

View File

@ -0,0 +1,9 @@
use clap::{app_from_crate, arg};
fn main() {
let matches = app_from_crate!()
.arg(arg!(-n --name <NAME>).required(false))
.get_matches();
println!("name: {:?}", matches.value_of("name"));
}

View File

@ -0,0 +1,7 @@
use clap::{app_from_crate, arg};
fn main() {
let matches = app_from_crate!().arg(arg!([NAME])).get_matches();
println!("NAME: {:?}", matches.value_of("NAME"));
}

View File

@ -0,0 +1,22 @@
use clap::{app_from_crate, arg, App, AppSettings};
fn main() {
let matches = app_from_crate!()
.global_setting(AppSettings::PropagateVersion)
.global_setting(AppSettings::UseLongFormatForHelpSubcommand)
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
App::new("add")
.about("Adds files to myapp")
.arg(arg!([NAME])),
)
.get_matches();
match matches.subcommand() {
Some(("add", sub_matches)) => println!(
"'myapp add' was used, name is: {:?}",
sub_matches.value_of("NAME")
),
_ => unreachable!(),
}
}

View File

@ -0,0 +1,14 @@
use clap::{app_from_crate, arg};
fn main() {
let matches = app_from_crate!()
.arg(arg!([NAME]).default_value("alice"))
.get_matches();
println!(
"NAME: {:?}",
matches
.value_of("NAME")
.expect("default ensures there is always a value")
);
}

View File

@ -0,0 +1,60 @@
use clap::{app_from_crate, arg, ArgEnum, PossibleValue};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ArgEnum)]
enum Mode {
Fast,
Slow,
}
impl Mode {
pub fn possible_values() -> impl Iterator<Item = PossibleValue<'static>> {
Mode::value_variants()
.iter()
.filter_map(ArgEnum::to_possible_value)
}
}
impl std::fmt::Display for Mode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
}
}
impl std::str::FromStr for Mode {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
for variant in Self::value_variants() {
if variant.to_possible_value().unwrap().matches(s, false) {
return Ok(*variant);
}
}
Err(format!("Invalid variant: {}", s))
}
}
fn main() {
let matches = app_from_crate!()
.arg(
arg!(<MODE>)
.help("What mode to run the program in")
.possible_values(Mode::possible_values()),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
match matches
.value_of_t("MODE")
.expect("'MODE' is required and parsing will fail if its missing")
{
Mode::Fast => {
println!("Hare");
}
Mode::Slow => {
println!("Tortoise");
}
}
}

View File

@ -0,0 +1,25 @@
use clap::{app_from_crate, arg};
fn main() {
let matches = app_from_crate!()
.arg(
arg!(<MODE>)
.help("What mode to run the program in")
.possible_values(["fast", "slow"]),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
match matches
.value_of("MODE")
.expect("'MODE' is required and parsing will fail if its missing")
{
"fast" => {
println!("Hare");
}
"slow" => {
println!("Tortoise");
}
_ => unreachable!(),
}
}

View File

@ -0,0 +1,17 @@
use clap::{app_from_crate, arg};
fn main() {
let matches = app_from_crate!()
.arg(
arg!(<PORT>)
.help("Network port to use")
.validator(|s| s.parse::<usize>()),
)
.get_matches();
// Note, it's safe to call unwrap() because the arg is required
let port: usize = matches
.value_of_t("PORT")
.expect("'PORT' is required and parsing will fail if its missing");
println!("PORT = {}", port);
}

View File

@ -0,0 +1,67 @@
use clap::{app_from_crate, arg, ArgGroup};
fn main() {
// Create application like normal
let matches = app_from_crate!()
// Add the version arguments
.arg(arg!(--"set-ver" <VER> "set version manually").required(false))
.arg(arg!(--major "auto inc major"))
.arg(arg!(--minor "auto inc minor"))
.arg(arg!(--patch "auto inc patch"))
// Create a group, make it required, and add the above arguments
.group(
ArgGroup::new("vers")
.required(true)
.args(&["set-ver", "major", "minor", "patch"]),
)
// Arguments can also be added to a group individually, these two arguments
// are part of the "input" group which is not required
.arg(arg!([INPUT_FILE] "some regular input").group("input"))
.arg(
arg!(--"spec-in" <SPEC_IN> "some special input argument")
.required(false)
.group("input"),
)
// Now let's assume we have a -c [config] argument which requires one of
// (but **not** both) the "input" arguments
.arg(arg!(config: -c <CONFIG>).required(false).requires("input"))
.get_matches();
// Let's assume the old version 1.2.3
let mut major = 1;
let mut minor = 2;
let mut patch = 3;
// See if --set-ver was used to set the version manually
let version = if let Some(ver) = matches.value_of("set-ver") {
ver.to_string()
} else {
// Increment the one requested (in a real program, we'd reset the lower numbers)
let (maj, min, pat) = (
matches.is_present("major"),
matches.is_present("minor"),
matches.is_present("patch"),
);
match (maj, min, pat) {
(true, _, _) => major += 1,
(_, true, _) => minor += 1,
(_, _, true) => patch += 1,
_ => unreachable!(),
};
format!("{}.{}.{}", major, minor, patch)
};
println!("Version: {}", version);
// Check for usage of -c
if matches.is_present("config") {
let input = matches
.value_of("INPUT_FILE")
.unwrap_or_else(|| matches.value_of("spec-in").unwrap());
println!(
"Doing work using input {} and config {}",
input,
matches.value_of("config").unwrap()
);
}
}

View File

@ -0,0 +1,78 @@
use clap::{app_from_crate, arg, ErrorKind};
fn main() {
// Create application like normal
let mut app = app_from_crate!()
// Add the version arguments
.arg(arg!(--"set-ver" <VER> "set version manually").required(false))
.arg(arg!(--major "auto inc major"))
.arg(arg!(--minor "auto inc minor"))
.arg(arg!(--patch "auto inc patch"))
// Arguments can also be added to a group individually, these two arguments
// are part of the "input" group which is not required
.arg(arg!([INPUT_FILE] "some regular input"))
.arg(arg!(--"spec-in" <SPEC_IN> "some special input argument").required(false))
// Now let's assume we have a -c [config] argument which requires one of
// (but **not** both) the "input" arguments
.arg(arg!(config: -c <CONFIG>).required(false));
let matches = app.get_matches_mut();
// Let's assume the old version 1.2.3
let mut major = 1;
let mut minor = 2;
let mut patch = 3;
// See if --set-ver was used to set the version manually
let version = if let Some(ver) = matches.value_of("set-ver") {
if matches.is_present("major") || matches.is_present("minor") || matches.is_present("patch")
{
app.error(
ErrorKind::ArgumentConflict,
"Can't do relative and absolute version change",
)
.exit();
}
ver.to_string()
} else {
// Increment the one requested (in a real program, we'd reset the lower numbers)
let (maj, min, pat) = (
matches.is_present("major"),
matches.is_present("minor"),
matches.is_present("patch"),
);
match (maj, min, pat) {
(true, false, false) => major += 1,
(false, true, false) => minor += 1,
(false, false, true) => patch += 1,
_ => {
app.error(
ErrorKind::ArgumentConflict,
"Cam only modify one version field",
)
.exit();
}
};
format!("{}.{}.{}", major, minor, patch)
};
println!("Version: {}", version);
// Check for usage of -c
if matches.is_present("config") {
let input = matches
.value_of("INPUT_FILE")
.or_else(|| matches.value_of("spec-in"))
.unwrap_or_else(|| {
app.error(
ErrorKind::MissingRequiredArgument,
"INPUT_FILE or --spec-in is required when using --config",
)
.exit()
});
println!(
"Doing work using input {} and config {}",
input,
matches.value_of("config").unwrap()
);
}
}

View File

@ -0,0 +1,24 @@
use clap::{app_from_crate, arg};
fn main() {
let matches = app().get_matches();
// Note, it's safe to call unwrap() because the arg is required
let port: usize = matches
.value_of_t("PORT")
.expect("'PORT' is required and parsing will fail if its missing");
println!("PORT = {}", port);
}
fn app() -> clap::App<'static> {
app_from_crate!().arg(
arg!(<PORT>)
.help("Network port to use")
.validator(|s| s.parse::<usize>()),
)
}
#[test]
fn verify_app() {
app().debug_assert();
}

View File

@ -0,0 +1,622 @@
# Tutorial
*Jump to [derive tutorial](../tutorial_derive/README.md)*
1. [Quick Start](#quick-start)
2. [Configuring the Parser](#configuring-the-parser)
3. [Adding Arguments](#adding-arguments)
1. [Flags](#flags)
2. [Options](#options)
3. [Positionals](#positionals)
4. [Subcommands](#subcommands)
5. [Defaults](#defaults)
4. Validation
1. [Enumerated values](#enumerated-values)
2. [Validated values](#validated-values)
3. [Argument Relations](#argument-relations)
4. [Custom Validation](#custom-validation)
5. [Tips](#tips)
6. [Contributing](#contributing)
## Quick Start
You can create an application with several arguments using usage strings.
[Example:](01_quick.rs)
```console
$ 01_quick --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
01_quick[EXE] [OPTIONS] [name] [SUBCOMMAND]
ARGS:
<name> Optional name to operate on
OPTIONS:
-c, --config <FILE> Sets a custom config file
-d, --debug Turn debugging information on
-h, --help Print help information
-V, --version Print version information
SUBCOMMANDS:
help Print this message or the help of the given subcommand(s)
test does testing things
```
By default, the program does nothing:
```console
$ 01_quick
Debug mode is off
```
But you can mix and match the various features
```console
$ 01_quick -dd test
Debug mode is on
Not printing testing lists...
```
## Configuring the Parser
You use the `App` the start building a parser.
[Example:](02_apps.rs)
```console
$ 02_apps --help
MyApp 1.0
Kevin K. <kbknapp@gmail.com>
Does awesome things
USAGE:
02_apps[EXE] --two <VALUE> --one <VALUE>
OPTIONS:
-h, --help Print help information
--one <VALUE>
--two <VALUE>
-V, --version Print version information
$ 02_apps --version
MyApp 1.0
```
You can use `app_from_crate!()` to fill these fields in from your `Cargo.toml`
file. **This requires the `cargo` feature flag.**
[Example:](02_crate.rs)
```console
$ 02_crate --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
02_crate[EXE] --two <VALUE> --one <VALUE>
OPTIONS:
-h, --help Print help information
--one <VALUE>
--two <VALUE>
-V, --version Print version information
$ 02_crate --version
clap [..]
```
You can use `AppSettings` to change the application level behavior of clap. You
can apply the setting to the top level command (`app.setting()`) or to it and
all subcommands (`app.global_setting()`).
[Example:](02_app_settings.rs)
```console
$ 02_app_settings --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
02_app_settings[EXE] --two <VALUE> --one <VALUE>
OPTIONS:
--two <VALUE>
--one <VALUE>
-h, --help Print help information
-V, --version Print version information
$ 02_app_settings --one -1 --one -3 --two 10
two: "10"
one: "-3"
```
## Adding Arguments
### Flags
Flags are switches that can be on/off:
[Example:](03_01_flag_bool.rs)
```console
$ 03_01_flag_bool --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_01_flag_bool[EXE] [OPTIONS]
OPTIONS:
-h, --help Print help information
-v, --verbose
-V, --version Print version information
$ 03_01_flag_bool
verbose: false
$ 03_01_flag_bool --verbose
verbose: true
$ 03_01_flag_bool --verbose --verbose
? failed
error: The argument '--verbose' was provided more than once, but cannot be used multiple times
USAGE:
03_01_flag_bool[EXE] [OPTIONS]
For more information try --help
```
Or counted.
[Example:](03_01_flag_count.rs)
```console
$ 03_01_flag_count --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_01_flag_count[EXE] [OPTIONS]
OPTIONS:
-h, --help Print help information
-v, --verbose
-V, --version Print version information
$ 03_01_flag_count
verbose: 0
$ 03_01_flag_count --verbose
verbose: 1
$ 03_01_flag_count --verbose --verbose
verbose: 2
```
### Options
Flags can also accept a value.
[Example:](03_02_option.rs)
```console
$ 03_02_option --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_02_option[EXE] [OPTIONS]
OPTIONS:
-h, --help Print help information
-n, --name <NAME>
-V, --version Print version information
$ 03_02_option
name: None
$ 03_02_option --name bob
name: Some("bob")
$ 03_02_option --name=bob
name: Some("bob")
$ 03_02_option -n bob
name: Some("bob")
$ 03_02_option -n=bob
name: Some("bob")
$ 03_02_option -nbob
name: Some("bob")
```
### Positionals
Or you can have users specify values by their position on the command-line:
[Example:](03_03_positional.rs)
```console
$ 03_03_positional --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_03_positional[EXE] [NAME]
ARGS:
<NAME>
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 03_03_positional
NAME: None
$ 03_03_positional bob
NAME: Some("bob")
```
### Subcommands
Subcommands are defined as `App`s that get added via `App::subcommand`. Each
instance of a Subcommand can have its own version, author(s), Args, and even its own
subcommands.
[Example:](03_04_subcommands.rs)
```console
$ 03_04_subcommands
? failed
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_04_subcommands[EXE] <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
-V, --version Print version information
SUBCOMMANDS:
add Adds files to myapp
help Print this message or the help of the given subcommand(s)
$ 03_04_subcommands help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_04_subcommands[EXE] <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
-V, --version Print version information
SUBCOMMANDS:
add Adds files to myapp
help Print this message or the help of the given subcommand(s)
$ 03_04_subcommands help add
03_04_subcommands[EXE]-add [..]
Adds files to myapp
USAGE:
03_04_subcommands[EXE] add [NAME]
ARGS:
<NAME>
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 03_04_subcommands add bob
'myapp add' was used, name is: Some("bob")
```
Because we set `AppSettings::PropagateVersion`:
```console
$ 03_04_subcommands --version
clap [..]
$ 03_04_subcommands add --version
03_04_subcommands[EXE]-add [..]
```
### Defaults
We've previously showed that arguments can be `required` or optional. When
optional, you work with a `Option` and can `unwrap_or`. Alternatively, you can
set `Arg::default_value`.
[Example:](03_05_default_values.rs)
```console
$ 03_05_default_values --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_05_default_values[EXE] [NAME]
ARGS:
<NAME> [default: alice]
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 03_05_default_values
NAME: "alice"
$ 03_05_default_values bob
NAME: "bob"
```
## Validation
### Enumerated values
If you have arguments of specific values you want to test for, you can use the
`Arg::possible_values()`.
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
[Example:](04_01_possible.rs)
```console
$ 04_01_possible --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
04_01_possible[EXE] <MODE>
ARGS:
<MODE> What mode to run the program in [possible values: fast, slow]
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 04_01_possible fast
Hare
$ 04_01_possible slow
Tortoise
$ 04_01_possible medium
? failed
error: "medium" isn't a valid value for '<MODE>'
[possible values: fast, slow]
USAGE:
04_01_possible[EXE] <MODE>
For more information try --help
```
When enabling the `derive` feature, you can use `ArgEnum` to take care of the boiler plate for you, giving the same results.
[Example:](04_01_enum.rs)
```console
$ 04_01_enum --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
04_01_enum[EXE] <MODE>
ARGS:
<MODE> What mode to run the program in [possible values: fast, slow]
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 04_01_enum fast
Hare
$ 04_01_enum slow
Tortoise
$ 04_01_enum medium
? failed
error: "medium" isn't a valid value for '<MODE>'
[possible values: fast, slow]
USAGE:
04_01_enum[EXE] <MODE>
For more information try --help
```
### Validated values
More generally, you can validate and parse into any data type.
[Example:](04_02_validate.rs)
```console
$ 04_02_validate --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
04_02_validate[EXE] <PORT>
ARGS:
<PORT> Network port to use
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 04_02_validate 22
PORT = 22
$ 04_02_validate foobar
? failed
error: Invalid value for '<PORT>': invalid digit found in string
For more information try --help
```
### Argument Relations
You can declare dependencies or conflicts between `Arg`s or even `ArgGroup`s.
`ArgGroup`s make it easier to declare relations instead of having to list each
individually, or when you want a rule to apply "any but not all" arguments.
Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be
present out of a given set. Imagine that you had multiple arguments, and you want one of them to
be required, but making all of them required isn't feasible because perhaps they conflict with
each other.
[Example:](04_03_relations.rs)
```console
$ 04_03_relations --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
04_03_relations[EXE] [OPTIONS] <--set-ver <VER>|--major|--minor|--patch> [INPUT_FILE]
ARGS:
<INPUT_FILE> some regular input
OPTIONS:
-c <CONFIG>
-h, --help Print help information
--major auto inc major
--minor auto inc minor
--patch auto inc patch
--set-ver <VER> set version manually
--spec-in <SPEC_IN> some special input argument
-V, --version Print version information
$ 04_03_relations
? failed
error: The following required arguments were not provided:
<--set-ver <VER>|--major|--minor|--patch>
USAGE:
04_03_relations[EXE] [OPTIONS] <--set-ver <VER>|--major|--minor|--patch> [INPUT_FILE]
For more information try --help
$ 04_03_relations --major
Version: 2.2.3
$ 04_03_relations --major --minor
? failed
error: The argument '--major' cannot be used with '--minor'
USAGE:
04_03_relations[EXE] <--set-ver <VER>|--major|--minor|--patch>
For more information try --help
$ 04_03_relations --major -c config.toml
? failed
error: The following required arguments were not provided:
<INPUT_FILE|--spec-in <SPEC_IN>>
USAGE:
04_03_relations[EXE] -c <CONFIG> <--set-ver <VER>|--major|--minor|--patch> <INPUT_FILE|--spec-in <SPEC_IN>>
For more information try --help
$ 04_03_relations --major -c config.toml --spec-in input.txt
Version: 2.2.3
Doing work using input input.txt and config config.toml
```
### Custom Validation
As a last resort, you can create custom errors with the basics of clap's formatting.
[Example:](04_04_custom.rs)
```console
$ 04_04_custom --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
04_04_custom[EXE] [OPTIONS] [INPUT_FILE]
ARGS:
<INPUT_FILE> some regular input
OPTIONS:
-c <CONFIG>
-h, --help Print help information
--major auto inc major
--minor auto inc minor
--patch auto inc patch
--set-ver <VER> set version manually
--spec-in <SPEC_IN> some special input argument
-V, --version Print version information
$ 04_04_custom
? failed
error: Cam only modify one version field
USAGE:
04_04_custom[EXE] [OPTIONS] [INPUT_FILE]
For more information try --help
$ 04_04_custom --major
Version: 2.2.3
$ 04_04_custom --major --minor
? failed
error: Cam only modify one version field
USAGE:
04_04_custom[EXE] [OPTIONS] [INPUT_FILE]
For more information try --help
$ 04_04_custom --major -c config.toml
? failed
Version: 2.2.3
error: INPUT_FILE or --spec-in is required when using --config
USAGE:
04_04_custom[EXE] [OPTIONS] [INPUT_FILE]
For more information try --help
$ 04_04_custom --major -c config.toml --spec-in input.txt
Version: 2.2.3
Doing work using input input.txt and config config.toml
```
## Tips
- Proactively check for bad `App` configurations by calling `App::debug_assert` ([example](05_01_assert.rs))
## Contributing
New example code:
- Building: They must be added to [Cargo.toml](../../Cargo.toml) with the appropriate `required-features`.
- Testing: Ensure there is a markdown file with [trycmd](https://docs.rs/trycmd) syntax (generally they'll go in here).
See also the general [CONTRIBUTING](../../CONTRIBUTING.md).

View File

@ -0,0 +1,68 @@
use std::path::PathBuf;
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
/// Optional name to operate on
name: Option<String>,
/// Sets a custom config file
#[clap(short, long, parse(from_os_str), value_name = "FILE")]
config: Option<PathBuf>,
/// Turn debugging information on
#[clap(short, long, parse(from_occurrences))]
debug: usize,
#[clap(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
/// does testing things
Test {
/// lists test values
#[clap(short, long)]
list: bool,
},
}
fn main() {
let cli = Cli::parse();
// You can check the value provided by positional arguments, or option arguments
if let Some(name) = cli.name.as_deref() {
println!("Value for name: {}", name);
}
if let Some(config_path) = cli.config.as_deref() {
println!("Value for config: {}", config_path.display());
}
// You can see how many times a particular flag or argument occurred
// Note, only flags can have multiple occurrences
match cli.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
match &cli.command {
Some(Commands::Test { list }) => {
if *list {
println!("Printing testing lists...");
} else {
println!("Not printing testing lists...");
}
}
None => {}
}
// Continued program logic goes here...
}

View File

@ -0,0 +1,20 @@
use clap::{AppSettings, Parser};
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
#[clap(global_setting(AppSettings::AllArgsOverrideSelf))]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
#[clap(global_setting(AppSettings::AllowNegativeNumbers))]
struct Cli {
#[clap(long)]
two: String,
#[clap(long)]
one: String,
}
fn main() {
let cli = Cli::parse();
println!("two: {:?}", cli.two);
println!("one: {:?}", cli.one);
}

View File

@ -0,0 +1,20 @@
use clap::Parser;
#[derive(Parser)]
#[clap(name = "MyApp")]
#[clap(author = "Kevin K. <kbknapp@gmail.com>")]
#[clap(version = "1.0")]
#[clap(about = "Does awesome things", long_about = None)]
struct Cli {
#[clap(long)]
two: String,
#[clap(long)]
one: String,
}
fn main() {
let cli = Cli::parse();
println!("two: {:?}", cli.two);
println!("one: {:?}", cli.one);
}

View File

@ -0,0 +1,17 @@
use clap::Parser;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
#[clap(long)]
two: String,
#[clap(long)]
one: String,
}
fn main() {
let cli = Cli::parse();
println!("two: {:?}", cli.two);
println!("one: {:?}", cli.one);
}

View File

@ -0,0 +1,14 @@
use clap::Parser;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
#[clap(short, long)]
verbose: bool,
}
fn main() {
let cli = Cli::parse();
println!("verbose: {:?}", cli.verbose);
}

View File

@ -0,0 +1,14 @@
use clap::Parser;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
#[clap(short, long, parse(from_occurrences))]
verbose: usize,
}
fn main() {
let cli = Cli::parse();
println!("verbose: {:?}", cli.verbose);
}

View File

@ -0,0 +1,14 @@
use clap::Parser;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
#[clap(short, long)]
name: Option<String>,
}
fn main() {
let cli = Cli::parse();
println!("name: {:?}", cli.name.as_deref());
}

View File

@ -0,0 +1,13 @@
use clap::Parser;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
name: Option<String>,
}
fn main() {
let cli = Cli::parse();
println!("name: {:?}", cli.name.as_deref());
}

View File

@ -0,0 +1,28 @@
use clap::{AppSettings, Parser, Subcommand};
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
#[clap(global_setting(AppSettings::PropagateVersion))]
#[clap(global_setting(AppSettings::UseLongFormatForHelpSubcommand))]
struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Adds files to myapp
Add { name: Option<String> },
}
fn main() {
let cli = Cli::parse();
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level app
match &cli.command {
Commands::Add { name } => {
println!("'myapp add' was used, name is: {:?}", name)
}
}
}

View File

@ -0,0 +1,14 @@
use clap::Parser;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
#[clap(default_value_t = String::from("alice"))]
name: String,
}
fn main() {
let cli = Cli::parse();
println!("name: {:?}", cli.name);
}

View File

@ -0,0 +1,28 @@
use clap::{ArgEnum, Parser};
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
/// What mode to run the program in
#[clap(arg_enum)]
mode: Mode,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ArgEnum)]
enum Mode {
Fast,
Slow,
}
fn main() {
let cli = Cli::parse();
match cli.mode {
Mode::Fast => {
println!("Hare");
}
Mode::Slow => {
println!("Tortoise");
}
}
}

View File

@ -0,0 +1,15 @@
use clap::Parser;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
/// Network port to use
#[clap(parse(try_from_str))]
port: usize,
}
fn main() {
let cli = Cli::parse();
println!("PORT = {}", cli.port);
}

View File

@ -0,0 +1,72 @@
use clap::{ArgGroup, Parser};
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
#[clap(group(
ArgGroup::new("vers")
.required(true)
.args(&["set-ver", "major", "minor", "patch"]),
))]
struct Cli {
/// set version manually
#[clap(long, value_name = "VER")]
set_ver: Option<String>,
/// auto inc major
#[clap(long)]
major: bool,
/// auto inc minor
#[clap(long)]
minor: bool,
/// auto inc patch
#[clap(long)]
patch: bool,
/// some regular input
#[clap(group = "input")]
input_file: Option<String>,
/// some special input argument
#[clap(long, group = "input")]
spec_in: Option<String>,
#[clap(short, requires = "input")]
config: Option<String>,
}
fn main() {
let cli = Cli::parse();
// Let's assume the old version 1.2.3
let mut major = 1;
let mut minor = 2;
let mut patch = 3;
// See if --set-ver was used to set the version manually
let version = if let Some(ver) = cli.set_ver.as_deref() {
ver.to_string()
} else {
// Increment the one requested (in a real program, we'd reset the lower numbers)
let (maj, min, pat) = (cli.major, cli.minor, cli.patch);
match (maj, min, pat) {
(true, _, _) => major += 1,
(_, true, _) => minor += 1,
(_, _, true) => patch += 1,
_ => unreachable!(),
};
format!("{}.{}.{}", major, minor, patch)
};
println!("Version: {}", version);
// Check for usage of -c
if let Some(config) = cli.config.as_deref() {
let input = cli
.input_file
.as_deref()
.unwrap_or_else(|| cli.spec_in.as_deref().unwrap());
println!("Doing work using input {} and config {}", input, config);
}
}

View File

@ -0,0 +1,92 @@
use clap::{ErrorKind, IntoApp, Parser};
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
/// set version manually
#[clap(long, value_name = "VER")]
set_ver: Option<String>,
/// auto inc major
#[clap(long)]
major: bool,
/// auto inc minor
#[clap(long)]
minor: bool,
/// auto inc patch
#[clap(long)]
patch: bool,
/// some regular input
input_file: Option<String>,
/// some special input argument
#[clap(long)]
spec_in: Option<String>,
#[clap(short)]
config: Option<String>,
}
fn main() {
let cli = Cli::parse();
// Let's assume the old version 1.2.3
let mut major = 1;
let mut minor = 2;
let mut patch = 3;
// See if --set-ver was used to set the version manually
let version = if let Some(ver) = cli.set_ver.as_deref() {
if cli.major || cli.minor || cli.patch {
let mut app = Cli::into_app();
app.error(
ErrorKind::ArgumentConflict,
"Can't do relative and absolute version change",
)
.exit();
}
ver.to_string()
} else {
// Increment the one requested (in a real program, we'd reset the lower numbers)
let (maj, min, pat) = (cli.major, cli.minor, cli.patch);
match (maj, min, pat) {
(true, false, false) => major += 1,
(false, true, false) => minor += 1,
(false, false, true) => patch += 1,
_ => {
let mut app = Cli::into_app();
app.error(
ErrorKind::ArgumentConflict,
"Cam only modify one version field",
)
.exit();
}
};
format!("{}.{}.{}", major, minor, patch)
};
println!("Version: {}", version);
// Check for usage of -c
if let Some(config) = cli.config.as_deref() {
// todo: remove `#[allow(clippy::or_fun_call)]` lint when MSRV is bumped.
#[allow(clippy::or_fun_call)]
let input = cli
.input_file
.as_deref()
// 'or' is preferred to 'or_else' here since `Option::as_deref` is 'const'
.or(cli.spec_in.as_deref())
.unwrap_or_else(|| {
let mut app = Cli::into_app();
app.error(
ErrorKind::MissingRequiredArgument,
"INPUT_FILE or --spec-in is required when using --config",
)
.exit()
});
println!("Doing work using input {} and config {}", input, config);
}
}

View File

@ -0,0 +1,21 @@
use clap::Parser;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Cli {
/// Network port to use
#[clap(parse(try_from_str))]
port: usize,
}
fn main() {
let cli = Cli::parse();
println!("PORT = {}", cli.port);
}
#[test]
fn verify_app() {
use clap::IntoApp;
Cli::into_app().debug_assert()
}

View File

@ -0,0 +1,588 @@
# Tutorial
*Jump to [builder tutorial](../tutorial_builder/README.md)*
1. [Quick Start](#quick-start)
2. [Configuring the Parser](#configuring-the-parser)
3. [Adding Arguments](#adding-arguments)
1. [Flags](#flags)
2. [Options](#options)
3. [Positionals](#positionals)
4. [Subcommands](#subcommands)
5. [Defaults](#defaults)
4. Validation
1. [Enumerated values](#enumerated-values)
2. [Validated values](#validated-values)
3. [Argument Relations](#argument-relations)
4. [Custom Validation](#custom-validation)
5. [Tips](#tips)
6. [Contributing](#contributing)
## Quick Start
You can create an application declaratively with a `struct` and some
attributes. **This requires enabling the `derive` feature flag.**
[Example:](01_quick.rs)
```console
$ 01_quick_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
01_quick_derive[EXE] [OPTIONS] [NAME] [SUBCOMMAND]
ARGS:
<NAME> Optional name to operate on
OPTIONS:
-c, --config <FILE> Sets a custom config file
-d, --debug Turn debugging information on
-h, --help Print help information
-V, --version Print version information
SUBCOMMANDS:
help Print this message or the help of the given subcommand(s)
test does testing things
```
By default, the program does nothing:
```console
$ 01_quick_derive
Debug mode is off
```
But you can mix and match the various features
```console
$ 01_quick_derive -dd test
Debug mode is on
Not printing testing lists...
```
In addition to this tutorial, see the [derive reference](../derive_ref/README.md).
## Configuring the Parser
You use the `App` the start building a parser.
[Example:](02_apps.rs)
```console
$ 02_apps_derive --help
MyApp 1.0
Kevin K. <kbknapp@gmail.com>
Does awesome things
USAGE:
02_apps_derive[EXE] --two <TWO> --one <ONE>
OPTIONS:
-h, --help Print help information
--one <ONE>
--two <TWO>
-V, --version Print version information
$ 02_apps_derive --version
MyApp 1.0
```
You can use `app_from_crate!()` to fill these fields in from your `Cargo.toml` file.
[Example:](02_crate.rs)
```console
$ 02_crate_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
02_crate_derive[EXE] --two <TWO> --one <ONE>
OPTIONS:
-h, --help Print help information
--one <ONE>
--two <TWO>
-V, --version Print version information
$ 02_crate_derive --version
clap [..]
```
You can use `AppSettings` to change the application level behavior of clap. You
can apply the setting to the top level command (`app.setting()`) or to it and
all subcommands (`app.global_setting()`).
[Example:](02_app_settings.rs)
```console
$ 02_app_settings_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
02_app_settings_derive[EXE] --two <TWO> --one <ONE>
OPTIONS:
--two <TWO>
--one <ONE>
-h, --help Print help information
-V, --version Print version information
$ 02_app_settings_derive --one -1 --one -3 --two 10
two: "10"
one: "-3"
```
## Adding Arguments
### Flags
Flags are switches that can be on/off:
[Example:](03_01_flag_bool.rs)
```console
$ 03_01_flag_bool_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_01_flag_bool_derive[EXE] [OPTIONS]
OPTIONS:
-h, --help Print help information
-v, --verbose
-V, --version Print version information
$ 03_01_flag_bool_derive
verbose: false
$ 03_01_flag_bool_derive --verbose
verbose: true
$ 03_01_flag_bool_derive --verbose --verbose
? failed
error: The argument '--verbose' was provided more than once, but cannot be used multiple times
USAGE:
03_01_flag_bool_derive[EXE] [OPTIONS]
For more information try --help
```
Or counted.
[Example:](03_01_flag_count.rs)
```console
$ 03_01_flag_count_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_01_flag_count_derive[EXE] [OPTIONS]
OPTIONS:
-h, --help Print help information
-v, --verbose
-V, --version Print version information
$ 03_01_flag_count_derive
verbose: 0
$ 03_01_flag_count_derive --verbose
verbose: 1
$ 03_01_flag_count_derive --verbose --verbose
verbose: 2
```
### Options
Flags can also accept a value.
[Example:](03_02_option.rs)
```console
$ 03_02_option_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_02_option_derive[EXE] [OPTIONS]
OPTIONS:
-h, --help Print help information
-n, --name <NAME>
-V, --version Print version information
$ 03_02_option_derive
name: None
$ 03_02_option_derive --name bob
name: Some("bob")
$ 03_02_option_derive --name=bob
name: Some("bob")
$ 03_02_option_derive -n bob
name: Some("bob")
$ 03_02_option_derive -n=bob
name: Some("bob")
$ 03_02_option_derive -nbob
name: Some("bob")
```
### Positionals
Or you can have users specify values by their position on the command-line:
[Example:](03_03_positional.rs)
```console
$ 03_03_positional_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_03_positional_derive[EXE] [NAME]
ARGS:
<NAME>
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 03_03_positional_derive
name: None
$ 03_03_positional_derive bob
name: Some("bob")
```
### Subcommands
Subcommands are defined as `App`s that get added via `App::subcommand`. Each
instance of a Subcommand can have its own version, author(s), Args, and even its own
subcommands.
[Example:](03_04_subcommands.rs)
```console
$ 03_04_subcommands_derive
? failed
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_04_subcommands_derive[EXE] <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
-V, --version Print version information
SUBCOMMANDS:
add Adds files to myapp
help Print this message or the help of the given subcommand(s)
$ 03_04_subcommands_derive help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_04_subcommands_derive[EXE] <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
-V, --version Print version information
SUBCOMMANDS:
add Adds files to myapp
help Print this message or the help of the given subcommand(s)
$ 03_04_subcommands_derive help add
03_04_subcommands_derive[EXE]-add [..]
Adds files to myapp
USAGE:
03_04_subcommands_derive[EXE] add [NAME]
ARGS:
<NAME>
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 03_04_subcommands_derive add bob
'myapp add' was used, name is: Some("bob")
```
Because we set `AppSettings::PropagateVersion`:
```console
$ 03_04_subcommands_derive --version
clap [..]
$ 03_04_subcommands_derive add --version
03_04_subcommands_derive[EXE]-add [..]
```
### Defaults
We've previously showed that arguments can be `required` or optional. When
optional, you work with a `Option` and can `unwrap_or`. Alternatively, you can
set `Arg::default_value`.
[Example:](03_05_default_values.rs)
```console
$ 03_05_default_values_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
03_05_default_values_derive[EXE] [NAME]
ARGS:
<NAME> [default: alice]
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 03_05_default_values_derive
name: "alice"
$ 03_05_default_values_derive bob
name: "bob"
```
## Validation
### Enumerated values
If you have arguments of specific values you want to test for, you can derive
`ArgEnum`.
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
[Example:](04_01_enum.rs)
```console
$ 04_01_enum_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
04_01_enum_derive[EXE] <MODE>
ARGS:
<MODE> What mode to run the program in [possible values: fast, slow]
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 04_01_enum_derive fast
Hare
$ 04_01_enum_derive slow
Tortoise
$ 04_01_enum_derive medium
? failed
error: "medium" isn't a valid value for '<MODE>'
[possible values: fast, slow]
USAGE:
04_01_enum_derive[EXE] <MODE>
For more information try --help
```
### Validated values
More generally, you can validate and parse into any data type.
[Example:](04_02_validate.rs)
```console
$ 04_02_validate_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
04_02_validate_derive[EXE] <PORT>
ARGS:
<PORT> Network port to use
OPTIONS:
-h, --help Print help information
-V, --version Print version information
$ 04_02_validate_derive 22
PORT = 22
$ 04_02_validate_derive foobar
? failed
error: Invalid value for '<PORT>': invalid digit found in string
For more information try --help
```
### Argument Relations
You can declare dependencies or conflicts between `Arg`s or even `ArgGroup`s.
`ArgGroup`s make it easier to declare relations instead of having to list each
individually, or when you want a rule to apply "any but not all" arguments.
Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be
present out of a given set. Imagine that you had multiple arguments, and you want one of them to
be required, but making all of them required isn't feasible because perhaps they conflict with
each other.
[Example:](04_03_relations.rs)
```console
$ 04_03_relations_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
04_03_relations_derive[EXE] [OPTIONS] <--set-ver <VER>|--major|--minor|--patch> [INPUT_FILE]
ARGS:
<INPUT_FILE> some regular input
OPTIONS:
-c <CONFIG>
-h, --help Print help information
--major auto inc major
--minor auto inc minor
--patch auto inc patch
--set-ver <VER> set version manually
--spec-in <SPEC_IN> some special input argument
-V, --version Print version information
$ 04_03_relations_derive
? failed
error: The following required arguments were not provided:
<--set-ver <VER>|--major|--minor|--patch>
USAGE:
04_03_relations_derive[EXE] [OPTIONS] <--set-ver <VER>|--major|--minor|--patch> [INPUT_FILE]
For more information try --help
$ 04_03_relations_derive --major
Version: 2.2.3
$ 04_03_relations_derive --major --minor
? failed
error: The argument '--major' cannot be used with '--minor'
USAGE:
04_03_relations_derive[EXE] <--set-ver <VER>|--major|--minor|--patch>
For more information try --help
$ 04_03_relations_derive --major -c config.toml
? failed
error: The following required arguments were not provided:
<INPUT_FILE|--spec-in <SPEC_IN>>
USAGE:
04_03_relations_derive[EXE] -c <CONFIG> <--set-ver <VER>|--major|--minor|--patch> <INPUT_FILE|--spec-in <SPEC_IN>>
For more information try --help
$ 04_03_relations_derive --major -c config.toml --spec-in input.txt
Version: 2.2.3
Doing work using input input.txt and config config.toml
```
### Custom Validation
As a last resort, you can create custom errors with the basics of clap's formatting.
[Example:](04_04_custom.rs)
```console
$ 04_04_custom_derive --help
clap [..]
A simple to use, efficient, and full-featured Command Line Argument Parser
USAGE:
04_04_custom_derive[EXE] [OPTIONS] [INPUT_FILE]
ARGS:
<INPUT_FILE> some regular input
OPTIONS:
-c <CONFIG>
-h, --help Print help information
--major auto inc major
--minor auto inc minor
--patch auto inc patch
--set-ver <VER> set version manually
--spec-in <SPEC_IN> some special input argument
-V, --version Print version information
$ 04_04_custom_derive
? failed
error: Cam only modify one version field
USAGE:
clap [OPTIONS] [INPUT_FILE]
For more information try --help
$ 04_04_custom_derive --major
Version: 2.2.3
$ 04_04_custom_derive --major --minor
? failed
error: Cam only modify one version field
USAGE:
clap [OPTIONS] [INPUT_FILE]
For more information try --help
$ 04_04_custom_derive --major -c config.toml
? failed
Version: 2.2.3
error: INPUT_FILE or --spec-in is required when using --config
USAGE:
clap [OPTIONS] [INPUT_FILE]
For more information try --help
$ 04_04_custom_derive --major -c config.toml --spec-in input.txt
Version: 2.2.3
Doing work using input input.txt and config config.toml
```
## Tips
- Proactively check for bad `App` configurations by calling `App::debug_assert` ([example](05_01_assert.rs))
## Contributing
New example code:
- Building: They must be added to [Cargo.toml](../../Cargo.toml) with the appropriate `required-features`.
- Testing: Ensure there is a markdown file with [trycmd](https://docs.rs/trycmd) syntax (generally they'll go in here).
See also the general [CONTRIBUTING](../../CONTRIBUTING.md).

View File

@ -1,39 +0,0 @@
@update-contributors:
echo 'Removing old CONTRIBUTORS.md'
mv CONTRIBUTORS.md CONTRIBUTORS.md.bak
echo 'Downloading a list of new contributors'
echo "the following is a list of contributors:" > CONTRIBUTORS.md
echo "" >> CONTRIBUTORS.md
echo "" >> CONTRIBUTORS.md
githubcontrib --owner clap-rs --repo clap --sha master --cols 6 --format md --showlogin true --sortBy contributions --sortOrder desc >> CONTRIBUTORS.md
echo "" >> CONTRIBUTORS.md
echo "" >> CONTRIBUTORS.md
echo "This list was generated by [mgechev/github-contributors-list](https://github.com/mgechev/github-contributors-list)" >> CONTRIBUTORS.md
rm CONTRIBUTORS.md.bak
run-test TEST:
cargo test --test {{TEST}}
debug TEST:
cargo test --test {{TEST}} --features debug
run-tests:
cargo test --features "yaml unstable"
@bench: nightly
cargo bench && just remove-nightly
nightly:
rustup override add nightly
remove-nightly:
rustup override remove
@lint: nightly
cargo build --features lints && just remove-nightly
clean:
cargo clean
find . -type f -name "*.orig" -exec rm {} \;
find . -type f -name "*.bk" -exec rm {} \;
find . -type f -name ".*~" -exec rm {} \;

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
#[doc(hidden)]
#[allow(missing_debug_implementations)]
#[derive(Default, Clone)]
pub struct AppMeta<'b> {
pub name: String,
pub bin_name: Option<String>,
pub author: Option<&'b str>,
pub version: Option<&'b str>,
pub long_version: Option<&'b str>,
pub about: Option<&'b str>,
pub long_about: Option<&'b str>,
pub more_help: Option<&'b str>,
pub pre_help: Option<&'b str>,
pub aliases: Option<Vec<(&'b str, bool)>>, // (name, visible)
pub usage_str: Option<&'b str>,
pub usage: Option<String>,
pub help_str: Option<&'b str>,
pub disp_ord: usize,
pub term_w: Option<usize>,
pub max_w: Option<usize>,
pub template: Option<&'b str>,
}
impl<'b> AppMeta<'b> {
pub fn new() -> Self {
Default::default()
}
pub fn with_name(s: String) -> Self {
AppMeta {
name: s,
disp_ord: 999,
..Default::default()
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,493 +0,0 @@
// std
use std::collections::{BTreeMap, VecDeque};
// Internal
use crate::{
app::{parser::Parser, settings::AppSettings as AS},
args::{settings::ArgSettings, AnyArg, ArgMatcher, PosBuilder},
INTERNAL_ERROR_MSG,
};
// Creates a usage string for display. This happens just after all arguments were parsed, but before
// any subcommands have been parsed (so as to give subcommands their own usage recursively)
pub fn create_usage_with_title(p: &Parser, used: &[&str]) -> String {
debugln!("usage::create_usage_with_title;");
let mut usage = String::with_capacity(75);
usage.push_str("USAGE:\n ");
usage.push_str(&*create_usage_no_title(p, used));
usage
}
// Creates a usage string to be used in error message (i.e. one with currently used args)
pub fn create_error_usage<'a, 'b>(
p: &Parser<'a, 'b>,
matcher: &'b ArgMatcher<'a>,
extra: Option<&str>,
) -> String {
let mut args: Vec<_> = matcher
.arg_names()
.iter()
.filter(|n| {
if let Some(o) = find_by_name!(p, **n, opts, iter) {
!o.b.is_set(ArgSettings::Required) && !o.b.is_set(ArgSettings::Hidden)
} else if let Some(p) = find_by_name!(p, **n, positionals, values) {
!p.b.is_set(ArgSettings::Required) && p.b.is_set(ArgSettings::Hidden)
} else {
true // flags can't be required, so they're always true
}
})
.copied()
.collect();
if let Some(r) = extra {
args.push(r);
}
create_usage_with_title(p, &*args)
}
// Creates a usage string (*without title*) if one was not provided by the user manually.
pub fn create_usage_no_title(p: &Parser, used: &[&str]) -> String {
debugln!("usage::create_usage_no_title;");
if let Some(u) = p.meta.usage_str {
String::from(&*u)
} else if used.is_empty() {
create_help_usage(p, true)
} else {
create_smart_usage(p, used)
}
}
// Creates a usage string for display in help messages (i.e. not for errors)
pub fn create_help_usage(p: &Parser, incl_reqs: bool) -> String {
let mut usage = String::with_capacity(75);
let name = p
.meta
.usage
.as_ref()
.unwrap_or_else(|| p.meta.bin_name.as_ref().unwrap_or(&p.meta.name));
usage.push_str(&*name);
let req_string = if incl_reqs {
let mut reqs: Vec<&str> = p.required().map(|r| &**r).collect();
reqs.sort_unstable();
reqs.dedup();
get_required_usage_from(p, &reqs, None, None, false)
.iter()
.fold(String::new(), |a, s| a + &format!(" {}", s)[..])
} else {
String::new()
};
let flags = needs_flags_tag(p);
if flags && !p.is_set(AS::UnifiedHelpMessage) {
usage.push_str(" [FLAGS]");
} else if flags {
usage.push_str(" [OPTIONS]");
}
if !p.is_set(AS::UnifiedHelpMessage)
&& p.opts
.iter()
.any(|o| !o.is_set(ArgSettings::Required) && !o.is_set(ArgSettings::Hidden))
{
usage.push_str(" [OPTIONS]");
}
usage.push_str(&req_string[..]);
let has_last = p.positionals.values().any(|p| p.is_set(ArgSettings::Last));
// places a '--' in the usage string if there are args and options
// supporting multiple values
if p.opts.iter().any(|o| o.is_set(ArgSettings::Multiple))
&& p.positionals
.values()
.any(|p| !p.is_set(ArgSettings::Required))
&& !(p.has_visible_subcommands() || p.is_set(AS::AllowExternalSubcommands))
&& !has_last
{
usage.push_str(" [--]");
}
let not_req_or_hidden = |p: &PosBuilder| {
(!p.is_set(ArgSettings::Required) || p.is_set(ArgSettings::Last))
&& !p.is_set(ArgSettings::Hidden)
};
if p.has_positionals() && p.positionals.values().any(not_req_or_hidden) {
if let Some(args_tag) = get_args_tag(p, incl_reqs) {
usage.push_str(&*args_tag);
} else {
usage.push_str(" [ARGS]");
}
if has_last && incl_reqs {
let pos = p
.positionals
.values()
.find(|p| p.b.is_set(ArgSettings::Last))
.expect(INTERNAL_ERROR_MSG);
debugln!("usage::create_help_usage: '{}' has .last(true)", pos.name());
let req = pos.is_set(ArgSettings::Required);
if req
&& p.positionals
.values()
.any(|p| !p.is_set(ArgSettings::Required))
{
usage.push_str(" -- <");
} else if req {
usage.push_str(" [--] <");
} else {
usage.push_str(" [-- <");
}
usage.push_str(&*pos.name_no_brackets());
usage.push('>');
usage.push_str(pos.multiple_str());
if !req {
usage.push(']');
}
}
}
// incl_reqs is only false when this function is called recursively
if p.has_visible_subcommands() && incl_reqs || p.is_set(AS::AllowExternalSubcommands) {
if p.is_set(AS::SubcommandsNegateReqs) || p.is_set(AS::ArgsNegateSubcommands) {
usage.push_str("\n ");
if !p.is_set(AS::ArgsNegateSubcommands) {
usage.push_str(&*create_help_usage(p, false));
} else {
usage.push_str(&*name);
}
usage.push_str(" <SUBCOMMAND>");
} else if p.is_set(AS::SubcommandRequired) || p.is_set(AS::SubcommandRequiredElseHelp) {
usage.push_str(" <SUBCOMMAND>");
} else {
usage.push_str(" [SUBCOMMAND]");
}
}
usage.shrink_to_fit();
debugln!("usage::create_help_usage: usage={}", usage);
usage
}
// Creates a context aware usage string, or "smart usage" from currently used
// args, and requirements
fn create_smart_usage(p: &Parser, used: &[&str]) -> String {
debugln!("usage::smart_usage;");
let mut usage = String::with_capacity(75);
let mut hs: Vec<&str> = p.required().map(|s| &**s).collect();
hs.extend_from_slice(used);
let r_string = get_required_usage_from(p, &hs, None, None, false)
.iter()
.fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]);
usage.push_str(
&p.meta
.usage
.as_ref()
.unwrap_or_else(|| p.meta.bin_name.as_ref().unwrap_or(&p.meta.name))[..],
);
usage.push_str(&*r_string);
if p.is_set(AS::SubcommandRequired) {
usage.push_str(" <SUBCOMMAND>");
}
usage.shrink_to_fit();
usage
}
// Gets the `[ARGS]` tag for the usage string
fn get_args_tag(p: &Parser, incl_reqs: bool) -> Option<String> {
debugln!("usage::get_args_tag;");
let mut count = 0;
'outer: for pos in p
.positionals
.values()
.filter(|pos| !pos.is_set(ArgSettings::Required))
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
.filter(|pos| !pos.is_set(ArgSettings::Last))
{
debugln!("usage::get_args_tag:iter:{}:", pos.b.name);
if let Some(g_vec) = p.groups_for_arg(pos.b.name) {
for grp_s in &g_vec {
debugln!("usage::get_args_tag:iter:{}:iter:{};", pos.b.name, grp_s);
// if it's part of a required group we don't want to count it
if p.groups.iter().any(|g| g.required && (&g.name == grp_s)) {
continue 'outer;
}
}
}
count += 1;
debugln!(
"usage::get_args_tag:iter: {} Args not required or hidden",
count
);
}
if !p.is_set(AS::DontCollapseArgsInUsage) && count > 1 {
debugln!("usage::get_args_tag:iter: More than one, returning [ARGS]");
return None; // [ARGS]
} else if count == 1 && incl_reqs {
let pos = p
.positionals
.values()
.find(|pos| {
!pos.is_set(ArgSettings::Required)
&& !pos.is_set(ArgSettings::Hidden)
&& !pos.is_set(ArgSettings::Last)
})
.expect(INTERNAL_ERROR_MSG);
debugln!(
"usage::get_args_tag:iter: Exactly one, returning '{}'",
pos.name()
);
return Some(format!(
" [{}]{}",
pos.name_no_brackets(),
pos.multiple_str()
));
} else if p.is_set(AS::DontCollapseArgsInUsage) && !p.positionals.is_empty() && incl_reqs {
debugln!("usage::get_args_tag:iter: Don't collapse returning all");
return Some(
p.positionals
.values()
.filter(|pos| !pos.is_set(ArgSettings::Required))
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
.filter(|pos| !pos.is_set(ArgSettings::Last))
.map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str()))
.collect::<Vec<_>>()
.join(""),
);
} else if !incl_reqs {
debugln!("usage::get_args_tag:iter: incl_reqs=false, building secondary usage string");
let highest_req_pos = p
.positionals
.iter()
.filter_map(|(idx, pos)| {
if pos.b.is_set(ArgSettings::Required) && !pos.b.is_set(ArgSettings::Last) {
Some(idx)
} else {
None
}
})
.max()
.unwrap_or_else(|| p.positionals.len());
return Some(
p.positionals
.iter()
.filter_map(|(idx, pos)| {
if idx <= highest_req_pos {
Some(pos)
} else {
None
}
})
.filter(|pos| !pos.is_set(ArgSettings::Required))
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
.filter(|pos| !pos.is_set(ArgSettings::Last))
.map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str()))
.collect::<Vec<_>>()
.join(""),
);
}
Some("".into())
}
// Determines if we need the `[FLAGS]` tag in the usage string
fn needs_flags_tag(p: &Parser) -> bool {
debugln!("usage::needs_flags_tag;");
'outer: for f in &p.flags {
debugln!("usage::needs_flags_tag:iter: f={};", f.b.name);
if let Some(l) = f.s.long {
if l == "help" || l == "version" {
// Don't print `[FLAGS]` just for help or version
continue;
}
}
if let Some(g_vec) = p.groups_for_arg(f.b.name) {
for grp_s in &g_vec {
debugln!("usage::needs_flags_tag:iter:iter: grp_s={};", grp_s);
if p.groups.iter().any(|g| &g.name == grp_s && g.required) {
debugln!("usage::needs_flags_tag:iter:iter: Group is required");
continue 'outer;
}
}
}
if f.is_set(ArgSettings::Hidden) {
continue;
}
debugln!("usage::needs_flags_tag:iter: [FLAGS] required");
return true;
}
debugln!("usage::needs_flags_tag: [FLAGS] not required");
false
}
// Returns the required args in usage string form by fully unrolling all groups
pub fn get_required_usage_from<'a, 'b>(
p: &Parser<'a, 'b>,
reqs: &[&'a str],
matcher: Option<&ArgMatcher<'a>>,
extra: Option<&str>,
incl_last: bool,
) -> VecDeque<String> {
debugln!(
"usage::get_required_usage_from: reqs={:?}, extra={:?}",
reqs,
extra
);
let mut desc_reqs: Vec<&str> = vec![];
desc_reqs.extend(extra);
let mut new_reqs: Vec<&str> = vec![];
macro_rules! get_requires {
(@group $a: ident, $v:ident, $p:ident) => {{
if let Some(rl) = p
.groups
.iter()
.filter(|g| g.requires.is_some())
.find(|g| &g.name == $a)
.map(|g| g.requires.as_ref().unwrap())
{
for r in rl {
if !$p.contains(&r) {
debugln!(
"usage::get_required_usage_from:iter:{}: adding group req={:?}",
$a,
r
);
$v.push(r);
}
}
}
}};
($a:ident, $what:ident, $how:ident, $v:ident, $p:ident) => {{
if let Some(rl) = p
.$what
.$how()
.filter(|a| a.b.requires.is_some())
.find(|arg| &arg.b.name == $a)
.map(|a| a.b.requires.as_ref().unwrap())
{
for &(_, r) in rl.iter() {
if !$p.contains(&r) {
debugln!(
"usage::get_required_usage_from:iter:{}: adding arg req={:?}",
$a,
r
);
$v.push(r);
}
}
}
}};
}
// initialize new_reqs
for a in reqs {
get_requires!(a, flags, iter, new_reqs, reqs);
get_requires!(a, opts, iter, new_reqs, reqs);
get_requires!(a, positionals, values, new_reqs, reqs);
get_requires!(@group a, new_reqs, reqs);
}
desc_reqs.extend_from_slice(&*new_reqs);
debugln!(
"usage::get_required_usage_from: after init desc_reqs={:?}",
desc_reqs
);
loop {
let mut tmp = vec![];
for a in &new_reqs {
get_requires!(a, flags, iter, tmp, desc_reqs);
get_requires!(a, opts, iter, tmp, desc_reqs);
get_requires!(a, positionals, values, tmp, desc_reqs);
get_requires!(@group a, tmp, desc_reqs);
}
if tmp.is_empty() {
debugln!("usage::get_required_usage_from: no more children");
break;
} else {
debugln!("usage::get_required_usage_from: after iter tmp={:?}", tmp);
debugln!(
"usage::get_required_usage_from: after iter new_reqs={:?}",
new_reqs
);
desc_reqs.extend_from_slice(&*new_reqs);
new_reqs.clear();
new_reqs.extend_from_slice(&*tmp);
debugln!(
"usage::get_required_usage_from: after iter desc_reqs={:?}",
desc_reqs
);
}
}
desc_reqs.extend_from_slice(reqs);
desc_reqs.sort_unstable();
desc_reqs.dedup();
debugln!(
"usage::get_required_usage_from: final desc_reqs={:?}",
desc_reqs
);
let mut ret_val = VecDeque::new();
let args_in_groups = p
.groups
.iter()
.filter(|gn| desc_reqs.contains(&gn.name))
.flat_map(|g| p.arg_names_in_group(g.name))
.collect::<Vec<_>>();
let pmap = if let Some(m) = matcher {
desc_reqs
.iter()
.filter(|a| p.positionals.values().any(|p| &&p.b.name == a))
.filter(|&pos| !m.contains(pos))
.filter_map(|pos| p.positionals.values().find(|x| &x.b.name == pos))
.filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
.filter(|pos| !args_in_groups.contains(&pos.b.name))
.map(|pos| (pos.index, pos))
.collect::<BTreeMap<u64, &PosBuilder>>() // sort by index
} else {
desc_reqs
.iter()
.filter(|a| p.positionals.values().any(|pos| &&pos.b.name == a))
.filter_map(|pos| p.positionals.values().find(|x| &x.b.name == pos))
.filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
.filter(|pos| !args_in_groups.contains(&pos.b.name))
.map(|pos| (pos.index, pos))
.collect::<BTreeMap<u64, &PosBuilder>>() // sort by index
};
debugln!(
"usage::get_required_usage_from: args_in_groups={:?}",
args_in_groups
);
for &p in pmap.values() {
let s = p.to_string();
if args_in_groups.is_empty() || !args_in_groups.contains(&&*s) {
ret_val.push_back(s);
}
}
for a in desc_reqs
.iter()
.filter(|name| !p.positionals.values().any(|p| &&p.b.name == name))
.filter(|name| !p.groups.iter().any(|g| &&g.name == name))
.filter(|name| !args_in_groups.contains(name))
.filter(|name| !(matcher.is_some() && matcher.as_ref().unwrap().contains(name)))
{
debugln!("usage::get_required_usage_from:iter:{}:", a);
let arg = find_by_name!(p, *a, flags, iter)
.map(|f| f.to_string())
.unwrap_or_else(|| {
find_by_name!(p, *a, opts, iter)
.map(|o| o.to_string())
.expect(INTERNAL_ERROR_MSG)
});
ret_val.push_back(arg);
}
let mut g_vec: Vec<String> = vec![];
for g in desc_reqs
.iter()
.filter(|n| p.groups.iter().any(|g| &&g.name == n))
{
let g_string = p.args_in_group(g).join("|");
let elem = format!("<{}>", &g_string[..g_string.len()]);
if !g_vec.contains(&elem) {
g_vec.push(elem);
}
}
for g in g_vec {
ret_val.push_back(g);
}
ret_val
}

View File

@ -1,584 +0,0 @@
// std
#[allow(deprecated, unused_imports)]
use std::{ascii::AsciiExt, fmt::Display};
// Internal
use crate::{
app::{
parser::{ParseResult, Parser},
settings::AppSettings as AS,
usage,
},
args::{settings::ArgSettings, AnyArg, ArgMatcher, MatchedArg},
errors::{Error, ErrorKind, Result as ClapResult},
fmt::{Colorizer, ColorizerOption},
INTERNAL_ERROR_MSG, INVALID_UTF8,
};
pub struct Validator<'a, 'b, 'z>(&'z mut Parser<'a, 'b>)
where
'a: 'b,
'b: 'z;
impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
pub fn new(p: &'z mut Parser<'a, 'b>) -> Self {
Validator(p)
}
pub fn validate(
&mut self,
needs_val_of: ParseResult<'a>,
subcmd_name: Option<String>,
matcher: &mut ArgMatcher<'a>,
) -> ClapResult<()> {
debugln!("Validator::validate;");
let mut reqs_validated = false;
self.0.add_env(matcher)?;
self.0.add_defaults(matcher)?;
if let ParseResult::Opt(a) = needs_val_of {
debugln!("Validator::validate: needs_val_of={:?}", a);
let o = {
self.0
.opts
.iter()
.find(|o| o.b.name == a)
.expect(INTERNAL_ERROR_MSG)
.clone()
};
self.validate_required(matcher)?;
reqs_validated = true;
let should_err = if let Some(v) = matcher.0.args.get(&*o.b.name) {
v.vals.is_empty() && !(o.v.min_vals.is_some() && o.v.min_vals.unwrap() == 0)
} else {
true
};
if should_err {
return Err(Error::empty_value(
&o,
&*usage::create_error_usage(self.0, matcher, None),
self.0.color(),
));
}
}
if matcher.is_empty()
&& matcher.subcommand_name().is_none()
&& self.0.is_set(AS::ArgRequiredElseHelp)
{
let mut out = vec![];
self.0.write_help_err(&mut out)?;
return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
}
self.validate_blacklist(matcher)?;
if !(reqs_validated || self.0.is_set(AS::SubcommandsNegateReqs) && subcmd_name.is_some()) {
self.validate_required(matcher)?;
}
self.validate_matched_args(matcher)?;
matcher.usage(usage::create_usage_with_title(self.0, &[]));
Ok(())
}
fn validate_arg_values<A>(
&self,
arg: &A,
ma: &MatchedArg,
matcher: &ArgMatcher<'a>,
) -> ClapResult<()>
where
A: AnyArg<'a, 'b> + Display,
{
debugln!("Validator::validate_arg_values: arg={:?}", arg.name());
for val in &ma.vals {
if self.0.is_set(AS::StrictUtf8) && val.to_str().is_none() {
debugln!(
"Validator::validate_arg_values: invalid UTF-8 found in val {:?}",
val
);
return Err(Error::invalid_utf8(
&*usage::create_error_usage(self.0, matcher, None),
self.0.color(),
));
}
if let Some(p_vals) = arg.possible_vals() {
debugln!("Validator::validate_arg_values: possible_vals={:?}", p_vals);
let val_str = val.to_string_lossy();
let ok = if arg.is_set(ArgSettings::CaseInsensitive) {
p_vals.iter().any(|pv| pv.eq_ignore_ascii_case(&*val_str))
} else {
p_vals.contains(&&*val_str)
};
if !ok {
return Err(Error::invalid_value(
val_str,
p_vals,
arg,
&*usage::create_error_usage(self.0, matcher, None),
self.0.color(),
));
}
}
if !arg.is_set(ArgSettings::EmptyValues)
&& val.is_empty()
&& matcher.contains(&*arg.name())
{
debugln!("Validator::validate_arg_values: illegal empty val found");
return Err(Error::empty_value(
arg,
&*usage::create_error_usage(self.0, matcher, None),
self.0.color(),
));
}
if let Some(vtor) = arg.validator() {
debug!("Validator::validate_arg_values: checking validator...");
if let Err(e) = vtor(val.to_string_lossy().into_owned()) {
sdebugln!("error");
return Err(Error::value_validation(Some(arg), e, self.0.color()));
} else {
sdebugln!("good");
}
}
if let Some(vtor) = arg.validator_os() {
debug!("Validator::validate_arg_values: checking validator_os...");
if let Err(e) = vtor(val) {
sdebugln!("error");
return Err(Error::value_validation(
Some(arg),
(*e).to_string_lossy().to_string(),
self.0.color(),
));
} else {
sdebugln!("good");
}
}
}
Ok(())
}
fn build_err(&self, name: &str, matcher: &ArgMatcher) -> ClapResult<()> {
debugln!("build_err!: name={}", name);
let mut c_with = find_from!(self.0, &name, blacklist, matcher);
c_with = c_with.or_else(|| {
self.0
.find_any_arg(name)
.and_then(|aa| aa.blacklist())
.and_then(|bl| bl.iter().find(|arg| matcher.contains(arg)))
.and_then(|an| self.0.find_any_arg(an))
.map(|aa| format!("{}", aa))
});
debugln!("build_err!: '{:?}' conflicts with '{}'", c_with, &name);
// matcher.remove(&name);
let usg = usage::create_error_usage(self.0, matcher, None);
if let Some(f) = find_by_name!(self.0, name, flags, iter) {
debugln!("build_err!: It was a flag...");
Err(Error::argument_conflict(f, c_with, &*usg, self.0.color()))
} else if let Some(o) = find_by_name!(self.0, name, opts, iter) {
debugln!("build_err!: It was an option...");
Err(Error::argument_conflict(o, c_with, &*usg, self.0.color()))
} else {
match find_by_name!(self.0, name, positionals, values) {
Some(p) => {
debugln!("build_err!: It was a positional...");
Err(Error::argument_conflict(p, c_with, &*usg, self.0.color()))
}
None => panic!("{}", INTERNAL_ERROR_MSG),
}
}
}
fn validate_blacklist(&self, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("Validator::validate_blacklist;");
let mut conflicts: Vec<&str> = vec![];
for (&name, _) in matcher.iter() {
debugln!("Validator::validate_blacklist:iter:{};", name);
if let Some(grps) = self.0.groups_for_arg(name) {
for grp in &grps {
if let Some(g) = self.0.groups.iter().find(|g| &g.name == grp) {
if !g.multiple {
for arg in &g.args {
if arg == &name {
continue;
}
conflicts.push(arg);
}
}
if let Some(ref gc) = g.conflicts {
conflicts.extend(&*gc);
}
}
}
}
if let Some(arg) = find_any_by_name!(self.0, name) {
if let Some(bl) = arg.blacklist() {
for conf in bl {
if matcher.get(conf).is_some() {
conflicts.push(conf);
}
}
}
} else {
debugln!("Validator::validate_blacklist:iter:{}:group;", name);
let args = self.0.arg_names_in_group(name);
for arg in &args {
debugln!(
"Validator::validate_blacklist:iter:{}:group:iter:{};",
name,
arg
);
if let Some(bl) = find_any_by_name!(self.0, *arg).unwrap().blacklist() {
for conf in bl {
if matcher.get(conf).is_some() {
conflicts.push(conf);
}
}
}
}
}
}
for name in &conflicts {
debugln!(
"Validator::validate_blacklist:iter:{}: Checking blacklisted arg",
name
);
let mut should_err = false;
if self.0.groups.iter().any(|g| &g.name == name) {
debugln!(
"Validator::validate_blacklist:iter:{}: groups contains it...",
name
);
for n in self.0.arg_names_in_group(name) {
debugln!(
"Validator::validate_blacklist:iter:{}:iter:{}: looking in group...",
name,
n
);
if matcher.contains(n) {
debugln!(
"Validator::validate_blacklist:iter:{}:iter:{}: matcher contains it...",
name,
n
);
return self.build_err(n, matcher);
}
}
} else if let Some(ma) = matcher.get(name) {
debugln!(
"Validator::validate_blacklist:iter:{}: matcher contains it...",
name
);
should_err = ma.occurs > 0;
}
if should_err {
return self.build_err(*name, matcher);
}
}
Ok(())
}
fn validate_matched_args(&self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
debugln!("Validator::validate_matched_args;");
for (name, ma) in matcher.iter() {
debugln!(
"Validator::validate_matched_args:iter:{}: vals={:#?}",
name,
ma.vals
);
if let Some(opt) = find_by_name!(self.0, *name, opts, iter) {
self.validate_arg_num_vals(opt, ma, matcher)?;
self.validate_arg_values(opt, ma, matcher)?;
self.validate_arg_requires(opt, ma, matcher)?;
self.validate_arg_num_occurs(opt, ma, matcher)?;
} else if let Some(flag) = find_by_name!(self.0, *name, flags, iter) {
self.validate_arg_requires(flag, ma, matcher)?;
self.validate_arg_num_occurs(flag, ma, matcher)?;
} else if let Some(pos) = find_by_name!(self.0, *name, positionals, values) {
self.validate_arg_num_vals(pos, ma, matcher)?;
self.validate_arg_num_occurs(pos, ma, matcher)?;
self.validate_arg_values(pos, ma, matcher)?;
self.validate_arg_requires(pos, ma, matcher)?;
} else {
let grp = self
.0
.groups
.iter()
.find(|g| &g.name == name)
.expect(INTERNAL_ERROR_MSG);
if let Some(ref g_reqs) = grp.requires {
if g_reqs.iter().any(|&n| !matcher.contains(n)) {
return self.missing_required_error(matcher, None);
}
}
}
}
Ok(())
}
fn validate_arg_num_occurs<A>(
&self,
a: &A,
ma: &MatchedArg,
matcher: &ArgMatcher,
) -> ClapResult<()>
where
A: AnyArg<'a, 'b> + Display,
{
debugln!("Validator::validate_arg_num_occurs: a={};", a.name());
if ma.occurs > 1 && !a.is_set(ArgSettings::Multiple) {
// Not the first time, and we don't allow multiples
return Err(Error::unexpected_multiple_usage(
a,
&*usage::create_error_usage(self.0, matcher, None),
self.0.color(),
));
}
Ok(())
}
fn validate_arg_num_vals<A>(
&self,
a: &A,
ma: &MatchedArg,
matcher: &ArgMatcher,
) -> ClapResult<()>
where
A: AnyArg<'a, 'b> + Display,
{
debugln!("Validator::validate_arg_num_vals:{}", a.name());
if let Some(num) = a.num_vals() {
debugln!("Validator::validate_arg_num_vals: num_vals set...{}", num);
let should_err = if a.is_set(ArgSettings::Multiple) {
((ma.vals.len() as u64) % num) != 0
} else {
num != (ma.vals.len() as u64)
};
if should_err {
debugln!("Validator::validate_arg_num_vals: Sending error WrongNumberOfValues");
return Err(Error::wrong_number_of_values(
a,
num,
if a.is_set(ArgSettings::Multiple) {
ma.vals.len() % num as usize
} else {
ma.vals.len()
},
if ma.vals.len() == 1
|| (a.is_set(ArgSettings::Multiple) && (ma.vals.len() % num as usize) == 1)
{
"as"
} else {
"ere"
},
&*usage::create_error_usage(self.0, matcher, None),
self.0.color(),
));
}
}
if let Some(num) = a.max_vals() {
debugln!("Validator::validate_arg_num_vals: max_vals set...{}", num);
if (ma.vals.len() as u64) > num {
debugln!("Validator::validate_arg_num_vals: Sending error TooManyValues");
return Err(Error::too_many_values(
ma.vals
.iter()
.last()
.expect(INTERNAL_ERROR_MSG)
.to_str()
.expect(INVALID_UTF8),
a,
&*usage::create_error_usage(self.0, matcher, None),
self.0.color(),
));
}
}
let min_vals_zero = if let Some(num) = a.min_vals() {
debugln!("Validator::validate_arg_num_vals: min_vals set: {}", num);
if (ma.vals.len() as u64) < num && num != 0 {
debugln!("Validator::validate_arg_num_vals: Sending error TooFewValues");
return Err(Error::too_few_values(
a,
num,
ma.vals.len(),
&*usage::create_error_usage(self.0, matcher, None),
self.0.color(),
));
}
num == 0
} else {
false
};
// Issue 665 (https://github.com/clap-rs/clap/issues/665)
// Issue 1105 (https://github.com/clap-rs/clap/issues/1105)
if a.takes_value() && !min_vals_zero && ma.vals.is_empty() {
return Err(Error::empty_value(
a,
&*usage::create_error_usage(self.0, matcher, None),
self.0.color(),
));
}
Ok(())
}
fn validate_arg_requires<A>(
&self,
a: &A,
ma: &MatchedArg,
matcher: &ArgMatcher,
) -> ClapResult<()>
where
A: AnyArg<'a, 'b> + Display,
{
debugln!("Validator::validate_arg_requires:{};", a.name());
if let Some(a_reqs) = a.requires() {
for &(val, name) in a_reqs.iter().filter(|&&(val, _)| val.is_some()) {
let missing_req =
|v| v == val.expect(INTERNAL_ERROR_MSG) && !matcher.contains(name);
if ma.vals.iter().any(missing_req) {
return self.missing_required_error(matcher, None);
}
}
for &(_, name) in a_reqs.iter().filter(|&&(val, _)| val.is_none()) {
if !matcher.contains(name) {
return self.missing_required_error(matcher, Some(name));
}
}
}
Ok(())
}
fn validate_required(&mut self, matcher: &ArgMatcher) -> ClapResult<()> {
debugln!(
"Validator::validate_required: required={:?};",
self.0.required
);
let mut should_err = false;
let mut to_rem = Vec::new();
for name in &self.0.required {
debugln!("Validator::validate_required:iter:{}:", name);
if matcher.contains(name) {
continue;
}
if to_rem.contains(name) {
continue;
} else if let Some(a) = find_any_by_name!(self.0, *name) {
if self.is_missing_required_ok(a, matcher) {
to_rem.push(a.name());
if let Some(reqs) = a.requires() {
for r in reqs
.iter()
.filter(|&&(val, _)| val.is_none())
.map(|&(_, name)| name)
{
to_rem.push(r);
}
}
continue;
}
}
should_err = true;
break;
}
if should_err {
for r in &to_rem {
'inner: for i in (0..self.0.required.len()).rev() {
if &self.0.required[i] == r {
self.0.required.swap_remove(i);
break 'inner;
}
}
}
return self.missing_required_error(matcher, None);
}
// Validate the conditionally required args
for &(a, v, r) in &self.0.r_ifs {
if let Some(ma) = matcher.get(a) {
if matcher.get(r).is_none() && ma.vals.iter().any(|val| val == v) {
return self.missing_required_error(matcher, Some(r));
}
}
}
Ok(())
}
fn validate_arg_conflicts(&self, a: &AnyArg, matcher: &ArgMatcher) -> Option<bool> {
debugln!("Validator::validate_arg_conflicts: a={:?};", a.name());
a.blacklist().map(|bl| {
bl.iter().any(|conf| {
matcher.contains(conf)
|| self
.0
.groups
.iter()
.find(|g| &g.name == conf)
.map_or(false, |g| g.args.iter().any(|arg| matcher.contains(arg)))
})
})
}
fn validate_required_unless(&self, a: &AnyArg, matcher: &ArgMatcher) -> Option<bool> {
debugln!("Validator::validate_required_unless: a={:?};", a.name());
macro_rules! check {
($how:ident, $_self:expr, $a:ident, $m:ident) => {{
$a.required_unless().map(|ru| {
ru.iter().$how(|n| {
$m.contains(n) || {
if let Some(grp) = $_self.groups.iter().find(|g| &g.name == n) {
grp.args.iter().any(|arg| $m.contains(arg))
} else {
false
}
}
})
})
}};
}
if a.is_set(ArgSettings::RequiredUnlessAll) {
check!(all, self.0, a, matcher)
} else {
check!(any, self.0, a, matcher)
}
}
fn missing_required_error(&self, matcher: &ArgMatcher, extra: Option<&str>) -> ClapResult<()> {
debugln!("Validator::missing_required_error: extra={:?}", extra);
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: self.0.color(),
});
let mut reqs = self.0.required.iter().map(|&r| &*r).collect::<Vec<_>>();
if let Some(r) = extra {
reqs.push(r);
}
reqs.retain(|n| !matcher.contains(n));
reqs.dedup();
debugln!("Validator::missing_required_error: reqs={:#?}", reqs);
let req_args =
usage::get_required_usage_from(self.0, &reqs[..], Some(matcher), extra, true)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
});
debugln!(
"Validator::missing_required_error: req_args={:#?}",
req_args
);
Err(Error::missing_required_argument(
&*req_args,
&*usage::create_error_usage(self.0, matcher, extra),
self.0.color(),
))
}
#[inline]
fn is_missing_required_ok(&self, a: &AnyArg, matcher: &ArgMatcher) -> bool {
debugln!("Validator::is_missing_required_ok: a={}", a.name());
self.validate_arg_conflicts(a, matcher).unwrap_or(false)
|| self.validate_required_unless(a, matcher).unwrap_or(false)
}
}

View File

@ -1,139 +0,0 @@
// Std
use std::{
ffi::{OsStr, OsString},
fmt as std_fmt,
rc::Rc,
};
// Internal
use crate::{
args::settings::ArgSettings,
map::{self, VecMap},
INTERNAL_ERROR_MSG,
};
#[doc(hidden)]
pub trait AnyArg<'n, 'e>: std_fmt::Display {
fn name(&self) -> &'n str;
fn overrides(&self) -> Option<&[&'e str]>;
fn aliases(&self) -> Option<Vec<&'e str>>;
fn requires(&self) -> Option<&[(Option<&'e str>, &'n str)]>;
fn blacklist(&self) -> Option<&[&'e str]>;
fn required_unless(&self) -> Option<&[&'e str]>;
fn is_set(&self, setting: ArgSettings) -> bool;
fn set(&mut self, setting: ArgSettings);
fn has_switch(&self) -> bool;
fn max_vals(&self) -> Option<u64>;
fn min_vals(&self) -> Option<u64>;
fn num_vals(&self) -> Option<u64>;
fn possible_vals(&self) -> Option<&[&'e str]>;
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
fn validator(&self) -> Option<&Rc<Fn(String) -> Result<(), String>>>;
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
fn validator_os(&self) -> Option<&Rc<Fn(&OsStr) -> Result<(), OsString>>>;
fn short(&self) -> Option<char>;
fn long(&self) -> Option<&'e str>;
fn val_delim(&self) -> Option<char>;
fn takes_value(&self) -> bool;
fn val_names(&self) -> Option<&VecMap<&'e str>>;
fn help(&self) -> Option<&'e str>;
fn long_help(&self) -> Option<&'e str>;
fn default_val(&self) -> Option<&'e OsStr>;
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>>;
fn env<'s>(&'s self) -> Option<(&'n OsStr, Option<&'s OsString>)>;
fn longest_filter(&self) -> bool;
fn val_terminator(&self) -> Option<&'e str>;
}
pub trait DispOrder {
fn disp_ord(&self) -> usize;
}
impl<'n, 'e, 'z, T: ?Sized> AnyArg<'n, 'e> for &'z T
where
T: AnyArg<'n, 'e> + 'z,
{
fn name(&self) -> &'n str {
(*self).name()
}
fn overrides(&self) -> Option<&[&'e str]> {
(*self).overrides()
}
fn aliases(&self) -> Option<Vec<&'e str>> {
(*self).aliases()
}
fn requires(&self) -> Option<&[(Option<&'e str>, &'n str)]> {
(*self).requires()
}
fn blacklist(&self) -> Option<&[&'e str]> {
(*self).blacklist()
}
fn required_unless(&self) -> Option<&[&'e str]> {
(*self).required_unless()
}
fn is_set(&self, a: ArgSettings) -> bool {
(*self).is_set(a)
}
fn set(&mut self, _: ArgSettings) {
panic!("{}", INTERNAL_ERROR_MSG)
}
fn has_switch(&self) -> bool {
(*self).has_switch()
}
fn max_vals(&self) -> Option<u64> {
(*self).max_vals()
}
fn min_vals(&self) -> Option<u64> {
(*self).min_vals()
}
fn num_vals(&self) -> Option<u64> {
(*self).num_vals()
}
fn possible_vals(&self) -> Option<&[&'e str]> {
(*self).possible_vals()
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
fn validator(&self) -> Option<&Rc<Fn(String) -> Result<(), String>>> {
(*self).validator()
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
fn validator_os(&self) -> Option<&Rc<Fn(&OsStr) -> Result<(), OsString>>> {
(*self).validator_os()
}
fn short(&self) -> Option<char> {
(*self).short()
}
fn long(&self) -> Option<&'e str> {
(*self).long()
}
fn val_delim(&self) -> Option<char> {
(*self).val_delim()
}
fn takes_value(&self) -> bool {
(*self).takes_value()
}
fn val_names(&self) -> Option<&VecMap<&'e str>> {
(*self).val_names()
}
fn help(&self) -> Option<&'e str> {
(*self).help()
}
fn long_help(&self) -> Option<&'e str> {
(*self).long_help()
}
fn default_val(&self) -> Option<&'e OsStr> {
(*self).default_val()
}
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
(*self).default_vals_ifs()
}
fn env<'s>(&'s self) -> Option<(&'n OsStr, Option<&'s OsString>)> {
(*self).env()
}
fn longest_filter(&self) -> bool {
(*self).longest_filter()
}
fn val_terminator(&self) -> Option<&'e str> {
(*self).val_terminator()
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,48 +0,0 @@
use crate::args::{Arg, ArgFlags, ArgSettings};
#[derive(Debug, Clone, Default)]
pub struct Base<'a, 'b>
where
'a: 'b,
{
pub name: &'a str,
pub help: Option<&'b str>,
pub long_help: Option<&'b str>,
pub blacklist: Option<Vec<&'a str>>,
pub settings: ArgFlags,
pub r_unless: Option<Vec<&'a str>>,
pub overrides: Option<Vec<&'a str>>,
pub groups: Option<Vec<&'a str>>,
pub requires: Option<Vec<(Option<&'b str>, &'a str)>>,
}
impl<'n, 'e> Base<'n, 'e> {
pub fn new(name: &'n str) -> Self {
Base {
name,
..Default::default()
}
}
pub fn set(&mut self, s: ArgSettings) {
self.settings.set(s);
}
pub fn unset(&mut self, s: ArgSettings) {
self.settings.unset(s);
}
pub fn is_set(&self, s: ArgSettings) -> bool {
self.settings.is_set(s)
}
}
impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Base<'n, 'e> {
fn from(a: &'z Arg<'n, 'e>) -> Self {
a.b.clone()
}
}
impl<'n, 'e> PartialEq for Base<'n, 'e> {
fn eq(&self, other: &Base<'n, 'e>) -> bool {
self.name == other.name
}
}

View File

@ -1,216 +0,0 @@
// Std
use std::{
convert::From,
ffi::{OsStr, OsString},
fmt::{Display, Formatter, Result},
mem,
rc::Rc,
result::Result as StdResult,
};
// Internal
use crate::{
args::{AnyArg, Arg, ArgSettings, Base, DispOrder, Switched},
map::{self, VecMap},
};
#[derive(Default, Clone, Debug)]
#[doc(hidden)]
pub struct FlagBuilder<'n, 'e>
where
'n: 'e,
{
pub b: Base<'n, 'e>,
pub s: Switched<'e>,
}
impl<'n, 'e> FlagBuilder<'n, 'e> {
pub fn new(name: &'n str) -> Self {
FlagBuilder {
b: Base::new(name),
..Default::default()
}
}
}
impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for FlagBuilder<'a, 'b> {
fn from(a: &'z Arg<'a, 'b>) -> Self {
FlagBuilder {
b: Base::from(a),
s: Switched::from(a),
}
}
}
impl<'a, 'b> From<Arg<'a, 'b>> for FlagBuilder<'a, 'b> {
fn from(mut a: Arg<'a, 'b>) -> Self {
FlagBuilder {
b: mem::take(&mut a.b),
s: mem::take(&mut a.s),
}
}
}
impl<'n, 'e> Display for FlagBuilder<'n, 'e> {
fn fmt(&self, f: &mut Formatter) -> Result {
if let Some(l) = self.s.long {
write!(f, "--{}", l)?;
} else {
write!(f, "-{}", self.s.short.unwrap())?;
}
Ok(())
}
}
impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
fn name(&self) -> &'n str {
self.b.name
}
fn overrides(&self) -> Option<&[&'e str]> {
self.b.overrides.as_ref().map(|o| &o[..])
}
fn requires(&self) -> Option<&[(Option<&'e str>, &'n str)]> {
self.b.requires.as_ref().map(|o| &o[..])
}
fn blacklist(&self) -> Option<&[&'e str]> {
self.b.blacklist.as_ref().map(|o| &o[..])
}
fn required_unless(&self) -> Option<&[&'e str]> {
self.b.r_unless.as_ref().map(|o| &o[..])
}
fn is_set(&self, s: ArgSettings) -> bool {
self.b.settings.is_set(s)
}
fn has_switch(&self) -> bool {
true
}
fn takes_value(&self) -> bool {
false
}
fn set(&mut self, s: ArgSettings) {
self.b.settings.set(s)
}
fn max_vals(&self) -> Option<u64> {
None
}
fn val_names(&self) -> Option<&VecMap<&'e str>> {
None
}
fn num_vals(&self) -> Option<u64> {
None
}
fn possible_vals(&self) -> Option<&[&'e str]> {
None
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
None
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
fn validator_os(&self) -> Option<&Rc<Fn(&OsStr) -> StdResult<(), OsString>>> {
None
}
fn min_vals(&self) -> Option<u64> {
None
}
fn short(&self) -> Option<char> {
self.s.short
}
fn long(&self) -> Option<&'e str> {
self.s.long
}
fn val_delim(&self) -> Option<char> {
None
}
fn help(&self) -> Option<&'e str> {
self.b.help
}
fn long_help(&self) -> Option<&'e str> {
self.b.long_help
}
fn val_terminator(&self) -> Option<&'e str> {
None
}
fn default_val(&self) -> Option<&'e OsStr> {
None
}
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
None
}
fn env<'s>(&'s self) -> Option<(&'n OsStr, Option<&'s OsString>)> {
None
}
fn longest_filter(&self) -> bool {
self.s.long.is_some()
}
fn aliases(&self) -> Option<Vec<&'e str>> {
if let Some(ref aliases) = self.s.aliases {
let vis_aliases: Vec<_> = aliases
.iter()
.filter_map(|&(n, v)| if v { Some(n) } else { None })
.collect();
if vis_aliases.is_empty() {
None
} else {
Some(vis_aliases)
}
} else {
None
}
}
}
impl<'n, 'e> DispOrder for FlagBuilder<'n, 'e> {
fn disp_ord(&self) -> usize {
self.s.disp_ord
}
}
impl<'n, 'e> PartialEq for FlagBuilder<'n, 'e> {
fn eq(&self, other: &FlagBuilder<'n, 'e>) -> bool {
self.b == other.b
}
}
#[cfg(test)]
mod test {
use super::FlagBuilder;
use crate::args::settings::ArgSettings;
#[test]
fn flagbuilder_display() {
let mut f = FlagBuilder::new("flg");
f.b.settings.set(ArgSettings::Multiple);
f.s.long = Some("flag");
assert_eq!(&*format!("{}", f), "--flag");
let mut f2 = FlagBuilder::new("flg");
f2.s.short = Some('f');
assert_eq!(&*format!("{}", f2), "-f");
}
#[test]
fn flagbuilder_display_single_alias() {
let mut f = FlagBuilder::new("flg");
f.s.long = Some("flag");
f.s.aliases = Some(vec![("als", true)]);
assert_eq!(&*format!("{}", f), "--flag");
}
#[test]
fn flagbuilder_display_multiple_aliases() {
let mut f = FlagBuilder::new("flg");
f.s.short = Some('f');
f.s.aliases = Some(vec![
("alias_not_visible", false),
("f2", true),
("f3", true),
("f4", true),
]);
assert_eq!(&*format!("{}", f), "-f");
}
}

View File

@ -1,13 +0,0 @@
pub use self::base::Base;
pub use self::flag::FlagBuilder;
pub use self::option::OptBuilder;
pub use self::positional::PosBuilder;
pub use self::switched::Switched;
pub use self::valued::Valued;
mod base;
mod flag;
mod option;
mod positional;
mod switched;
mod valued;

View File

@ -1,295 +0,0 @@
// Std
use std::{
ffi::{OsStr, OsString},
fmt::{Display, Formatter, Result},
mem,
rc::Rc,
result::Result as StdResult,
};
// Internal
use crate::{
args::{AnyArg, Arg, ArgSettings, Base, DispOrder, Switched, Valued},
map::{self, VecMap},
INTERNAL_ERROR_MSG,
};
#[allow(missing_debug_implementations)]
#[doc(hidden)]
#[derive(Default, Clone)]
pub struct OptBuilder<'n, 'e>
where
'n: 'e,
{
pub b: Base<'n, 'e>,
pub s: Switched<'e>,
pub v: Valued<'n, 'e>,
}
impl<'n, 'e> OptBuilder<'n, 'e> {
pub fn new(name: &'n str) -> Self {
OptBuilder {
b: Base::new(name),
..Default::default()
}
}
}
impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for OptBuilder<'n, 'e> {
fn from(a: &'z Arg<'n, 'e>) -> Self {
OptBuilder {
b: Base::from(a),
s: Switched::from(a),
v: Valued::from(a),
}
}
}
impl<'n, 'e> From<Arg<'n, 'e>> for OptBuilder<'n, 'e> {
fn from(mut a: Arg<'n, 'e>) -> Self {
a.v.fill_in();
OptBuilder {
b: mem::take(&mut a.b),
s: mem::take(&mut a.s),
v: mem::take(&mut a.v),
}
}
}
impl<'n, 'e> Display for OptBuilder<'n, 'e> {
fn fmt(&self, f: &mut Formatter) -> Result {
debugln!("OptBuilder::fmt:{}", self.b.name);
let sep = if self.b.is_set(ArgSettings::RequireEquals) {
"="
} else {
" "
};
// Write the name such --long or -l
if let Some(l) = self.s.long {
write!(f, "--{}{}", l, sep)?;
} else {
write!(f, "-{}{}", self.s.short.unwrap(), sep)?;
}
let delim = if self.is_set(ArgSettings::RequireDelimiter) {
self.v.val_delim.expect(INTERNAL_ERROR_MSG)
} else {
' '
};
// Write the values such as <name1> <name2>
if let Some(ref vec) = self.v.val_names {
let mut it = vec.iter().peekable();
while let Some((_, val)) = it.next() {
write!(f, "<{}>", val)?;
if it.peek().is_some() {
write!(f, "{}", delim)?;
}
}
let num = vec.len();
if self.is_set(ArgSettings::Multiple) && num == 1 {
write!(f, "...")?;
}
} else if let Some(num) = self.v.num_vals {
let mut it = (0..num).peekable();
while let Some(_) = it.next() {
write!(f, "<{}>", self.b.name)?;
if it.peek().is_some() {
write!(f, "{}", delim)?;
}
}
if self.is_set(ArgSettings::Multiple) && num == 1 {
write!(f, "...")?;
}
} else {
write!(
f,
"<{}>{}",
self.b.name,
if self.is_set(ArgSettings::Multiple) {
"..."
} else {
""
}
)?;
}
Ok(())
}
}
impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
fn name(&self) -> &'n str {
self.b.name
}
fn overrides(&self) -> Option<&[&'e str]> {
self.b.overrides.as_ref().map(|o| &o[..])
}
fn requires(&self) -> Option<&[(Option<&'e str>, &'n str)]> {
self.b.requires.as_ref().map(|o| &o[..])
}
fn blacklist(&self) -> Option<&[&'e str]> {
self.b.blacklist.as_ref().map(|o| &o[..])
}
fn required_unless(&self) -> Option<&[&'e str]> {
self.b.r_unless.as_ref().map(|o| &o[..])
}
fn val_names(&self) -> Option<&VecMap<&'e str>> {
self.v.val_names.as_ref()
}
fn is_set(&self, s: ArgSettings) -> bool {
self.b.settings.is_set(s)
}
fn has_switch(&self) -> bool {
true
}
fn set(&mut self, s: ArgSettings) {
self.b.settings.set(s)
}
fn max_vals(&self) -> Option<u64> {
self.v.max_vals
}
fn val_terminator(&self) -> Option<&'e str> {
self.v.terminator
}
fn num_vals(&self) -> Option<u64> {
self.v.num_vals
}
fn possible_vals(&self) -> Option<&[&'e str]> {
self.v.possible_vals.as_ref().map(|o| &o[..])
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
self.v.validator.as_ref()
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
fn validator_os(&self) -> Option<&Rc<Fn(&OsStr) -> StdResult<(), OsString>>> {
self.v.validator_os.as_ref()
}
fn min_vals(&self) -> Option<u64> {
self.v.min_vals
}
fn short(&self) -> Option<char> {
self.s.short
}
fn long(&self) -> Option<&'e str> {
self.s.long
}
fn val_delim(&self) -> Option<char> {
self.v.val_delim
}
fn takes_value(&self) -> bool {
true
}
fn help(&self) -> Option<&'e str> {
self.b.help
}
fn long_help(&self) -> Option<&'e str> {
self.b.long_help
}
fn default_val(&self) -> Option<&'e OsStr> {
self.v.default_val
}
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
self.v.default_vals_ifs.as_ref().map(|vm| vm.values())
}
fn env<'s>(&'s self) -> Option<(&'n OsStr, Option<&'s OsString>)> {
self.v
.env
.as_ref()
.map(|&(key, ref value)| (key, value.as_ref()))
}
fn longest_filter(&self) -> bool {
true
}
fn aliases(&self) -> Option<Vec<&'e str>> {
if let Some(ref aliases) = self.s.aliases {
let vis_aliases: Vec<_> = aliases
.iter()
.filter_map(|&(n, v)| if v { Some(n) } else { None })
.collect();
if vis_aliases.is_empty() {
None
} else {
Some(vis_aliases)
}
} else {
None
}
}
}
impl<'n, 'e> DispOrder for OptBuilder<'n, 'e> {
fn disp_ord(&self) -> usize {
self.s.disp_ord
}
}
impl<'n, 'e> PartialEq for OptBuilder<'n, 'e> {
fn eq(&self, other: &OptBuilder<'n, 'e>) -> bool {
self.b == other.b
}
}
#[cfg(test)]
mod test {
use super::OptBuilder;
use crate::{args::settings::ArgSettings, map::VecMap};
#[test]
fn optbuilder_display1() {
let mut o = OptBuilder::new("opt");
o.s.long = Some("option");
o.b.settings.set(ArgSettings::Multiple);
assert_eq!(&*format!("{}", o), "--option <opt>...");
}
#[test]
fn optbuilder_display2() {
let mut v_names = VecMap::new();
v_names.insert(0, "file");
v_names.insert(1, "name");
let mut o2 = OptBuilder::new("opt");
o2.s.short = Some('o');
o2.v.val_names = Some(v_names);
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
}
#[test]
fn optbuilder_display3() {
let mut v_names = VecMap::new();
v_names.insert(0, "file");
v_names.insert(1, "name");
let mut o2 = OptBuilder::new("opt");
o2.s.short = Some('o');
o2.v.val_names = Some(v_names);
o2.b.settings.set(ArgSettings::Multiple);
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
}
#[test]
fn optbuilder_display_single_alias() {
let mut o = OptBuilder::new("opt");
o.s.long = Some("option");
o.s.aliases = Some(vec![("als", true)]);
assert_eq!(&*format!("{}", o), "--option <opt>");
}
#[test]
fn optbuilder_display_multiple_aliases() {
let mut o = OptBuilder::new("opt");
o.s.long = Some("option");
o.s.aliases = Some(vec![
("als_not_visible", false),
("als2", true),
("als3", true),
("als4", true),
]);
assert_eq!(&*format!("{}", o), "--option <opt>");
}
}

View File

@ -1,284 +0,0 @@
// Std
use std::{
borrow::Cow,
ffi::{OsStr, OsString},
fmt::{Display, Formatter, Result},
mem,
rc::Rc,
result::Result as StdResult,
};
// Internal
use crate::{
args::{AnyArg, Arg, ArgSettings, Base, DispOrder, Valued},
map::{self, VecMap},
INTERNAL_ERROR_MSG,
};
#[allow(missing_debug_implementations)]
#[doc(hidden)]
#[derive(Clone, Default)]
pub struct PosBuilder<'n, 'e>
where
'n: 'e,
{
pub b: Base<'n, 'e>,
pub v: Valued<'n, 'e>,
pub index: u64,
}
impl<'n, 'e> PosBuilder<'n, 'e> {
pub fn new(name: &'n str, idx: u64) -> Self {
PosBuilder {
b: Base::new(name),
index: idx,
..Default::default()
}
}
pub fn from_arg_ref(a: &Arg<'n, 'e>, idx: u64) -> Self {
let mut pb = PosBuilder {
b: Base::from(a),
v: Valued::from(a),
index: idx,
};
if a.v.max_vals.is_some()
|| a.v.min_vals.is_some()
|| (a.v.num_vals.is_some() && a.v.num_vals.unwrap() > 1)
{
pb.b.settings.set(ArgSettings::Multiple);
}
pb
}
pub fn from_arg(mut a: Arg<'n, 'e>, idx: u64) -> Self {
if a.v.max_vals.is_some()
|| a.v.min_vals.is_some()
|| (a.v.num_vals.is_some() && a.v.num_vals.unwrap() > 1)
{
a.b.settings.set(ArgSettings::Multiple);
}
PosBuilder {
b: mem::take(&mut a.b),
v: mem::take(&mut a.v),
index: idx,
}
}
pub fn multiple_str(&self) -> &str {
let mult_vals = self
.v
.val_names
.as_ref()
.map_or(true, |names| names.len() < 2);
if self.is_set(ArgSettings::Multiple) && mult_vals {
"..."
} else {
""
}
}
pub fn name_no_brackets(&self) -> Cow<str> {
debugln!("PosBuilder::name_no_brackets;");
let mut delim = String::new();
delim.push(if self.is_set(ArgSettings::RequireDelimiter) {
self.v.val_delim.expect(INTERNAL_ERROR_MSG)
} else {
' '
});
if let Some(ref names) = self.v.val_names {
debugln!("PosBuilder:name_no_brackets: val_names={:#?}", names);
if names.len() > 1 {
Cow::Owned(
names
.values()
.map(|n| format!("<{}>", n))
.collect::<Vec<_>>()
.join(&*delim),
)
} else {
Cow::Borrowed(names.values().next().expect(INTERNAL_ERROR_MSG))
}
} else {
debugln!("PosBuilder:name_no_brackets: just name");
Cow::Borrowed(self.b.name)
}
}
}
impl<'n, 'e> Display for PosBuilder<'n, 'e> {
fn fmt(&self, f: &mut Formatter) -> Result {
let mut delim = String::new();
delim.push(if self.is_set(ArgSettings::RequireDelimiter) {
self.v.val_delim.expect(INTERNAL_ERROR_MSG)
} else {
' '
});
if let Some(ref names) = self.v.val_names {
write!(
f,
"{}",
names
.values()
.map(|n| format!("<{}>", n))
.collect::<Vec<_>>()
.join(&*delim)
)?;
} else {
write!(f, "<{}>", self.b.name)?;
}
if self.b.settings.is_set(ArgSettings::Multiple)
&& (self.v.val_names.is_none() || self.v.val_names.as_ref().unwrap().len() == 1)
{
write!(f, "...")?;
}
Ok(())
}
}
impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
fn name(&self) -> &'n str {
self.b.name
}
fn overrides(&self) -> Option<&[&'e str]> {
self.b.overrides.as_ref().map(|o| &o[..])
}
fn requires(&self) -> Option<&[(Option<&'e str>, &'n str)]> {
self.b.requires.as_ref().map(|o| &o[..])
}
fn blacklist(&self) -> Option<&[&'e str]> {
self.b.blacklist.as_ref().map(|o| &o[..])
}
fn required_unless(&self) -> Option<&[&'e str]> {
self.b.r_unless.as_ref().map(|o| &o[..])
}
fn val_names(&self) -> Option<&VecMap<&'e str>> {
self.v.val_names.as_ref()
}
fn is_set(&self, s: ArgSettings) -> bool {
self.b.settings.is_set(s)
}
fn set(&mut self, s: ArgSettings) {
self.b.settings.set(s)
}
fn has_switch(&self) -> bool {
false
}
fn max_vals(&self) -> Option<u64> {
self.v.max_vals
}
fn val_terminator(&self) -> Option<&'e str> {
self.v.terminator
}
fn num_vals(&self) -> Option<u64> {
self.v.num_vals
}
fn possible_vals(&self) -> Option<&[&'e str]> {
self.v.possible_vals.as_ref().map(|o| &o[..])
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
self.v.validator.as_ref()
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
fn validator_os(&self) -> Option<&Rc<Fn(&OsStr) -> StdResult<(), OsString>>> {
self.v.validator_os.as_ref()
}
fn min_vals(&self) -> Option<u64> {
self.v.min_vals
}
fn short(&self) -> Option<char> {
None
}
fn long(&self) -> Option<&'e str> {
None
}
fn val_delim(&self) -> Option<char> {
self.v.val_delim
}
fn takes_value(&self) -> bool {
true
}
fn help(&self) -> Option<&'e str> {
self.b.help
}
fn long_help(&self) -> Option<&'e str> {
self.b.long_help
}
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
self.v.default_vals_ifs.as_ref().map(|vm| vm.values())
}
fn default_val(&self) -> Option<&'e OsStr> {
self.v.default_val
}
fn env<'s>(&'s self) -> Option<(&'n OsStr, Option<&'s OsString>)> {
self.v
.env
.as_ref()
.map(|&(key, ref value)| (key, value.as_ref()))
}
fn longest_filter(&self) -> bool {
true
}
fn aliases(&self) -> Option<Vec<&'e str>> {
None
}
}
impl<'n, 'e> DispOrder for PosBuilder<'n, 'e> {
fn disp_ord(&self) -> usize {
self.index as usize
}
}
impl<'n, 'e> PartialEq for PosBuilder<'n, 'e> {
fn eq(&self, other: &PosBuilder<'n, 'e>) -> bool {
self.b == other.b
}
}
#[cfg(test)]
mod test {
use super::PosBuilder;
use crate::{args::settings::ArgSettings, map::VecMap};
#[test]
fn display_mult() {
let mut p = PosBuilder::new("pos", 1);
p.b.settings.set(ArgSettings::Multiple);
assert_eq!(&*format!("{}", p), "<pos>...");
}
#[test]
fn display_required() {
let mut p2 = PosBuilder::new("pos", 1);
p2.b.settings.set(ArgSettings::Required);
assert_eq!(&*format!("{}", p2), "<pos>");
}
#[test]
fn display_val_names() {
let mut p2 = PosBuilder::new("pos", 1);
let mut vm = VecMap::new();
vm.insert(0, "file1");
vm.insert(1, "file2");
p2.v.val_names = Some(vm);
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
}
#[test]
fn display_val_names_req() {
let mut p2 = PosBuilder::new("pos", 1);
p2.b.settings.set(ArgSettings::Required);
let mut vm = VecMap::new();
vm.insert(0, "file1");
vm.insert(1, "file2");
p2.v.val_names = Some(vm);
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
}
}

View File

@ -1,40 +0,0 @@
use crate::Arg;
#[derive(Debug)]
pub struct Switched<'b> {
pub short: Option<char>,
pub long: Option<&'b str>,
pub aliases: Option<Vec<(&'b str, bool)>>, // (name, visible)
pub disp_ord: usize,
pub unified_ord: usize,
}
impl<'e> Default for Switched<'e> {
fn default() -> Self {
Switched {
short: None,
long: None,
aliases: None,
disp_ord: 999,
unified_ord: 999,
}
}
}
impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Switched<'e> {
fn from(a: &'z Arg<'n, 'e>) -> Self {
a.s.clone()
}
}
impl<'e> Clone for Switched<'e> {
fn clone(&self) -> Self {
Switched {
short: self.short,
long: self.long,
aliases: self.aliases.clone(),
disp_ord: self.disp_ord,
unified_ord: self.unified_ord,
}
}
}

View File

@ -1,70 +0,0 @@
use std::{
ffi::{OsStr, OsString},
rc::Rc,
};
use crate::{map::VecMap, Arg};
#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub struct Valued<'a, 'b>
where
'a: 'b,
{
pub possible_vals: Option<Vec<&'b str>>,
pub val_names: Option<VecMap<&'b str>>,
pub num_vals: Option<u64>,
pub max_vals: Option<u64>,
pub min_vals: Option<u64>,
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
pub validator: Option<Rc<Fn(String) -> Result<(), String>>>,
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
pub validator_os: Option<Rc<Fn(&OsStr) -> Result<(), OsString>>>,
pub val_delim: Option<char>,
pub default_val: Option<&'b OsStr>,
#[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
pub default_vals_ifs: Option<VecMap<(&'a str, Option<&'b OsStr>, &'b OsStr)>>,
pub env: Option<(&'a OsStr, Option<OsString>)>,
pub terminator: Option<&'b str>,
}
impl<'n, 'e> Default for Valued<'n, 'e> {
fn default() -> Self {
Valued {
possible_vals: None,
num_vals: None,
min_vals: None,
max_vals: None,
val_names: None,
validator: None,
validator_os: None,
val_delim: None,
default_val: None,
default_vals_ifs: None,
env: None,
terminator: None,
}
}
}
impl<'n, 'e> Valued<'n, 'e> {
pub fn fill_in(&mut self) {
if let Some(ref vec) = self.val_names {
if vec.len() > 1 {
self.num_vals = Some(vec.len() as u64);
}
}
}
}
impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Valued<'n, 'e> {
fn from(a: &'z Arg<'n, 'e>) -> Self {
let mut v = a.v.clone();
if let Some(ref vec) = a.v.val_names {
if vec.len() > 1 {
v.num_vals = Some(vec.len() as u64);
}
}
v
}
}

View File

@ -1,274 +0,0 @@
// Std
use std::{
collections::{
hash_map::{Entry, Iter},
HashMap,
},
ffi::OsStr,
mem,
ops::Deref,
};
// Internal
use crate::args::{settings::ArgSettings, AnyArg, ArgMatches, MatchedArg, SubCommand};
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct ArgMatcher<'a>(pub ArgMatches<'a>);
impl<'a> Default for ArgMatcher<'a> {
fn default() -> Self {
ArgMatcher(ArgMatches::default())
}
}
impl<'a> ArgMatcher<'a> {
pub fn new() -> Self {
ArgMatcher::default()
}
pub fn process_arg_overrides<'b>(
&mut self,
a: Option<&AnyArg<'a, 'b>>,
overrides: &mut Vec<(&'b str, &'a str)>,
required: &mut Vec<&'a str>,
check_all: bool,
) {
debugln!(
"ArgMatcher::process_arg_overrides:{:?};",
a.map_or(None, |a| Some(a.name()))
);
if let Some(aa) = a {
let mut self_done = false;
if let Some(a_overrides) = aa.overrides() {
for overr in a_overrides {
debugln!("ArgMatcher::process_arg_overrides:iter:{};", overr);
if overr == &aa.name() {
self_done = true;
self.handle_self_overrides(a);
} else if self.is_present(overr) {
debugln!(
"ArgMatcher::process_arg_overrides:iter:{}: removing from matches;",
overr
);
self.remove(overr);
for i in (0..required.len()).rev() {
if &required[i] == overr {
debugln!(
"ArgMatcher::process_arg_overrides:iter:{}: removing required;",
overr
);
required.swap_remove(i);
break;
}
}
overrides.push((overr, aa.name()));
} else {
overrides.push((overr, aa.name()));
}
}
}
if check_all && !self_done {
self.handle_self_overrides(a);
}
}
}
pub fn handle_self_overrides<'b>(&mut self, a: Option<&AnyArg<'a, 'b>>) {
debugln!(
"ArgMatcher::handle_self_overrides:{:?};",
a.map_or(None, |a| Some(a.name()))
);
if let Some(aa) = a {
if !aa.has_switch() || aa.is_set(ArgSettings::Multiple) {
// positional args can't override self or else we would never advance to the next
// Also flags with --multiple set are ignored otherwise we could never have more
// than one
return;
}
if let Some(ma) = self.get_mut(aa.name()) {
if ma.vals.len() > 1 {
// swap_remove(0) would be O(1) but does not preserve order, which
// we need
ma.vals.remove(0);
ma.occurs = 1;
} else if !aa.takes_value() && ma.occurs > 1 {
ma.occurs = 1;
}
}
}
}
pub fn is_present(&self, name: &str) -> bool {
self.0.is_present(name)
}
pub fn propagate_globals(&mut self, global_arg_vec: &[&'a str]) {
debugln!(
"ArgMatcher::get_global_values: global_arg_vec={:?}",
global_arg_vec
);
let mut vals_map = HashMap::new();
self.fill_in_global_values(global_arg_vec, &mut vals_map);
}
fn fill_in_global_values(
&mut self,
global_arg_vec: &[&'a str],
vals_map: &mut HashMap<&'a str, MatchedArg>,
) {
for global_arg in global_arg_vec {
if let Some(ma) = self.get(global_arg) {
// We have to check if the parent's global arg wasn't used but still exists
// such as from a default value.
//
// For example, `myprog subcommand --global-arg=value` where --global-arg defines
// a default value of `other` myprog would have an existing MatchedArg for
// --global-arg where the value is `other`, however the occurs will be 0.
let to_update = if let Some(parent_ma) = vals_map.get(global_arg) {
if parent_ma.occurs > 0 && ma.occurs == 0 {
parent_ma.clone()
} else {
ma.clone()
}
} else {
ma.clone()
};
vals_map.insert(global_arg, to_update);
}
}
if let Some(ref mut sc) = self.0.subcommand {
let mut am = ArgMatcher(mem::replace(&mut sc.matches, ArgMatches::new()));
am.fill_in_global_values(global_arg_vec, vals_map);
mem::swap(&mut am.0, &mut sc.matches);
}
for (name, matched_arg) in vals_map.iter_mut() {
self.0.args.insert(name, matched_arg.clone());
}
}
pub fn get_mut(&mut self, arg: &str) -> Option<&mut MatchedArg> {
self.0.args.get_mut(arg)
}
pub fn get(&self, arg: &str) -> Option<&MatchedArg> {
self.0.args.get(arg)
}
pub fn remove(&mut self, arg: &str) {
self.0.args.remove(arg);
}
pub fn remove_all(&mut self, args: &[&str]) {
for &arg in args {
self.0.args.remove(arg);
}
}
pub fn insert(&mut self, name: &'a str) {
self.0.args.insert(name, MatchedArg::new());
}
pub fn contains(&self, arg: &str) -> bool {
self.0.args.contains_key(arg)
}
pub fn is_empty(&self) -> bool {
self.0.args.is_empty()
}
pub fn usage(&mut self, usage: String) {
self.0.usage = Some(usage);
}
pub fn arg_names(&'a self) -> Vec<&'a str> {
self.0.args.keys().map(Deref::deref).collect()
}
pub fn entry(&mut self, arg: &'a str) -> Entry<&'a str, MatchedArg> {
self.0.args.entry(arg)
}
pub fn subcommand(&mut self, sc: SubCommand<'a>) {
self.0.subcommand = Some(Box::new(sc));
}
pub fn subcommand_name(&self) -> Option<&str> {
self.0.subcommand_name()
}
pub fn iter(&self) -> Iter<&str, MatchedArg> {
self.0.args.iter()
}
pub fn inc_occurrence_of(&mut self, arg: &'a str) {
debugln!("ArgMatcher::inc_occurrence_of: arg={}", arg);
if let Some(a) = self.get_mut(arg) {
a.occurs += 1;
return;
}
debugln!("ArgMatcher::inc_occurrence_of: first instance");
self.insert(arg);
}
pub fn inc_occurrences_of(&mut self, args: &[&'a str]) {
debugln!("ArgMatcher::inc_occurrences_of: args={:?}", args);
for arg in args {
self.inc_occurrence_of(arg);
}
}
pub fn add_val_to(&mut self, arg: &'a str, val: &OsStr) {
let ma = self.entry(arg).or_insert(MatchedArg {
occurs: 0,
indices: Vec::with_capacity(1),
vals: Vec::with_capacity(1),
});
ma.vals.push(val.to_owned());
}
pub fn add_index_to(&mut self, arg: &'a str, idx: usize) {
let ma = self.entry(arg).or_insert(MatchedArg {
occurs: 0,
indices: Vec::with_capacity(1),
vals: Vec::new(),
});
ma.indices.push(idx);
}
pub fn needs_more_vals<'b, A>(&self, o: &A) -> bool
where
A: AnyArg<'a, 'b>,
{
debugln!("ArgMatcher::needs_more_vals: o={}", o.name());
if let Some(ma) = self.get(o.name()) {
if let Some(num) = o.num_vals() {
debugln!("ArgMatcher::needs_more_vals: num_vals...{}", num);
return if o.is_set(ArgSettings::Multiple) {
((ma.vals.len() as u64) % num) != 0
} else {
num != (ma.vals.len() as u64)
};
} else if let Some(num) = o.max_vals() {
debugln!("ArgMatcher::needs_more_vals: max_vals...{}", num);
return (ma.vals.len() as u64) <= num;
} else if o.min_vals().is_some() {
debugln!("ArgMatcher::needs_more_vals: min_vals...true");
return true;
}
return o.is_set(ArgSettings::Multiple);
}
true
}
}
// Not changing to From just to not deal with possible breaking changes on v2 since v3 is coming
// in the future anyways
#[cfg_attr(feature = "cargo-clippy", allow(clippy::from_over_into))]
impl<'a> Into<ArgMatches<'a>> for ArgMatcher<'a> {
fn into(self) -> ArgMatches<'a> {
self.0
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +0,0 @@
// Std
use std::ffi::OsString;
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct MatchedArg {
#[doc(hidden)]
pub occurs: u64,
#[doc(hidden)]
pub indices: Vec<usize>,
#[doc(hidden)]
pub vals: Vec<OsString>,
}
impl Default for MatchedArg {
fn default() -> Self {
MatchedArg {
occurs: 1,
indices: Vec::new(),
vals: Vec::new(),
}
}
}
impl MatchedArg {
pub fn new() -> Self {
MatchedArg::default()
}
}

View File

@ -1,21 +0,0 @@
pub use self::any_arg::{AnyArg, DispOrder};
pub use self::arg::Arg;
pub use self::arg_builder::{Base, FlagBuilder, OptBuilder, PosBuilder, Switched, Valued};
pub use self::arg_matcher::ArgMatcher;
pub use self::arg_matches::{ArgMatches, OsValues, Values};
pub use self::group::ArgGroup;
pub use self::matched_arg::MatchedArg;
pub use self::settings::{ArgFlags, ArgSettings};
pub use self::subcommand::SubCommand;
#[macro_use]
mod macros;
pub mod any_arg;
mod arg;
mod arg_builder;
mod arg_matcher;
mod arg_matches;
mod group;
mod matched_arg;
pub mod settings;
mod subcommand;

View File

@ -1,237 +0,0 @@
// Std
#[allow(deprecated, unused_imports)]
use std::ascii::AsciiExt;
use std::str::FromStr;
bitflags! {
struct Flags: u32 {
const REQUIRED = 1;
const MULTIPLE = 1 << 1;
const EMPTY_VALS = 1 << 2;
const GLOBAL = 1 << 3;
const HIDDEN = 1 << 4;
const TAKES_VAL = 1 << 5;
const USE_DELIM = 1 << 6;
const NEXT_LINE_HELP = 1 << 7;
const R_UNLESS_ALL = 1 << 8;
const REQ_DELIM = 1 << 9;
const DELIM_NOT_SET = 1 << 10;
const HIDE_POS_VALS = 1 << 11;
const ALLOW_TAC_VALS = 1 << 12;
const REQUIRE_EQUALS = 1 << 13;
const LAST = 1 << 14;
const HIDE_DEFAULT_VAL = 1 << 15;
const CASE_INSENSITIVE = 1 << 16;
const HIDE_ENV_VALS = 1 << 17;
const HIDDEN_SHORT_H = 1 << 18;
const HIDDEN_LONG_H = 1 << 19;
}
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct ArgFlags(Flags);
impl ArgFlags {
pub fn new() -> Self {
ArgFlags::default()
}
impl_settings! {ArgSettings,
Required => Flags::REQUIRED,
Multiple => Flags::MULTIPLE,
EmptyValues => Flags::EMPTY_VALS,
Global => Flags::GLOBAL,
Hidden => Flags::HIDDEN,
TakesValue => Flags::TAKES_VAL,
UseValueDelimiter => Flags::USE_DELIM,
NextLineHelp => Flags::NEXT_LINE_HELP,
RequiredUnlessAll => Flags::R_UNLESS_ALL,
RequireDelimiter => Flags::REQ_DELIM,
ValueDelimiterNotSet => Flags::DELIM_NOT_SET,
HidePossibleValues => Flags::HIDE_POS_VALS,
AllowLeadingHyphen => Flags::ALLOW_TAC_VALS,
RequireEquals => Flags::REQUIRE_EQUALS,
Last => Flags::LAST,
CaseInsensitive => Flags::CASE_INSENSITIVE,
HideEnvValues => Flags::HIDE_ENV_VALS,
HideDefaultValue => Flags::HIDE_DEFAULT_VAL,
HiddenShortHelp => Flags::HIDDEN_SHORT_H,
HiddenLongHelp => Flags::HIDDEN_LONG_H
}
}
impl Default for ArgFlags {
fn default() -> Self {
ArgFlags(Flags::EMPTY_VALS | Flags::DELIM_NOT_SET)
}
}
/// Various settings that apply to arguments and may be set, unset, and checked via getter/setter
/// methods [`Arg::set`], [`Arg::unset`], and [`Arg::is_set`]
///
/// [`Arg::set`]: ./struct.Arg.html#method.set
/// [`Arg::unset`]: ./struct.Arg.html#method.unset
/// [`Arg::is_set`]: ./struct.Arg.html#method.is_set
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum ArgSettings {
/// The argument must be used
Required,
/// The argument may be used multiple times such as `--flag --flag`
Multiple,
/// The argument allows empty values such as `--option ""`
EmptyValues,
/// The argument should be propagated down through all child [`SubCommand`]s
///
/// [`SubCommand`]: ./struct.SubCommand.html
Global,
/// The argument should **not** be shown in help text
Hidden,
/// The argument accepts a value, such as `--option <value>`
TakesValue,
/// Determines if the argument allows values to be grouped via a delimiter
UseValueDelimiter,
/// Prints the help text on the line after the argument
NextLineHelp,
/// Requires the use of a value delimiter for all multiple values
RequireDelimiter,
/// Hides the possible values from the help string
HidePossibleValues,
/// Allows vals that start with a '-'
AllowLeadingHyphen,
/// Require options use `--option=val` syntax
RequireEquals,
/// Specifies that the arg is the last positional argument and may be accessed early via `--`
/// syntax
Last,
/// Hides the default value from the help string
HideDefaultValue,
/// Makes `Arg::possible_values` case insensitive
CaseInsensitive,
/// Hides ENV values in the help message
HideEnvValues,
/// The argument should **not** be shown in short help text
HiddenShortHelp,
/// The argument should **not** be shown in long help text
HiddenLongHelp,
#[doc(hidden)]
RequiredUnlessAll,
#[doc(hidden)]
ValueDelimiterNotSet,
}
impl FromStr for ArgSettings {
type Err = String;
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
match &*s.to_ascii_lowercase() {
"required" => Ok(ArgSettings::Required),
"multiple" => Ok(ArgSettings::Multiple),
"global" => Ok(ArgSettings::Global),
"emptyvalues" => Ok(ArgSettings::EmptyValues),
"hidden" => Ok(ArgSettings::Hidden),
"takesvalue" => Ok(ArgSettings::TakesValue),
"usevaluedelimiter" => Ok(ArgSettings::UseValueDelimiter),
"nextlinehelp" => Ok(ArgSettings::NextLineHelp),
"requiredunlessall" => Ok(ArgSettings::RequiredUnlessAll),
"requiredelimiter" => Ok(ArgSettings::RequireDelimiter),
"valuedelimiternotset" => Ok(ArgSettings::ValueDelimiterNotSet),
"hidepossiblevalues" => Ok(ArgSettings::HidePossibleValues),
"allowleadinghyphen" => Ok(ArgSettings::AllowLeadingHyphen),
"requireequals" => Ok(ArgSettings::RequireEquals),
"last" => Ok(ArgSettings::Last),
"hidedefaultvalue" => Ok(ArgSettings::HideDefaultValue),
"caseinsensitive" => Ok(ArgSettings::CaseInsensitive),
"hideenvvalues" => Ok(ArgSettings::HideEnvValues),
"hiddenshorthelp" => Ok(ArgSettings::HiddenShortHelp),
"hiddenlonghelp" => Ok(ArgSettings::HiddenLongHelp),
_ => Err("unknown ArgSetting, cannot convert from str".to_owned()),
}
}
}
#[cfg(test)]
mod test {
use super::ArgSettings;
#[test]
fn arg_settings_fromstr() {
assert_eq!(
"allowleadinghyphen".parse::<ArgSettings>().unwrap(),
ArgSettings::AllowLeadingHyphen
);
assert_eq!(
"emptyvalues".parse::<ArgSettings>().unwrap(),
ArgSettings::EmptyValues
);
assert_eq!(
"global".parse::<ArgSettings>().unwrap(),
ArgSettings::Global
);
assert_eq!(
"hidepossiblevalues".parse::<ArgSettings>().unwrap(),
ArgSettings::HidePossibleValues
);
assert_eq!(
"hidden".parse::<ArgSettings>().unwrap(),
ArgSettings::Hidden
);
assert_eq!(
"multiple".parse::<ArgSettings>().unwrap(),
ArgSettings::Multiple
);
assert_eq!(
"nextlinehelp".parse::<ArgSettings>().unwrap(),
ArgSettings::NextLineHelp
);
assert_eq!(
"requiredunlessall".parse::<ArgSettings>().unwrap(),
ArgSettings::RequiredUnlessAll
);
assert_eq!(
"requiredelimiter".parse::<ArgSettings>().unwrap(),
ArgSettings::RequireDelimiter
);
assert_eq!(
"required".parse::<ArgSettings>().unwrap(),
ArgSettings::Required
);
assert_eq!(
"takesvalue".parse::<ArgSettings>().unwrap(),
ArgSettings::TakesValue
);
assert_eq!(
"usevaluedelimiter".parse::<ArgSettings>().unwrap(),
ArgSettings::UseValueDelimiter
);
assert_eq!(
"valuedelimiternotset".parse::<ArgSettings>().unwrap(),
ArgSettings::ValueDelimiterNotSet
);
assert_eq!(
"requireequals".parse::<ArgSettings>().unwrap(),
ArgSettings::RequireEquals
);
assert_eq!("last".parse::<ArgSettings>().unwrap(), ArgSettings::Last);
assert_eq!(
"hidedefaultvalue".parse::<ArgSettings>().unwrap(),
ArgSettings::HideDefaultValue
);
assert_eq!(
"caseinsensitive".parse::<ArgSettings>().unwrap(),
ArgSettings::CaseInsensitive
);
assert_eq!(
"hideenvvalues".parse::<ArgSettings>().unwrap(),
ArgSettings::HideEnvValues
);
assert_eq!(
"hiddenshorthelp".parse::<ArgSettings>().unwrap(),
ArgSettings::HiddenShortHelp
);
assert_eq!(
"hiddenlonghelp".parse::<ArgSettings>().unwrap(),
ArgSettings::HiddenLongHelp
);
assert!("hahahaha".parse::<ArgSettings>().is_err());
}
}

View File

@ -1,71 +0,0 @@
// Third Party
#[cfg(feature = "yaml")]
use yaml_rust::Yaml;
// Internal
use crate::{App, ArgMatches};
/// The abstract representation of a command line subcommand.
///
/// This struct describes all the valid options of the subcommand for the program. Subcommands are
/// essentially "sub-[`App`]s" and contain all the same possibilities (such as their own
/// [arguments], subcommands, and settings).
///
/// # Examples
///
/// ```rust
/// # use clap::{App, Arg, SubCommand};
/// App::new("myprog")
/// .subcommand(
/// SubCommand::with_name("config")
/// .about("Used for configuration")
/// .arg(Arg::with_name("config_file")
/// .help("The configuration file to use")
/// .index(1)))
/// # ;
/// ```
/// [`App`]: ./struct.App.html
/// [arguments]: ./struct.Arg.html
#[derive(Debug, Clone)]
pub struct SubCommand<'a> {
#[doc(hidden)]
pub name: String,
#[doc(hidden)]
pub matches: ArgMatches<'a>,
}
impl<'a> SubCommand<'a> {
/// Creates a new instance of a subcommand requiring a name. The name will be displayed
/// to the user when they print version or help and usage information.
///
/// # Examples
///
/// ```rust
/// # use clap::{App, Arg, SubCommand};
/// App::new("myprog")
/// .subcommand(
/// SubCommand::with_name("config"))
/// # ;
/// ```
pub fn with_name<'b>(name: &str) -> App<'a, 'b> {
App::new(name)
}
/// Creates a new instance of a subcommand from a YAML (.yml) document
///
/// # Examples
///
/// ```ignore
/// # #[macro_use]
/// # extern crate clap;
/// # use clap::Subcommand;
/// # fn main() {
/// let sc_yaml = load_yaml!("test_subcommand.yml");
/// let sc = SubCommand::from_yaml(sc_yaml);
/// # }
/// ```
#[cfg(feature = "yaml")]
pub fn from_yaml(yaml: &Yaml) -> App {
App::from_yaml(yaml)
}
}

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