зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 3 changesets (bug 1836415) for causing wd failures on allow_hosts.py.
Backed out changeset dbbb407831c0 (bug 1836415) Backed out changeset a1fbb5a4179b (bug 1836415) Backed out changeset e2d49a57b1c9 (bug 1836415)
This commit is contained in:
Родитель
3c70421790
Коммит
9ed9b5d1c8
|
@ -732,45 +732,41 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.1.14"
|
||||
version = "3.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "906f7fe1da4185b7a282b2bc90172a496f9def1aca4545fe7526810741591e14"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "351f9ad9688141ed83dfd8f5fb998a06225ef444b48ff4dc43de6d409b7fd10b"
|
||||
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"once_cell",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"strsim",
|
||||
"terminal_size",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.1.14"
|
||||
version = "3.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81d7dc0031c3a59a04fc2ba395c8e2dd463cba1859275f065d225f6122221b45"
|
||||
checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 1.0.107",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.4.1"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmake"
|
||||
|
@ -3842,6 +3838,12 @@ dependencies = [
|
|||
"origin-trial-token",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "osclientcerts-static"
|
||||
version = "0.1.4"
|
||||
|
@ -4114,6 +4116,30 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.107",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.20+deprecated"
|
||||
|
@ -5111,7 +5137,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.2.999"
|
||||
version = "0.1.999"
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d"
|
||||
dependencies = [
|
||||
"terminal_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thin-vec"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "terminal_size"
|
||||
version = "0.2.999"
|
||||
version = "0.1.999"
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
|
|
|
@ -2760,11 +2760,6 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
|||
criteria = "safe-to-deploy"
|
||||
delta = "0.15.0 -> 0.15.2"
|
||||
|
||||
[[audits.textwrap]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.15.2 -> 0.16.0"
|
||||
|
||||
[[audits.thin-vec]]
|
||||
who = "Aria Beingessner <a.beingessner@gmail.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -3693,30 +3688,6 @@ user-id = 6741 # Alice Ryhl (Darksonn)
|
|||
start = "2021-01-11"
|
||||
end = "2024-05-05"
|
||||
|
||||
[[trusted.clap]]
|
||||
criteria = "safe-to-deploy"
|
||||
user-id = 6743 # Ed Page (epage)
|
||||
start = "2021-12-08"
|
||||
end = "2024-06-02"
|
||||
|
||||
[[trusted.clap_builder]]
|
||||
criteria = "safe-to-deploy"
|
||||
user-id = 6743 # Ed Page (epage)
|
||||
start = "2023-03-28"
|
||||
end = "2024-06-02"
|
||||
|
||||
[[trusted.clap_derive]]
|
||||
criteria = "safe-to-deploy"
|
||||
user-id = 6743 # Ed Page (epage)
|
||||
start = "2021-12-08"
|
||||
end = "2024-06-02"
|
||||
|
||||
[[trusted.clap_lex]]
|
||||
criteria = "safe-to-deploy"
|
||||
user-id = 6743 # Ed Page (epage)
|
||||
start = "2022-04-15"
|
||||
end = "2024-06-02"
|
||||
|
||||
[[trusted.dtoa]]
|
||||
criteria = "safe-to-deploy"
|
||||
user-id = 3618 # David Tolnay (dtolnay)
|
||||
|
|
|
@ -285,6 +285,18 @@ criteria = "safe-to-deploy"
|
|||
version = "1.3.3"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.clap]]
|
||||
version = "3.1.18"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.clap_derive]]
|
||||
version = "3.1.18"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.clap_lex]]
|
||||
version = "0.2.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.cookie]]
|
||||
version = "0.16.0"
|
||||
criteria = "safe-to-run"
|
||||
|
@ -625,6 +637,10 @@ criteria = "safe-to-deploy"
|
|||
version = "1.12.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.os_str_bytes]]
|
||||
version = "6.1.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.owning_ref]]
|
||||
version = "0.4.1"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -669,6 +685,10 @@ criteria = "safe-to-run"
|
|||
version = "0.2.16"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.proc-macro-error]]
|
||||
version = "1.0.4"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.profiling]]
|
||||
version = "1.0.6"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -801,6 +821,10 @@ criteria = "safe-to-deploy"
|
|||
version = "3.3.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.textwrap]]
|
||||
version = "0.15.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.time]]
|
||||
version = "0.1.44"
|
||||
criteria = "safe-to-deploy"
|
||||
|
|
|
@ -71,34 +71,6 @@ user-id = 3788
|
|||
user-login = "emilio"
|
||||
user-name = "Emilio Cobos Álvarez"
|
||||
|
||||
[[publisher.clap]]
|
||||
version = "4.1.14"
|
||||
when = "2023-03-28"
|
||||
user-id = 6743
|
||||
user-login = "epage"
|
||||
user-name = "Ed Page"
|
||||
|
||||
[[publisher.clap_builder]]
|
||||
version = "4.1.14"
|
||||
when = "2023-03-28"
|
||||
user-id = 6743
|
||||
user-login = "epage"
|
||||
user-name = "Ed Page"
|
||||
|
||||
[[publisher.clap_derive]]
|
||||
version = "4.1.14"
|
||||
when = "2023-03-28"
|
||||
user-id = 6743
|
||||
user-login = "epage"
|
||||
user-name = "Ed Page"
|
||||
|
||||
[[publisher.clap_lex]]
|
||||
version = "0.4.1"
|
||||
when = "2023-03-28"
|
||||
user-id = 6743
|
||||
user-login = "epage"
|
||||
user-name = "Ed Page"
|
||||
|
||||
[[publisher.core-foundation]]
|
||||
version = "0.9.3"
|
||||
when = "2022-02-07"
|
||||
|
@ -958,6 +930,12 @@ criteria = "safe-to-run"
|
|||
version = "1.0.12"
|
||||
aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
|
||||
|
||||
[[audits.google.audits.proc-macro-error-attr]]
|
||||
who = "George Burgess IV <gbiv@google.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.0.4"
|
||||
aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
|
||||
|
||||
[[audits.google.audits.scoped-tls]]
|
||||
who = "George Burgess IV <gbiv@google.com>"
|
||||
criteria = "safe-to-run"
|
||||
|
|
|
@ -23,7 +23,7 @@ repository = "https://hg.mozilla.org/mozilla-central/file/tip/testing/geckodrive
|
|||
[dependencies]
|
||||
base64 = "0.21"
|
||||
chrono = "0.4.6"
|
||||
clap = { version = "4", default-features = false, features = ["cargo", "std", "suggestions", "wrap_help", "string"] }
|
||||
clap = { version = "~3.1", default-features = false, features = ["cargo", "std", "suggestions", "wrap_help"] }
|
||||
hyper = "0.14"
|
||||
lazy_static = "1.0"
|
||||
log = { version = "0.4", features = ["std"] }
|
||||
|
|
|
@ -34,7 +34,7 @@ use std::path::PathBuf;
|
|||
use std::result;
|
||||
use std::str::FromStr;
|
||||
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use clap::{AppSettings, Arg, Command};
|
||||
|
||||
macro_rules! try_opt {
|
||||
($expr:expr, $err_type:expr, $err_msg:expr) => {{
|
||||
|
@ -190,7 +190,7 @@ fn parse_hostname(webdriver_host: &str) -> Result<Host, url::ParseError> {
|
|||
///
|
||||
/// This only covers domain names, not IP addresses, since IP adresses
|
||||
/// are always accepted.
|
||||
fn get_default_allowed_hosts(ip: IpAddr) -> Vec<Host> {
|
||||
fn get_default_allowed_hosts(ip: IpAddr) -> Vec<Result<Host, url::ParseError>> {
|
||||
let localhost_is_loopback = ("localhost".to_string(), 80)
|
||||
.to_socket_addrs()
|
||||
.map(|addr_iter| {
|
||||
|
@ -202,50 +202,61 @@ fn get_default_allowed_hosts(ip: IpAddr) -> Vec<Host> {
|
|||
.len()
|
||||
> 0;
|
||||
if ip.is_loopback() && localhost_is_loopback {
|
||||
vec![Host::parse("localhost").unwrap()]
|
||||
vec![Host::parse("localhost")]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
fn get_allowed_hosts(host: Host, allow_hosts: Option<clap::parser::ValuesRef<Host>>) -> Vec<Host> {
|
||||
fn get_allowed_hosts(
|
||||
host: Host,
|
||||
allow_hosts: Option<clap::Values>,
|
||||
) -> Result<Vec<Host>, url::ParseError> {
|
||||
allow_hosts
|
||||
.map(|hosts| hosts.cloned().collect())
|
||||
.map(|hosts| hosts.map(Host::parse).collect::<Vec<_>>())
|
||||
.unwrap_or_else(|| match host {
|
||||
Host::Domain(_) => {
|
||||
vec![host.clone()]
|
||||
vec![Ok(host.clone())]
|
||||
}
|
||||
Host::Ipv4(ip) => get_default_allowed_hosts(IpAddr::V4(ip)),
|
||||
Host::Ipv6(ip) => get_default_allowed_hosts(IpAddr::V6(ip)),
|
||||
})
|
||||
.into_iter()
|
||||
.collect::<Result<Vec<Host>, url::ParseError>>()
|
||||
}
|
||||
|
||||
fn get_allowed_origins(allow_origins: Option<clap::parser::ValuesRef<Url>>) -> Vec<Url> {
|
||||
allow_origins.into_iter().flatten().cloned().collect()
|
||||
fn get_allowed_origins(allow_origins: Option<clap::Values>) -> Result<Vec<Url>, url::ParseError> {
|
||||
allow_origins
|
||||
.map(|origins| {
|
||||
origins
|
||||
.map(Url::parse)
|
||||
.collect::<Result<Vec<Url>, url::ParseError>>()
|
||||
})
|
||||
.unwrap_or_else(|| Ok(vec![]))
|
||||
}
|
||||
|
||||
fn parse_args(cmd: &mut Command) -> ProgramResult<Operation> {
|
||||
let args = cmd.try_get_matches_from_mut(env::args())?;
|
||||
|
||||
if args.get_flag("help") {
|
||||
if args.is_present("help") {
|
||||
return Ok(Operation::Help);
|
||||
} else if args.get_flag("version") {
|
||||
} else if args.is_present("version") {
|
||||
return Ok(Operation::Version);
|
||||
}
|
||||
|
||||
let log_level = if let Some(log_level) = args.get_one::<String>("log_level") {
|
||||
Level::from_str(log_level).ok()
|
||||
let log_level = if args.is_present("log_level") {
|
||||
Level::from_str(args.value_of("log_level").unwrap()).ok()
|
||||
} else {
|
||||
Some(match args.get_count("verbosity") {
|
||||
Some(match args.occurrences_of("verbosity") {
|
||||
0 => Level::Info,
|
||||
1 => Level::Debug,
|
||||
_ => Level::Trace,
|
||||
})
|
||||
};
|
||||
|
||||
let webdriver_host = args.get_one::<String>("webdriver_host").unwrap();
|
||||
let webdriver_host = args.value_of("webdriver_host").unwrap();
|
||||
let webdriver_port = {
|
||||
let s = args.get_one::<String>("webdriver_port").unwrap();
|
||||
let s = args.value_of("webdriver_port").unwrap();
|
||||
match u16::from_str(s) {
|
||||
Ok(n) => n,
|
||||
Err(e) => usage!("invalid --port: {}: {}", e, s),
|
||||
|
@ -253,13 +264,12 @@ fn parse_args(cmd: &mut Command) -> ProgramResult<Operation> {
|
|||
};
|
||||
|
||||
let android_storage = args
|
||||
.get_one::<String>("android_storage")
|
||||
.and_then(|arg| AndroidStorageInput::from_str(arg).ok())
|
||||
.value_of_t::<AndroidStorageInput>("android_storage")
|
||||
.unwrap_or(AndroidStorageInput::Auto);
|
||||
|
||||
let binary = args.get_one::<String>("binary").map(PathBuf::from);
|
||||
let binary = args.value_of("binary").map(PathBuf::from);
|
||||
|
||||
let profile_root = args.get_one::<String>("profile_root").map(PathBuf::from);
|
||||
let profile_root = args.value_of("profile_root").map(PathBuf::from);
|
||||
|
||||
// Try to create a temporary directory on startup to check that the directory exists and is writable
|
||||
{
|
||||
|
@ -273,8 +283,8 @@ fn parse_args(cmd: &mut Command) -> ProgramResult<Operation> {
|
|||
}
|
||||
}
|
||||
|
||||
let marionette_host = args.get_one::<String>("marionette_host").unwrap();
|
||||
let marionette_port = match args.get_one::<String>("marionette_port") {
|
||||
let marionette_host = args.value_of("marionette_host").unwrap();
|
||||
let marionette_port = match args.value_of("marionette_port") {
|
||||
Some(s) => match u16::from_str(s) {
|
||||
Ok(n) => Some(n),
|
||||
Err(e) => usage!("invalid --marionette-port: {}", e),
|
||||
|
@ -284,7 +294,7 @@ fn parse_args(cmd: &mut Command) -> ProgramResult<Operation> {
|
|||
|
||||
// For Android the port on the device must be the same as the one on the
|
||||
// host. For now default to 9222, which is the default for --remote-debugging-port.
|
||||
let websocket_port = match args.get_one::<String>("websocket_port") {
|
||||
let websocket_port = match args.value_of("websocket_port") {
|
||||
Some(s) => match u16::from_str(s) {
|
||||
Ok(n) => n,
|
||||
Err(e) => usage!("invalid --websocket-port: {}", e),
|
||||
|
@ -297,32 +307,38 @@ fn parse_args(cmd: &mut Command) -> ProgramResult<Operation> {
|
|||
Err(e) => usage!("invalid --host {}: {}", webdriver_host, e),
|
||||
};
|
||||
|
||||
let allow_hosts = get_allowed_hosts(host, args.get_many("allow_hosts"));
|
||||
let allow_hosts = match get_allowed_hosts(host, args.values_of("allow_hosts")) {
|
||||
Ok(hosts) => hosts,
|
||||
Err(e) => usage!("invalid --allow-hosts {}", e),
|
||||
};
|
||||
|
||||
let allow_origins = get_allowed_origins(args.get_many("allow_origins"));
|
||||
let allow_origins = match get_allowed_origins(args.values_of("allow_origins")) {
|
||||
Ok(origins) => origins,
|
||||
Err(e) => usage!("invalid --allow-origins {}", e),
|
||||
};
|
||||
|
||||
let address = server_address(webdriver_host, webdriver_port)?;
|
||||
|
||||
let settings = MarionetteSettings {
|
||||
binary,
|
||||
profile_root,
|
||||
connect_existing: args.get_flag("connect_existing"),
|
||||
connect_existing: args.is_present("connect_existing"),
|
||||
host: marionette_host.into(),
|
||||
port: marionette_port,
|
||||
websocket_port,
|
||||
allow_hosts: allow_hosts.clone(),
|
||||
allow_origins: allow_origins.clone(),
|
||||
jsdebugger: args.get_flag("jsdebugger"),
|
||||
jsdebugger: args.is_present("jsdebugger"),
|
||||
android_storage,
|
||||
};
|
||||
Ok(Operation::Server {
|
||||
log_level,
|
||||
log_truncate: !args.get_flag("log_no_truncate"),
|
||||
log_truncate: !args.is_present("log_no_truncate"),
|
||||
allow_hosts,
|
||||
allow_origins,
|
||||
address,
|
||||
settings,
|
||||
deprecated_storage_arg: args.contains_id("android_storage"),
|
||||
deprecated_storage_arg: args.is_present("android_storage"),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -385,86 +401,40 @@ fn main() {
|
|||
});
|
||||
}
|
||||
|
||||
fn make_command() -> Command {
|
||||
fn make_command<'a>() -> Command<'a> {
|
||||
Command::new(format!("geckodriver {}", build::build_info()))
|
||||
.disable_help_flag(true)
|
||||
.disable_version_flag(true)
|
||||
.setting(AppSettings::NoAutoHelp)
|
||||
.setting(AppSettings::NoAutoVersion)
|
||||
.about("WebDriver implementation for Firefox")
|
||||
.arg(
|
||||
Arg::new("allow_hosts")
|
||||
.long("allow-hosts")
|
||||
.num_args(1..)
|
||||
.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::new("allow_origins")
|
||||
.long("allow-origins")
|
||||
.num_args(1..)
|
||||
.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."),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("android_storage")
|
||||
.long("android-storage")
|
||||
.value_parser(["auto", "app", "internal", "sdcard"])
|
||||
.value_name("ANDROID_STORAGE")
|
||||
.help("Selects storage location to be used for test data (deprecated)."),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("binary")
|
||||
.short('b')
|
||||
.long("binary")
|
||||
.num_args(1)
|
||||
.value_name("BINARY")
|
||||
.help("Path to the Firefox binary"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("connect_existing")
|
||||
.long("connect-existing")
|
||||
.requires("marionette_port")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Connect to an existing Firefox instance"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("help")
|
||||
.short('h')
|
||||
.long("help")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Prints this message"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("webdriver_host")
|
||||
.long("host")
|
||||
.num_args(1)
|
||||
.takes_value(true)
|
||||
.value_name("HOST")
|
||||
.default_value("127.0.0.1")
|
||||
.help("Host IP to use for WebDriver server"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("jsdebugger")
|
||||
.long("jsdebugger")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Attach browser toolbox debugger for Firefox"),
|
||||
Arg::new("webdriver_port")
|
||||
.short('p')
|
||||
.long("port")
|
||||
.takes_value(true)
|
||||
.value_name("PORT")
|
||||
.default_value("4444")
|
||||
.help("Port to use for WebDriver server"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("log_level")
|
||||
.long("log")
|
||||
.num_args(1)
|
||||
.value_name("LEVEL")
|
||||
.value_parser(["fatal", "error", "warn", "info", "config", "debug", "trace"])
|
||||
.help("Set Gecko log level"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("log_no_truncate")
|
||||
.long("log-no-truncate")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Disable truncation of long log lines"),
|
||||
Arg::new("binary")
|
||||
.short('b')
|
||||
.long("binary")
|
||||
.takes_value(true)
|
||||
.value_name("BINARY")
|
||||
.help("Path to the Firefox binary"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("marionette_host")
|
||||
.long("marionette-host")
|
||||
.num_args(1)
|
||||
.takes_value(true)
|
||||
.value_name("HOST")
|
||||
.default_value("127.0.0.1")
|
||||
.help("Host to use to connect to Gecko"),
|
||||
|
@ -472,47 +442,90 @@ fn make_command() -> Command {
|
|||
.arg(
|
||||
Arg::new("marionette_port")
|
||||
.long("marionette-port")
|
||||
.num_args(1)
|
||||
.takes_value(true)
|
||||
.value_name("PORT")
|
||||
.help("Port to use to connect to Gecko [default: system-allocated port]"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("webdriver_port")
|
||||
.short('p')
|
||||
.long("port")
|
||||
.num_args(1)
|
||||
Arg::new("websocket_port")
|
||||
.long("websocket-port")
|
||||
.takes_value(true)
|
||||
.value_name("PORT")
|
||||
.default_value("4444")
|
||||
.help("Port to use for WebDriver server"),
|
||||
.conflicts_with("connect_existing")
|
||||
.help("Port to use to connect to WebDriver BiDi [default: 9222]"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("profile_root")
|
||||
.long("profile-root")
|
||||
.num_args(1)
|
||||
.value_name("PROFILE_ROOT")
|
||||
.help("Directory in which to create profiles. Defaults to the system temporary directory."),
|
||||
Arg::new("connect_existing")
|
||||
.long("connect-existing")
|
||||
.requires("marionette_port")
|
||||
.help("Connect to an existing Firefox instance"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("jsdebugger")
|
||||
.long("jsdebugger")
|
||||
.help("Attach browser toolbox debugger for Firefox"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("verbosity")
|
||||
.multiple_occurrences(true)
|
||||
.conflicts_with("log_level")
|
||||
.short('v')
|
||||
.action(ArgAction::Count)
|
||||
.help("Log level verbosity (-v for debug and -vv for trace level)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("log_level")
|
||||
.long("log")
|
||||
.takes_value(true)
|
||||
.value_name("LEVEL")
|
||||
.possible_values(["fatal", "error", "warn", "info", "config", "debug", "trace"])
|
||||
.help("Set Gecko log level"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("log_no_truncate")
|
||||
.long("log-no-truncate")
|
||||
.help("Disable truncation of long log lines"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("help")
|
||||
.short('h')
|
||||
.long("help")
|
||||
.help("Prints this message"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("version")
|
||||
.short('V')
|
||||
.long("version")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Prints version and copying information"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("websocket_port")
|
||||
.long("websocket-port")
|
||||
.num_args(1)
|
||||
.value_name("PORT")
|
||||
.conflicts_with("connect_existing")
|
||||
.help("Port to use to connect to WebDriver BiDi [default: 9222]"),
|
||||
Arg::new("profile_root")
|
||||
.long("profile-root")
|
||||
.takes_value(true)
|
||||
.value_name("PROFILE_ROOT")
|
||||
.help("Directory in which to create profiles. Defaults to the system temporary directory."),
|
||||
)
|
||||
.arg(
|
||||
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::new("allow_hosts")
|
||||
.long("allow-hosts")
|
||||
.takes_value(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::new("allow_origins")
|
||||
.long("allow-origins")
|
||||
.takes_value(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."),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -10,10 +10,9 @@
|
|||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
rust-version = "1.64.0"
|
||||
edition = "2018"
|
||||
name = "clap"
|
||||
version = "4.1.14"
|
||||
version = "3.1.18"
|
||||
include = [
|
||||
"build.rs",
|
||||
"src/**/*",
|
||||
|
@ -24,6 +23,7 @@ include = [
|
|||
"examples/**/*",
|
||||
]
|
||||
description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
|
||||
documentation = "https://docs.rs/clap/"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"argument",
|
||||
|
@ -44,7 +44,7 @@ rustdoc-args = [
|
|||
]
|
||||
cargo-args = [
|
||||
"-Zunstable-options",
|
||||
"-Zrustdoc-scrape-examples",
|
||||
"-Zrustdoc-scrape-examples=examples",
|
||||
]
|
||||
|
||||
[package.metadata.playground]
|
||||
|
@ -90,14 +90,25 @@ replace = """
|
|||
exactly = 1
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
file = "CITATION.cff"
|
||||
search = "^date-released: ....-..-.."
|
||||
replace = "date-released: {{date}}"
|
||||
file = "README.md"
|
||||
search = "github.com/clap-rs/clap/blob/[^/]+/"
|
||||
replace = "github.com/clap-rs/clap/blob/{{tag_name}}/"
|
||||
exactly = 13
|
||||
prerelease = true
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
file = "CITATION.cff"
|
||||
search = '^version: .+\..+\..+'
|
||||
replace = "version: {{version}}"
|
||||
file = "README.md"
|
||||
search = 'version = "[a-z0-9\.-]+"'
|
||||
replace = "version = \"{{version}}\""
|
||||
exactly = 1
|
||||
prerelease = true
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
file = "src/derive.rs"
|
||||
search = "github.com/clap-rs/clap/blob/[^/]+/"
|
||||
replace = "github.com/clap-rs/clap/blob/{{tag_name}}/"
|
||||
exactly = 4
|
||||
prerelease = true
|
||||
|
||||
[profile.bench]
|
||||
lto = true
|
||||
|
@ -129,10 +140,6 @@ required-features = ["cargo"]
|
|||
name = "escaped-positional-derive"
|
||||
required-features = ["derive"]
|
||||
|
||||
[[example]]
|
||||
name = "find"
|
||||
required-features = ["cargo"]
|
||||
|
||||
[[example]]
|
||||
name = "git-derive"
|
||||
required-features = ["derive"]
|
||||
|
@ -144,15 +151,17 @@ 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 = "repl"
|
||||
path = "examples/repl.rs"
|
||||
required-features = ["help"]
|
||||
required-features = ["unstable-multicall"]
|
||||
|
||||
[[example]]
|
||||
name = "01_quick"
|
||||
|
@ -188,21 +197,11 @@ name = "03_02_option"
|
|||
path = "examples/tutorial_builder/03_02_option.rs"
|
||||
required-features = ["cargo"]
|
||||
|
||||
[[example]]
|
||||
name = "03_02_option_mult"
|
||||
path = "examples/tutorial_builder/03_02_option_mult.rs"
|
||||
required-features = ["cargo"]
|
||||
|
||||
[[example]]
|
||||
name = "03_03_positional"
|
||||
path = "examples/tutorial_builder/03_03_positional.rs"
|
||||
required-features = ["cargo"]
|
||||
|
||||
[[example]]
|
||||
name = "03_03_positional_mult"
|
||||
path = "examples/tutorial_builder/03_03_positional_mult.rs"
|
||||
required-features = ["cargo"]
|
||||
|
||||
[[example]]
|
||||
name = "03_04_subcommands"
|
||||
path = "examples/tutorial_builder/03_04_subcommands.rs"
|
||||
|
@ -221,7 +220,10 @@ required-features = ["cargo"]
|
|||
[[example]]
|
||||
name = "04_01_enum"
|
||||
path = "examples/tutorial_builder/04_01_enum.rs"
|
||||
required-features = ["cargo"]
|
||||
required-features = [
|
||||
"cargo",
|
||||
"derive",
|
||||
]
|
||||
|
||||
[[example]]
|
||||
name = "04_02_parse"
|
||||
|
@ -284,21 +286,11 @@ name = "03_02_option_derive"
|
|||
path = "examples/tutorial_derive/03_02_option.rs"
|
||||
required-features = ["derive"]
|
||||
|
||||
[[example]]
|
||||
name = "03_02_option_mult_derive"
|
||||
path = "examples/tutorial_derive/03_02_option_mult.rs"
|
||||
required-features = ["derive"]
|
||||
|
||||
[[example]]
|
||||
name = "03_03_positional_derive"
|
||||
path = "examples/tutorial_derive/03_03_positional.rs"
|
||||
required-features = ["derive"]
|
||||
|
||||
[[example]]
|
||||
name = "03_03_positional_mult_derive"
|
||||
path = "examples/tutorial_derive/03_03_positional_mult.rs"
|
||||
required-features = ["derive"]
|
||||
|
||||
[[example]]
|
||||
name = "03_04_subcommands_derive"
|
||||
path = "examples/tutorial_derive/03_04_subcommands.rs"
|
||||
|
@ -345,6 +337,11 @@ 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"]
|
||||
|
||||
[[example]]
|
||||
name = "interop_augment_args"
|
||||
path = "examples/derive_ref/augment_args.rs"
|
||||
|
@ -365,38 +362,116 @@ name = "interop_flatten_hand_args"
|
|||
path = "examples/derive_ref/flatten_hand_args.rs"
|
||||
required-features = ["derive"]
|
||||
|
||||
[dependencies.clap_builder]
|
||||
version = "=4.1.14"
|
||||
default-features = false
|
||||
[[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"
|
||||
optional = true
|
||||
|
||||
[dependencies.backtrace]
|
||||
version = "0.3"
|
||||
optional = true
|
||||
|
||||
[dependencies.bitflags]
|
||||
version = "1.2"
|
||||
|
||||
[dependencies.clap_derive]
|
||||
version = "=4.1.14"
|
||||
version = "=3.1.18"
|
||||
optional = true
|
||||
|
||||
[dependencies.once_cell]
|
||||
version = "1.12.0"
|
||||
[dependencies.clap_lex]
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies.indexmap]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "1"
|
||||
optional = true
|
||||
|
||||
[dependencies.regex]
|
||||
version = "1.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.strsim]
|
||||
version = "0.10"
|
||||
optional = true
|
||||
|
||||
[dependencies.termcolor]
|
||||
version = "1.1.1"
|
||||
optional = true
|
||||
|
||||
[dependencies.terminal_size]
|
||||
version = "0.1.12"
|
||||
optional = true
|
||||
|
||||
[dependencies.textwrap]
|
||||
version = "0.15.0"
|
||||
features = []
|
||||
default-features = false
|
||||
|
||||
[dependencies.unicase]
|
||||
version = "2.6"
|
||||
optional = true
|
||||
|
||||
[dependencies.yaml-rust]
|
||||
version = "0.4.1"
|
||||
optional = true
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.3.2"
|
||||
|
||||
[dev-dependencies.humantime]
|
||||
version = "2.1.0"
|
||||
version = "2"
|
||||
|
||||
[dev-dependencies.lazy_static]
|
||||
version = "1"
|
||||
|
||||
[dev-dependencies.regex]
|
||||
version = "1.0"
|
||||
|
||||
[dev-dependencies.rustversion]
|
||||
version = "1.0.12"
|
||||
version = "1"
|
||||
|
||||
[dev-dependencies.shlex]
|
||||
version = "1.1.0"
|
||||
|
||||
[dev-dependencies.snapbox]
|
||||
version = "0.4.10"
|
||||
|
||||
[dev-dependencies.static_assertions]
|
||||
version = "1.1.0"
|
||||
version = "0.2.9"
|
||||
|
||||
[dev-dependencies.trybuild]
|
||||
version = "1.0.77"
|
||||
version = "1.0.18"
|
||||
|
||||
[dev-dependencies.trycmd]
|
||||
version = "0.14.15"
|
||||
version = "0.13"
|
||||
features = [
|
||||
"color-auto",
|
||||
"diff",
|
||||
|
@ -404,49 +479,50 @@ features = [
|
|||
]
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies.unic-emoji-char]
|
||||
version = "0.9.0"
|
||||
|
||||
[features]
|
||||
cargo = ["clap_builder/cargo"]
|
||||
color = ["clap_builder/color"]
|
||||
cargo = ["lazy_static"]
|
||||
color = [
|
||||
"atty",
|
||||
"termcolor",
|
||||
]
|
||||
debug = [
|
||||
"clap_builder/debug",
|
||||
"clap_derive?/debug",
|
||||
"clap_derive/debug",
|
||||
"backtrace",
|
||||
]
|
||||
default = [
|
||||
"std",
|
||||
"color",
|
||||
"help",
|
||||
"usage",
|
||||
"error-context",
|
||||
"suggestions",
|
||||
]
|
||||
deprecated = [
|
||||
"clap_builder/deprecated",
|
||||
"clap_derive?/deprecated",
|
||||
]
|
||||
derive = [
|
||||
"dep:clap_derive",
|
||||
"dep:once_cell",
|
||||
"clap_derive",
|
||||
"lazy_static",
|
||||
]
|
||||
env = []
|
||||
std = ["indexmap/std"]
|
||||
suggestions = ["strsim"]
|
||||
unicode = [
|
||||
"textwrap/unicode-width",
|
||||
"unicase",
|
||||
]
|
||||
env = ["clap_builder/env"]
|
||||
error-context = ["clap_builder/error-context"]
|
||||
help = ["clap_builder/help"]
|
||||
std = ["clap_builder/std"]
|
||||
string = ["clap_builder/string"]
|
||||
suggestions = ["clap_builder/suggestions"]
|
||||
unicode = ["clap_builder/unicode"]
|
||||
unstable-doc = [
|
||||
"clap_builder/unstable-doc",
|
||||
"derive",
|
||||
"cargo",
|
||||
"wrap_help",
|
||||
"yaml",
|
||||
"env",
|
||||
"unicode",
|
||||
"regex",
|
||||
"unstable-replace",
|
||||
"unstable-multicall",
|
||||
"unstable-grouped",
|
||||
]
|
||||
unstable-grouped = ["clap_builder/unstable-grouped"]
|
||||
unstable-replace = ["clap_builder/unstable-replace"]
|
||||
unstable-v5 = [
|
||||
"clap_builder/unstable-v5",
|
||||
"clap_derive?/unstable-v5",
|
||||
"deprecated",
|
||||
unstable-grouped = []
|
||||
unstable-multicall = []
|
||||
unstable-replace = []
|
||||
unstable-v4 = ["clap_derive/unstable-v4"]
|
||||
wrap_help = [
|
||||
"terminal_size",
|
||||
"textwrap/terminal_size",
|
||||
]
|
||||
usage = ["clap_builder/usage"]
|
||||
wrap_help = ["clap_builder/wrap_help"]
|
||||
yaml = ["yaml-rust"]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2022 Kevin B. Knapp and Clap Contributors
|
||||
Copyright (c) 2015-2016 Kevin B. Knapp
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,24 +1,160 @@
|
|||
<!-- omit in TOC -->
|
||||
# clap
|
||||
|
||||
> **Command Line Argument Parser for Rust**
|
||||
|
||||
[![Crates.io](https://img.shields.io/crates/v/clap?style=flat-square)](https://crates.io/crates/clap)
|
||||
[![Crates.io](https://img.shields.io/crates/d/clap?style=flat-square)](https://crates.io/crates/clap)
|
||||
[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](LICENSE-APACHE)
|
||||
[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE-MIT)
|
||||
[![Build Status](https://img.shields.io/github/actions/workflow/status/clap-rs/clap/ci.yml?branch=master&style=flat-square)](https://github.com/clap-rs/clap/actions/workflows/ci.yml?query=branch%3Amaster)
|
||||
[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/v3.1.18/LICENSE-APACHE)
|
||||
[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/v3.1.18/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)
|
||||
|
||||
Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT).
|
||||
|
||||
1. [About](#about)
|
||||
2. Tutorial: [Builder API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_builder/README.md), [Derive API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_derive/README.md)
|
||||
3. [Examples](https://github.com/clap-rs/clap/blob/v3.1.18/examples/README.md)
|
||||
4. [API Reference](https://docs.rs/clap)
|
||||
- [Derive Reference](https://github.com/clap-rs/clap/blob/v3.1.18/examples/derive_ref/README.md)
|
||||
- [Feature Flags](#feature-flags)
|
||||
5. [CHANGELOG](https://github.com/clap-rs/clap/blob/v3.1.18/CHANGELOG.md)
|
||||
6. [FAQ](https://github.com/clap-rs/clap/blob/v3.1.18/docs/FAQ.md)
|
||||
7. [Questions & Discussions](https://github.com/clap-rs/clap/discussions)
|
||||
8. [Contributing](https://github.com/clap-rs/clap/blob/v3.1.18/CONTRIBUTING.md)
|
||||
8. [Sponsors](#sponsors)
|
||||
|
||||
## About
|
||||
|
||||
Create your command-line parser, with all of the bells and whistles, declaratively or procedurally.
|
||||
|
||||
For more details, see:
|
||||
- [docs.rs](https://docs.rs/clap/latest/clap/)
|
||||
- [examples](examples/)
|
||||
### Example
|
||||
|
||||
This uses our
|
||||
[Derive API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_derive/README.md)
|
||||
which provides access to the [Builder API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_builder/README.md) as attributes on a `struct`:
|
||||
|
||||
<!-- Copied from examples/demo.{rs,md} -->
|
||||
```rust,no_run
|
||||
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)
|
||||
}
|
||||
}
|
||||
```
|
||||
Add this to `Cargo.toml`:
|
||||
```toml
|
||||
[dependencies]
|
||||
clap = { version = "3.1.18", features = ["derive"] }
|
||||
```
|
||||
```bash
|
||||
$ demo --help
|
||||
clap [..]
|
||||
Simple program to greet a person
|
||||
|
||||
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
|
||||
```
|
||||
*(version number and `.exe` extension on windows replaced by placeholders)*
|
||||
|
||||
### Aspirations
|
||||
|
||||
- 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)
|
||||
|
||||
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.
|
||||
|
||||
### Selecting an API
|
||||
|
||||
Why use the declarative [Derive API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_derive/README.md):
|
||||
- Easier to read, write, and modify
|
||||
- Easier to keep the argument declaration and reading of argument in sync
|
||||
- Easier to reuse, e.g. [clap-verbosity-flag](https://crates.io/crates/clap-verbosity-flag)
|
||||
|
||||
Why use the procedural [Builder API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_builder/README.md):
|
||||
- Faster compile times if you aren't already using other procedural macros
|
||||
- More flexible, e.g. you can look up how many times an argument showed up,
|
||||
what its values were, and what were the indexes of those values. The Derive
|
||||
API can only report presence, number of occurrences, or values but no indices
|
||||
or combinations of data.
|
||||
|
||||
### Related Projects
|
||||
|
||||
- [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)
|
||||
- [shadow-rs](https://crates.io/crates/shadow-rs) for generating `Command::long_version`
|
||||
- [clap_lex](https://crates.io/crates/clap_lex) for a lighter-weight, battle-tested CLI parser
|
||||
- [clap_mangen](https://crates.io/crates/clap_mangen) for generating man page source (roff)
|
||||
- [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)
|
||||
|
||||
## Feature Flags
|
||||
|
||||
### Default Features
|
||||
|
||||
* **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.
|
||||
|
||||
#### Optional features
|
||||
|
||||
* **derive**: Enables the custom derive (i.e. `#[derive(Parser)]`). Without this you must use one of the other methods of creating a `clap` CLI listed above.
|
||||
* **cargo**: Turns on macros that read values from `CARGO_*` environment variables.
|
||||
* **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.
|
||||
|
||||
#### Experimental features
|
||||
|
||||
**Warning:** These may contain breaking changes between minor releases.
|
||||
|
||||
* **unstable-replace**: Enable [`Command::replace`](https://github.com/clap-rs/clap/issues/2836)
|
||||
* **unstable-multicall**: Enable [`Command::multicall`](https://github.com/clap-rs/clap/issues/2861)
|
||||
* **unstable-grouped**: Enable [`ArgMatches::grouped_values_of`](https://github.com/clap-rs/clap/issues/2924)
|
||||
* **unstable-v4**: Preview features which will be stable on the v4.0 release
|
||||
|
||||
## Sponsors
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
use clap::Command;
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
pub fn build_empty(c: &mut Criterion) {
|
||||
c.bench_function("build_empty", |b| b.iter(|| Command::new("claptests")));
|
||||
}
|
||||
|
||||
pub fn parse_empty(c: &mut Criterion) {
|
||||
c.bench_function("parse_empty", |b| {
|
||||
b.iter(|| Command::new("claptests").get_matches_from(vec![""]))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, build_empty, parse_empty);
|
||||
criterion_main!(benches);
|
|
@ -0,0 +1,104 @@
|
|||
use clap::{arg, Arg, Command};
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
macro_rules! create_app {
|
||||
() => {{
|
||||
Command::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(|| Command::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");
|
||||
Command::new("claptests").arg(&arg)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn build_with_opt(c: &mut Criterion) {
|
||||
c.bench_function("build_with_opt", |b| {
|
||||
b.iter(|| Command::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");
|
||||
Command::new("claptests").arg(&arg)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn build_with_pos(c: &mut Criterion) {
|
||||
c.bench_function("build_with_pos", |b| {
|
||||
b.iter(|| Command::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");
|
||||
Command::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);
|
|
@ -0,0 +1,307 @@
|
|||
use clap::{arg, Arg, Command};
|
||||
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 {
|
||||
() => {{
|
||||
Command::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(
|
||||
Command::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(|| {
|
||||
Command::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(
|
||||
Command::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!()
|
||||
.args_conflicts_with_subcommands(true)
|
||||
.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);
|
|
@ -0,0 +1,223 @@
|
|||
use clap::Command;
|
||||
use clap::{arg, Arg};
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use std::io::Cursor;
|
||||
|
||||
fn build_help(cmd: &mut Command) -> String {
|
||||
let mut buf = Cursor::new(Vec::with_capacity(50));
|
||||
cmd.write_help(&mut buf).unwrap();
|
||||
let content = buf.into_inner();
|
||||
String::from_utf8(content).unwrap()
|
||||
}
|
||||
|
||||
fn app_example1<'c>() -> Command<'c> {
|
||||
Command::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(
|
||||
Command::new("test")
|
||||
.about("does testing things")
|
||||
.arg(arg!(-l --list "lists test values")),
|
||||
)
|
||||
}
|
||||
|
||||
fn app_example2<'c>() -> Command<'c> {
|
||||
Command::new("MyApp")
|
||||
.version("1.0")
|
||||
.author("Kevin K. <kbknapp@gmail.com>")
|
||||
.about("Does awesome things")
|
||||
}
|
||||
|
||||
fn app_example3<'c>() -> Command<'c> {
|
||||
Command::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>() -> Command<'c> {
|
||||
Command::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>() -> Command<'c> {
|
||||
Command::new("MyApp").arg(
|
||||
Arg::new("awesome")
|
||||
.help("turns up the awesome")
|
||||
.short('a')
|
||||
.long("awesome")
|
||||
.multiple_occurrences(true),
|
||||
)
|
||||
}
|
||||
|
||||
fn app_example6<'c>() -> Command<'c> {
|
||||
Command::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>() -> Command<'c> {
|
||||
Command::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>() -> Command<'c> {
|
||||
Command::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>() -> Command<'c> {
|
||||
Command::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 cmd = app_example1();
|
||||
c.bench_function("example1", |b| b.iter(|| build_help(&mut cmd)));
|
||||
}
|
||||
|
||||
pub fn example2(c: &mut Criterion) {
|
||||
let mut cmd = app_example2();
|
||||
c.bench_function("example2", |b| b.iter(|| build_help(&mut cmd)));
|
||||
}
|
||||
|
||||
pub fn example3(c: &mut Criterion) {
|
||||
let mut cmd = app_example3();
|
||||
c.bench_function("example3", |b| b.iter(|| build_help(&mut cmd)));
|
||||
}
|
||||
|
||||
pub fn example4(c: &mut Criterion) {
|
||||
let mut cmd = app_example4();
|
||||
c.bench_function("example4", |b| b.iter(|| build_help(&mut cmd)));
|
||||
}
|
||||
|
||||
pub fn example5(c: &mut Criterion) {
|
||||
let mut cmd = app_example5();
|
||||
c.bench_function("example5", |b| b.iter(|| build_help(&mut cmd)));
|
||||
}
|
||||
|
||||
pub fn example6(c: &mut Criterion) {
|
||||
let mut cmd = app_example6();
|
||||
c.bench_function("example6", |b| b.iter(|| build_help(&mut cmd)));
|
||||
}
|
||||
|
||||
pub fn example7(c: &mut Criterion) {
|
||||
let mut cmd = app_example7();
|
||||
c.bench_function("example7", |b| b.iter(|| build_help(&mut cmd)));
|
||||
}
|
||||
|
||||
pub fn example8(c: &mut Criterion) {
|
||||
let mut cmd = app_example8();
|
||||
c.bench_function("example8", |b| b.iter(|| build_help(&mut cmd)));
|
||||
}
|
||||
|
||||
pub fn example10(c: &mut Criterion) {
|
||||
let mut cmd = app_example10();
|
||||
c.bench_function("example10", |b| b.iter(|| build_help(&mut cmd)));
|
||||
}
|
||||
|
||||
pub fn example4_template(c: &mut Criterion) {
|
||||
let mut cmd = 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 cmd)));
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
example1,
|
||||
example2,
|
||||
example3,
|
||||
example4,
|
||||
example5,
|
||||
example6,
|
||||
example7,
|
||||
example8,
|
||||
example10,
|
||||
example4_template
|
||||
);
|
||||
|
||||
criterion_main!(benches);
|
|
@ -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::{Arg, Command};
|
||||
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 cmd = app_short();
|
||||
c.bench_function("write_rg_short_help", |b| b.iter(|| build_help(&mut cmd)));
|
||||
}
|
||||
|
||||
pub fn write_rg_long_help(c: &mut Criterion) {
|
||||
let mut cmd = app_long();
|
||||
c.bench_function("write_rg_long_help", |b| b.iter(|| build_help(&mut cmd)));
|
||||
}
|
||||
|
||||
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() -> Command<'static> {
|
||||
cmd(false, |k| USAGES[k].short)
|
||||
}
|
||||
|
||||
/// Build a clap application with long help strings.
|
||||
fn app_long() -> Command<'static> {
|
||||
cmd(true, |k| USAGES[k].long)
|
||||
}
|
||||
|
||||
/// Build the help text of an application.
|
||||
fn build_help(cmd: &mut Command) -> String {
|
||||
let mut buf = Cursor::new(Vec::with_capacity(50));
|
||||
cmd.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. `cmd` 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 cmd<F>(_next_line_help: bool, doc: F) -> Command<'static>
|
||||
where
|
||||
F: Fn(&'static str) -> &'static str,
|
||||
{
|
||||
let arg = |name| Arg::new(name).help(doc(name));
|
||||
let flag = |name| arg(name).long(name);
|
||||
|
||||
Command::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);
|
|
@ -0,0 +1,412 @@
|
|||
// Used to simulate a fairly large number of subcommands
|
||||
//
|
||||
// CLI used is from rustup 408ed84f0e50511ed44a405dd91365e5da588790
|
||||
|
||||
use clap::{AppSettings, Arg, ArgGroup, Command};
|
||||
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() -> Command<'static> {
|
||||
Command::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(
|
||||
Command::new("show")
|
||||
.about("Show the active and installed toolchains")
|
||||
.after_help(SHOW_HELP),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("install")
|
||||
.about("Update Rust toolchains")
|
||||
.after_help(TOOLCHAIN_INSTALL_HELP)
|
||||
.hide(true) // synonym for 'toolchain install'
|
||||
.arg(Arg::new("toolchain").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::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(
|
||||
Command::new("default")
|
||||
.about("Set the default toolchain")
|
||||
.after_help(DEFAULT_HELP)
|
||||
.arg(Arg::new("toolchain").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("toolchain")
|
||||
.about("Modify or query the installed toolchains")
|
||||
.after_help(TOOLCHAIN_HELP)
|
||||
.setting(AppSettings::DeriveDisplayOrder)
|
||||
// .setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(Command::new("list").about("List installed toolchains"))
|
||||
.subcommand(
|
||||
Command::new("install")
|
||||
.about("Install or update a given toolchain")
|
||||
.arg(Arg::new("toolchain").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("uninstall")
|
||||
.about("Uninstall a toolchain")
|
||||
.arg(Arg::new("toolchain").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::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(
|
||||
Command::new("update")
|
||||
.hide(true) // synonym for 'install'
|
||||
.arg(Arg::new("toolchain").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("add")
|
||||
.hide(true) // synonym for 'install'
|
||||
.arg(Arg::new("toolchain").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("remove")
|
||||
.hide(true) // synonym for 'uninstall'
|
||||
.arg(Arg::new("toolchain").required(true)),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("target")
|
||||
.about("Modify a toolchain's supported targets")
|
||||
.setting(AppSettings::DeriveDisplayOrder)
|
||||
// .setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
Command::new("list")
|
||||
.about("List installed and available targets")
|
||||
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::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(
|
||||
Command::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(
|
||||
Command::new("install")
|
||||
.hide(true) // synonym for 'add'
|
||||
.arg(Arg::new("target").required(true))
|
||||
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("uninstall")
|
||||
.hide(true) // synonym for 'remove'
|
||||
.arg(Arg::new("target").required(true))
|
||||
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("component")
|
||||
.about("Modify a toolchain's installed components")
|
||||
.setting(AppSettings::DeriveDisplayOrder)
|
||||
// .setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
Command::new("list")
|
||||
.about("List installed and available components")
|
||||
.arg(Arg::new("toolchain").long("toolchain").takes_value(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::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(
|
||||
Command::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(
|
||||
Command::new("override")
|
||||
.about("Modify directory toolchain overrides")
|
||||
.after_help(OVERRIDE_HELP)
|
||||
.setting(AppSettings::DeriveDisplayOrder)
|
||||
// .setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(Command::new("list").about("List directory toolchain overrides"))
|
||||
.subcommand(
|
||||
Command::new("set")
|
||||
.about("Set the override toolchain for a directory")
|
||||
.arg(Arg::new("toolchain").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::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(
|
||||
Command::new("add")
|
||||
.hide(true) // synonym for 'set'
|
||||
.arg(Arg::new("toolchain").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("remove")
|
||||
.hide(true) // 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(
|
||||
Command::new("run")
|
||||
.about("Run a command with an environment configured for a given toolchain")
|
||||
.after_help(RUN_HELP)
|
||||
.trailing_var_arg(true)
|
||||
.arg(Arg::new("toolchain").required(true))
|
||||
.arg(
|
||||
Arg::new("command")
|
||||
.required(true)
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.multiple_occurrences(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("which")
|
||||
.about("Display which binary will be run for a given command")
|
||||
.arg(Arg::new("command").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::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(
|
||||
Command::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(
|
||||
Command::new("self")
|
||||
.about("Modify the rustup installation")
|
||||
.setting(AppSettings::DeriveDisplayOrder)
|
||||
.subcommand(Command::new("update").about("Download and install updates to rustup"))
|
||||
.subcommand(
|
||||
Command::new("uninstall")
|
||||
.about("Uninstall rustup.")
|
||||
.arg(Arg::new("no-prompt").short('y')),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("upgrade-data").about("Upgrade the internal data format."),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("telemetry")
|
||||
.about("rustup telemetry commands")
|
||||
.hide(true)
|
||||
.setting(AppSettings::DeriveDisplayOrder)
|
||||
.subcommand(Command::new("enable").about("Enable rustup telemetry"))
|
||||
.subcommand(Command::new("disable").about("Disable rustup telemetry"))
|
||||
.subcommand(Command::new("analyze").about("Analyze stored telemetry")),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("set")
|
||||
.about("Alter rustup settings")
|
||||
.subcommand(
|
||||
Command::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);
|
|
@ -1,16 +1,40 @@
|
|||
# Examples
|
||||
|
||||
We try to focus our documentation on the [four types of
|
||||
documentation](https://documentation.divio.com/). Examples fit into this by
|
||||
providing:
|
||||
- [Cookbook / How-To Guides](https://docs.rs/clap/latest/clap/_cookbook/index.html)
|
||||
- Tutorials ([derive](https://docs.rs/clap/latest/clap/_derive/_tutorial/index.html), [builder](https://docs.rs/clap/latest/clap/_tutorial/index.html))
|
||||
|
||||
This directory contains the source for the above.
|
||||
- Basic demo: [derive](demo.md)
|
||||
- Typed arguments: [derive](typed-derive.md)
|
||||
- Topics:
|
||||
- Custom `parse()`
|
||||
- Custom cargo command: [builder](cargo-example.md), [derive](cargo-example-derive.md)
|
||||
- Topics:
|
||||
- Subcommands
|
||||
- Cargo plugins
|
||||
- git-like interface: [builder](git.md), [derive](git-derive.md)
|
||||
- Topics:
|
||||
- Subcommands
|
||||
- External subcommands
|
||||
- Optional subcommands
|
||||
- Default subcommands
|
||||
- pacman-like interface: [builder](pacman.md)
|
||||
- Topics:
|
||||
- Flag subcommands
|
||||
- Conflicting arguments
|
||||
- Escaped positionals with `--`: [builder](escaped-positional.md), [derive](escaped-positional-derive.md)
|
||||
- Multi-call
|
||||
- busybox: [builder](multicall-busybox.md)
|
||||
- Topics:
|
||||
- Subcommands
|
||||
- hostname: [builder](multicall-hostname.md)
|
||||
- Topics:
|
||||
- Subcommands
|
||||
- repl: [builder](repl.rs)
|
||||
- Topics:
|
||||
- Read-Eval-Print Loops / Custom command lines
|
||||
|
||||
## Contributing
|
||||
|
||||
New examples should fit within the above structure and support their narrative
|
||||
- Add the example to [Cargo.toml](../Cargo.toml) for any `required-features`
|
||||
- Document how the example works with a `.md` file which will be verified using [trycmd](https://docs.rs/trycmd)
|
||||
- Pull the `.rs` and `.md` file into the appropriate module doc comment to be accessible on docs.rs
|
||||
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).
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
*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
|
||||
|
@ -6,24 +8,29 @@ mimicking cargo's interface.
|
|||
The help looks like:
|
||||
```console
|
||||
$ cargo-example-derive --help
|
||||
Usage: cargo <COMMAND>
|
||||
cargo
|
||||
|
||||
Commands:
|
||||
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)
|
||||
USAGE:
|
||||
cargo <SUBCOMMAND>
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
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]
|
||||
USAGE:
|
||||
cargo example-derive [OPTIONS]
|
||||
|
||||
Options:
|
||||
--manifest-path <MANIFEST_PATH>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
--manifest-path <MANIFEST_PATH>
|
||||
-V, --version Print version information
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
// Note: this requires the `derive` feature
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser)] // requires `derive` feature
|
||||
#[command(name = "cargo")]
|
||||
#[command(bin_name = "cargo")]
|
||||
enum CargoCli {
|
||||
ExampleDerive(ExampleDeriveArgs),
|
||||
#[derive(Parser)]
|
||||
#[clap(name = "cargo")]
|
||||
#[clap(bin_name = "cargo")]
|
||||
enum Cargo {
|
||||
ExampleDerive(ExampleDerive),
|
||||
}
|
||||
|
||||
#[derive(clap::Args)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct ExampleDeriveArgs {
|
||||
#[arg(long)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct ExampleDerive {
|
||||
#[clap(long, parse(from_os_str))]
|
||||
manifest_path: Option<std::path::PathBuf>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let CargoCli::ExampleDerive(args) = CargoCli::parse();
|
||||
let Cargo::ExampleDerive(args) = Cargo::parse();
|
||||
println!("{:?}", args.manifest_path);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
*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
|
||||
|
@ -6,24 +8,29 @@ mimicking cargo's interface.
|
|||
The help looks like:
|
||||
```console
|
||||
$ cargo-example --help
|
||||
Usage: cargo <COMMAND>
|
||||
cargo
|
||||
|
||||
Commands:
|
||||
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)
|
||||
USAGE:
|
||||
cargo <SUBCOMMAND>
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
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]
|
||||
USAGE:
|
||||
cargo example [OPTIONS]
|
||||
|
||||
Options:
|
||||
--manifest-path <PATH>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
--manifest-path <PATH>
|
||||
-V, --version Print version information
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// Note: this requires the `cargo` feature
|
||||
|
||||
fn main() {
|
||||
let cmd = clap::Command::new("cargo")
|
||||
.bin_name("cargo")
|
||||
|
@ -5,7 +7,8 @@ fn main() {
|
|||
.subcommand(
|
||||
clap::command!("example").arg(
|
||||
clap::arg!(--"manifest-path" <PATH>)
|
||||
.value_parser(clap::value_parser!(std::path::PathBuf)),
|
||||
.required(false)
|
||||
.allow_invalid_utf8(true),
|
||||
),
|
||||
);
|
||||
let matches = cmd.get_matches();
|
||||
|
@ -13,6 +16,8 @@ fn main() {
|
|||
Some(("example", matches)) => matches,
|
||||
_ => unreachable!("clap should ensure we don't get here"),
|
||||
};
|
||||
let manifest_path = matches.get_one::<std::path::PathBuf>("manifest-path");
|
||||
let manifest_path = matches
|
||||
.value_of_os("manifest-path")
|
||||
.map(std::path::PathBuf::from);
|
||||
println!("{:?}", manifest_path);
|
||||
}
|
||||
|
|
|
@ -1,17 +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>
|
||||
USAGE:
|
||||
demo[EXE] [OPTIONS] --name <NAME>
|
||||
|
||||
Options:
|
||||
-n, --name <NAME> Name of the person to greet
|
||||
-c, --count <COUNT> Number of times to greet [default: 1]
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ demo --name Me
|
||||
Hello Me!
|
||||
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
|
||||
|
||||
```
|
||||
*(version number and `.exe` extension on windows replaced by placeholders)*
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
// Note: this requires the `derive` feature
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
/// Simple program to greet a person
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Name of the person to greet
|
||||
#[arg(short, long)]
|
||||
#[clap(short, long)]
|
||||
name: String,
|
||||
|
||||
/// Number of times to greet
|
||||
#[arg(short, long, default_value_t = 1)]
|
||||
#[clap(short, long, default_value_t = 1)]
|
||||
count: u8,
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,429 @@
|
|||
# Derive Reference
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Attributes](#attributes)
|
||||
1. [Terminology](#terminology)
|
||||
2. [Command Attributes](#command-attributes)
|
||||
3. [Arg Attributes](#arg-attributes)
|
||||
4. [Arg Enum Attributes](#arg-enum-attributes)
|
||||
5. [Possible Value Attributes](#possible-value-attributes)
|
||||
3. [Arg Types](#arg-types)
|
||||
4. [Doc Comments](#doc-comments)
|
||||
5. [Tips](#tips)
|
||||
6. [Mixing Builder and Derive APIS](#mixing-builder-and-derive-apis)
|
||||
|
||||
## 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 the anatomy of the derive attributes:
|
||||
```rust
|
||||
use clap::{Parser, Args, Subcommand, ArgEnum};
|
||||
|
||||
/// Doc comment
|
||||
#[derive(Parser)]
|
||||
#[clap(APP ATTRIBUTE)]
|
||||
struct Cli {
|
||||
/// Doc comment
|
||||
#[clap(ARG ATTRIBUTE)]
|
||||
field: UserType,
|
||||
|
||||
#[clap(arg_enum, ARG ATTRIBUTE...)]
|
||||
field: EnumValues,
|
||||
|
||||
#[clap(flatten)]
|
||||
delegate: Struct,
|
||||
|
||||
#[clap(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
/// Doc comment
|
||||
#[derive(Args)]
|
||||
#[clap(PARENT APP ATTRIBUTE)]
|
||||
struct Struct {
|
||||
/// Doc comment
|
||||
#[clap(ARG ATTRIBUTE)]
|
||||
field: UserType,
|
||||
}
|
||||
|
||||
/// 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: UserType,
|
||||
}
|
||||
}
|
||||
|
||||
/// Doc comment
|
||||
#[derive(ArgEnum)]
|
||||
#[clap(ARG ENUM ATTRIBUTE)]
|
||||
enum EnumValues {
|
||||
/// 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.
|
||||
- Subcommand arguments can be defined in a struct-variant or automatically flattened with a tuple-variant.
|
||||
- `ArgEnum` allows parsing a value directly into an `enum`, erroring on unsupported values.
|
||||
- The derive doesn't work on enums that contain non-unit variants, unless they are skipped
|
||||
|
||||
See also the [tutorial](../tutorial_derive/README.md) and [examples](../README.md).
|
||||
|
||||
## Attributes
|
||||
|
||||
### Terminology
|
||||
|
||||
**Raw attributes** are forwarded directly to the underlying `clap` builder. Any
|
||||
`Command`, `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** have post-processing done to them, whether that is
|
||||
- Providing of defaults
|
||||
- Special behavior is triggered off of it
|
||||
|
||||
Magic attributes are more constrained in the syntax they support, usually just
|
||||
`<attr> = <value>` though some use `<attr>(<value>)` instead. See the specific
|
||||
magic attributes documentation for details. This allows users to access the
|
||||
raw behavior of an attribute via `<attr>(<value>)` syntax.
|
||||
|
||||
**NOTE:** Some attributes are inferred from [Arg Types](#arg-types) and [Doc
|
||||
Comments](#doc-comments). Explicit attributes take precedence over inferred
|
||||
attributes.
|
||||
|
||||
### Command Attributes
|
||||
|
||||
These correspond to a `clap::Command` which is used for both top-level parsers and
|
||||
when defining subcommands.
|
||||
|
||||
**Magic attributes:**
|
||||
- `name = <expr>`: `clap::Command::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::Command::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::Command::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::Command::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::Command::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`
|
||||
- `next_display_order`: `clap::Command::next_display_order`
|
||||
- `next_help_heading`: `clap::Command::next_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 `Command::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::Command::allow_external_subcommand(true)`
|
||||
- Variant must be either `Variant(Vec<String>)` or `Variant(Vec<OsString>)`
|
||||
|
||||
**Raw attributes:** Any [`Command` method](https://docs.rs/clap/latest/clap/type.Command.html) can also be used as an attribute, see [Terminology](#terminology) for syntax.
|
||||
- e.g. `#[clap(arg_required_else_help(true))]` would translate to `cmd.arg_required_else_help(true)`
|
||||
|
||||
### Arg Attributes
|
||||
|
||||
These correspond to a `clap::Arg`.
|
||||
|
||||
**Magic attributes**:
|
||||
- `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` (needs `env` feature enabled)
|
||||
- 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 Command Attributes, this
|
||||
makes reuse harder. Generally prefer putting the cmd 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` and `clap::ArgMatches::values_of_t`
|
||||
- Default: `try_from_str`
|
||||
- Warning: for `Path` / `OsString`, be sure to use `try_from_os_str`
|
||||
- See [Arg Types](#arg-types) for more details
|
||||
- `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()`
|
||||
- `default_value_os_t [= <expr>]`: `clap::Arg::default_value_os` and `clap::Arg::required(false)`
|
||||
- Requires `std::convert::Into<OsString>` or `#[clap(arg_enum)]`
|
||||
- Without `<expr>`, relies on `Default::default()`
|
||||
|
||||
**Raw attributes:** Any [`Arg` method](https://docs.rs/clap/latest/clap/struct.Arg.html) can also be used as an attribute, see [Terminology](#terminology) for syntax.
|
||||
- e.g. `#[clap(max_values(3))]` would translate to `arg.max_values(3)`
|
||||
|
||||
### 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`.
|
||||
|
||||
**Magic attributes**:
|
||||
- `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)
|
||||
|
||||
**Raw attributes:** Any [`PossibleValue` method](https://docs.rs/clap/latest/clap/struct.PossibleValue.html) can also be used as an attribute, see [Terminology](#terminology) for syntax.
|
||||
- e.g. `#[clap(alias("foo"))]` would translate to `pv.alias("foo")`
|
||||
|
||||
## Arg Types
|
||||
|
||||
`clap` assumes some intent based on the type used:
|
||||
|
||||
| Type | Effect | Implies |
|
||||
|---------------------|--------------------------------------|------------------------------------------------------------------|
|
||||
| `bool` | flag | `#[clap(parse(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
|
||||
- Note: operations on values, like `default_value`, are unlikely to do what you want
|
||||
- `from_flag`
|
||||
- Implies `arg.takes_value(false)`
|
||||
- Reads from `clap::ArgMatches::is_present` rather than a `value_of` function
|
||||
- Note: operations on values, like `default_value`, are unlikely to do what you want
|
||||
|
||||
**Warning:**
|
||||
- To support non-UTF8 paths, you must use `parse(from_os_str)`, otherwise
|
||||
`clap` will use `clap::ArgMatches::value_of` with `PathBuf::FromStr`.
|
||||
|
||||
## Doc Comments
|
||||
|
||||
In clap, help messages for the whole binary can be specified
|
||||
via [`Command::about`] and [`Command::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 `Command::about/long_about` calls!**
|
||||
If you really want to use the `Command::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 `Command::about` / `Arg::help`. When a blank line is
|
||||
present, the whole doc comment will be passed to `Command::long_about` /
|
||||
`Arg::long_help`. Or in other words, a doc may result in just a `Command::about` /
|
||||
`Arg::help` or `Command::about` / `Arg::help` and `Command::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
|
||||
|
||||
## Tips
|
||||
|
||||
- To get access to a `Command` call `CommandFactory::command` (implemented when deriving `Parser`)
|
||||
- Proactively check for bad `Command` configurations by calling `Command::debug_assert` in a test ([example](../tutorial_derive/05_01_assert.rs))
|
||||
|
||||
## Mixing Builder and Derive APIs
|
||||
|
||||
The builder and derive APIs do not live in isolation. They can work together, which is especially helpful if some arguments can be specified at compile-time while others must be specified at runtime.
|
||||
|
||||
### Using derived arguments in a builder application
|
||||
|
||||
*[Jump to source](augment_args.rs)*
|
||||
|
||||
When using the derive API, you can `#[clap(flatten)]` a struct deriving `Args` into a struct deriving `Args` or `Parser`. This example shows how you can augment a `Command` instance created using the builder API with `Args` created using the derive API.
|
||||
|
||||
It uses the `Args::augment_args` method to add the arguments to the `Command` instance.
|
||||
|
||||
Crates such as [clap-verbosity-flag](https://github.com/rust-cli/clap-verbosity-flag) provide structs that implement `Args` or `Parser`. Without the technique shown in this example, it would not be possible to use such crates with the builder API. `augment_args` to the rescue!
|
||||
|
||||
### Using derived subcommands in a builder application
|
||||
|
||||
*[Jump to source](augment_subcommands.rs)*
|
||||
|
||||
When using the derive API, you can use `#[clap(subcommand)]` inside the struct to add subcommands. The type of the field is usually an enum that derived `Parser`. However, you can also add the subcommands in that enum to a `Command` instance created with the builder API.
|
||||
|
||||
It uses the `Subcommand::augment_subcommands` method to add the subcommands to the `Command` instance.
|
||||
|
||||
### Adding hand-implemented subcommands to a derived application
|
||||
|
||||
*[Jump to source](hand_subcommand.rs)*
|
||||
|
||||
When using the derive API, you can use `#[clap(subcommand)]` inside the struct to add subcommands. The type of the field is usually an enum that derived `Parser`. However, you can also implement the `Subcommand` trait manually on this enum (or any other type) and it can still be used inside the struct created with the derive API. The implementation of the `Subcommand` trait will use the builder API to add the subcommands to the `Command` instance created behind the scenes for you by the derive API.
|
||||
|
||||
Notice how in the previous example we used `augment_subcommands` on an enum that derived `Parser`, whereas now we implement `augment_subcommands` ourselves, but the derive API calls it automatically since we used the `#[clap(subcommand)]` attribute.
|
||||
|
||||
### Flattening hand-implemented args into a derived application
|
||||
|
||||
*[Jump to source](flatten_hand_args.rs)*
|
||||
|
||||
When using the derive API, you can use `#[clap(flatten)]` inside the struct to add arguments as if they were added directly to the containing struct. The type of the field is usually an struct that derived `Args`. However, you can also implement the `Args` trait manually on this struct (or any other type) and it can still be used inside the struct created with the derive API. The implementation of the `Args` trait will use the builder API to add the arguments to the `Command` instance created behind the scenes for you by the derive API.
|
||||
|
||||
Notice how in the example 1 we used `augment_args` on the struct that derived `Parser`, whereas now we implement `augment_args` ourselves, but the derive API calls it automatically since we used the `#[clap(flatten)]` attribute.
|
|
@ -1,21 +1,21 @@
|
|||
use clap::{arg, Args, Command, FromArgMatches as _};
|
||||
use clap::{arg, Args as _, Command, FromArgMatches as _, Parser};
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
#[derive(Parser, Debug)]
|
||||
struct DerivedArgs {
|
||||
#[arg(short, long)]
|
||||
#[clap(short, long)]
|
||||
derived: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cli = Command::new("CLI").arg(arg!(-b - -built).action(clap::ArgAction::SetTrue));
|
||||
let cli = Command::new("CLI").arg(arg!(-b - -built));
|
||||
// Augment built args with derived args
|
||||
let cli = DerivedArgs::augment_args(cli);
|
||||
|
||||
let matches = cli.get_matches();
|
||||
println!("Value of built: {:?}", matches.get_flag("built"));
|
||||
println!("Value of built: {:?}", matches.is_present("built"));
|
||||
println!(
|
||||
"Value of derived via ArgMatches: {:?}",
|
||||
matches.get_flag("derived")
|
||||
matches.is_present("derived")
|
||||
);
|
||||
|
||||
// Since DerivedArgs implements FromArgMatches, we can extract it from the unstructured ArgMatches.
|
||||
|
@ -23,5 +23,5 @@ fn main() {
|
|||
let derived_matches = DerivedArgs::from_arg_matches(&matches)
|
||||
.map_err(|err| err.exit())
|
||||
.unwrap();
|
||||
println!("Value of derived: {derived_matches:#?}");
|
||||
println!("Value of derived: {:#?}", derived_matches);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use clap::{Command, FromArgMatches as _, Parser, Subcommand as _};
|
|||
#[derive(Parser, Debug)]
|
||||
enum Subcommands {
|
||||
Derived {
|
||||
#[arg(short, long)]
|
||||
#[clap(short, long)]
|
||||
derived_flag: bool,
|
||||
},
|
||||
}
|
||||
|
@ -17,5 +17,5 @@ fn main() {
|
|||
let derived_subcommands = Subcommands::from_arg_matches(&matches)
|
||||
.map_err(|err| err.exit())
|
||||
.unwrap();
|
||||
println!("Derived subcommands: {derived_subcommands:#?}");
|
||||
println!("Derived subcommands: {:#?}", derived_subcommands);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
```
|
|
@ -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);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use clap::error::Error;
|
||||
use clap::{Arg, ArgAction, ArgMatches, Args, Command, FromArgMatches, Parser};
|
||||
use clap::{Arg, ArgMatches, Args, Command, FromArgMatches, Parser};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CliArgs {
|
||||
|
@ -10,82 +10,44 @@ struct CliArgs {
|
|||
|
||||
impl FromArgMatches for CliArgs {
|
||||
fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error> {
|
||||
let mut matches = matches.clone();
|
||||
Self::from_arg_matches_mut(&mut matches)
|
||||
}
|
||||
fn from_arg_matches_mut(matches: &mut ArgMatches) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
foo: matches.get_flag("foo"),
|
||||
bar: matches.get_flag("bar"),
|
||||
quuz: matches.remove_one::<String>("quuz"),
|
||||
foo: matches.is_present("foo"),
|
||||
bar: matches.is_present("bar"),
|
||||
quuz: matches.value_of("quuz").map(|quuz| quuz.to_owned()),
|
||||
})
|
||||
}
|
||||
fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error> {
|
||||
let mut matches = matches.clone();
|
||||
self.update_from_arg_matches_mut(&mut matches)
|
||||
}
|
||||
fn update_from_arg_matches_mut(&mut self, matches: &mut ArgMatches) -> Result<(), Error> {
|
||||
self.foo |= matches.get_flag("foo");
|
||||
self.bar |= matches.get_flag("bar");
|
||||
if let Some(quuz) = matches.remove_one::<String>("quuz") {
|
||||
self.quuz = Some(quuz);
|
||||
self.foo |= matches.is_present("foo");
|
||||
self.bar |= matches.is_present("bar");
|
||||
if let Some(quuz) = matches.value_of("quuz") {
|
||||
self.quuz = Some(quuz.to_owned());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Args for CliArgs {
|
||||
fn augment_args(cmd: Command) -> Command {
|
||||
cmd.arg(
|
||||
Arg::new("foo")
|
||||
.short('f')
|
||||
.long("foo")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("bar")
|
||||
.short('b')
|
||||
.long("bar")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("quuz")
|
||||
.short('q')
|
||||
.long("quuz")
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
fn augment_args(cmd: Command<'_>) -> Command<'_> {
|
||||
cmd.arg(Arg::new("foo").short('f').long("foo"))
|
||||
.arg(Arg::new("bar").short('b').long("bar"))
|
||||
.arg(Arg::new("quuz").short('q').long("quuz").takes_value(true))
|
||||
}
|
||||
fn augment_args_for_update(cmd: Command) -> Command {
|
||||
cmd.arg(
|
||||
Arg::new("foo")
|
||||
.short('f')
|
||||
.long("foo")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("bar")
|
||||
.short('b')
|
||||
.long("bar")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("quuz")
|
||||
.short('q')
|
||||
.long("quuz")
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
fn augment_args_for_update(cmd: Command<'_>) -> Command<'_> {
|
||||
cmd.arg(Arg::new("foo").short('f').long("foo"))
|
||||
.arg(Arg::new("bar").short('b').long("bar"))
|
||||
.arg(Arg::new("quuz").short('q').long("quuz").takes_value(true))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
#[arg(short, long)]
|
||||
#[clap(short, long)]
|
||||
top_level: bool,
|
||||
#[command(flatten)]
|
||||
#[clap(flatten)]
|
||||
more_args: CliArgs,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Cli::parse();
|
||||
println!("{args:#?}");
|
||||
println!("{:#?}", args);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ struct AddArgs {
|
|||
}
|
||||
#[derive(Parser, Debug)]
|
||||
struct RemoveArgs {
|
||||
#[arg(short, long)]
|
||||
#[clap(short, long)]
|
||||
force: bool,
|
||||
name: Vec<String>,
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ impl FromArgMatches for CliSub {
|
|||
Some(("add", args)) => Ok(Self::Add(AddArgs::from_arg_matches(args)?)),
|
||||
Some(("remove", args)) => Ok(Self::Remove(RemoveArgs::from_arg_matches(args)?)),
|
||||
Some((_, _)) => Err(Error::raw(
|
||||
ErrorKind::InvalidSubcommand,
|
||||
ErrorKind::UnrecognizedSubcommand,
|
||||
"Valid subcommands are `add` and `remove`",
|
||||
)),
|
||||
None => Err(Error::raw(
|
||||
|
@ -39,7 +39,7 @@ impl FromArgMatches for CliSub {
|
|||
Some(("remove", args)) => *self = Self::Remove(RemoveArgs::from_arg_matches(args)?),
|
||||
Some((_, _)) => {
|
||||
return Err(Error::raw(
|
||||
ErrorKind::InvalidSubcommand,
|
||||
ErrorKind::UnrecognizedSubcommand,
|
||||
"Valid subcommands are `add` and `remove`",
|
||||
))
|
||||
}
|
||||
|
@ -50,12 +50,12 @@ impl FromArgMatches for CliSub {
|
|||
}
|
||||
|
||||
impl Subcommand for CliSub {
|
||||
fn augment_subcommands(cmd: Command) -> Command {
|
||||
fn augment_subcommands(cmd: Command<'_>) -> Command<'_> {
|
||||
cmd.subcommand(AddArgs::augment_args(Command::new("add")))
|
||||
.subcommand(RemoveArgs::augment_args(Command::new("remove")))
|
||||
.subcommand_required(true)
|
||||
}
|
||||
fn augment_subcommands_for_update(cmd: Command) -> Command {
|
||||
fn augment_subcommands_for_update(cmd: Command<'_>) -> Command<'_> {
|
||||
cmd.subcommand(AddArgs::augment_args(Command::new("add")))
|
||||
.subcommand(RemoveArgs::augment_args(Command::new("remove")))
|
||||
.subcommand_required(true)
|
||||
|
@ -67,13 +67,13 @@ impl Subcommand for CliSub {
|
|||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
#[arg(short, long)]
|
||||
#[clap(short, long)]
|
||||
top_level: bool,
|
||||
#[command(subcommand)]
|
||||
#[clap(subcommand)]
|
||||
subcommand: CliSub,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Cli::parse();
|
||||
println!("{args:#?}");
|
||||
println!("{:#?}", args);
|
||||
}
|
||||
|
|
|
@ -35,11 +35,14 @@ Value of derived: DerivedArgs {
|
|||
```console
|
||||
$ interop_augment_args --unknown
|
||||
? failed
|
||||
error: unexpected argument '--unknown' found
|
||||
error: Found argument '--unknown' which wasn't expected, or isn't valid in this context
|
||||
|
||||
Usage: interop_augment_args[EXE] [OPTIONS]
|
||||
If you tried to supply `--unknown` as a value rather than a flag, use `-- --unknown`
|
||||
|
||||
For more information, try '--help'.
|
||||
USAGE:
|
||||
interop_augment_args[EXE] [OPTIONS]
|
||||
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
|
@ -70,22 +73,26 @@ Derived subcommands: Derived {
|
|||
```console
|
||||
$ interop_augment_subcommands derived --unknown
|
||||
? failed
|
||||
error: unexpected argument '--unknown' found
|
||||
error: Found argument '--unknown' which wasn't expected, or isn't valid in this context
|
||||
|
||||
Usage: interop_augment_subcommands[EXE] derived [OPTIONS]
|
||||
If you tried to supply `--unknown` as a value rather than a flag, use `-- --unknown`
|
||||
|
||||
For more information, try '--help'.
|
||||
USAGE:
|
||||
interop_augment_subcommands[EXE] derived [OPTIONS]
|
||||
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
```console
|
||||
$ interop_augment_subcommands unknown
|
||||
? failed
|
||||
error: unrecognized subcommand 'unknown'
|
||||
error: Found argument 'unknown' which wasn't expected, or isn't valid in this context
|
||||
|
||||
Usage: interop_augment_subcommands[EXE] [COMMAND]
|
||||
USAGE:
|
||||
interop_augment_subcommands[EXE] [SUBCOMMAND]
|
||||
|
||||
For more information, try '--help'.
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
|
@ -94,16 +101,12 @@ For more information, try '--help'.
|
|||
```console
|
||||
$ interop_hand_subcommand
|
||||
? failed
|
||||
Usage: interop_hand_subcommand[EXE] [OPTIONS] <COMMAND>
|
||||
error: 'interop_hand_subcommand[EXE]' requires a subcommand but one was not provided
|
||||
|
||||
Commands:
|
||||
add
|
||||
remove
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
USAGE:
|
||||
interop_hand_subcommand[EXE] [OPTIONS] <SUBCOMMAND>
|
||||
|
||||
Options:
|
||||
-t, --top-level
|
||||
-h, --help Print help
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
|
@ -140,13 +143,14 @@ Cli {
|
|||
```console
|
||||
$ interop_hand_subcommand add --unknown
|
||||
? failed
|
||||
error: unexpected argument '--unknown' found
|
||||
error: Found argument '--unknown' which wasn't expected, or isn't valid in this context
|
||||
|
||||
note: to pass '--unknown' as a value, use '-- --unknown'
|
||||
If you tried to supply `--unknown` as a value rather than a flag, use `-- --unknown`
|
||||
|
||||
Usage: interop_hand_subcommand[EXE] add [NAME]...
|
||||
USAGE:
|
||||
interop_hand_subcommand[EXE] add [NAME]...
|
||||
|
||||
For more information, try '--help'.
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
|
@ -185,11 +189,12 @@ Cli {
|
|||
```console
|
||||
$ interop_hand_subcommand unknown
|
||||
? failed
|
||||
error: unrecognized subcommand 'unknown'
|
||||
error: Found argument 'unknown' which wasn't expected, or isn't valid in this context
|
||||
|
||||
Usage: interop_hand_subcommand[EXE] [OPTIONS] <COMMAND>
|
||||
USAGE:
|
||||
interop_hand_subcommand[EXE] [OPTIONS] <SUBCOMMAND>
|
||||
|
||||
For more information, try '--help'.
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
|
@ -239,10 +244,13 @@ Cli {
|
|||
```console
|
||||
$ interop_flatten_hand_args --unknown
|
||||
? failed
|
||||
error: unexpected argument '--unknown' found
|
||||
error: Found argument '--unknown' which wasn't expected, or isn't valid in this context
|
||||
|
||||
Usage: interop_flatten_hand_args[EXE] [OPTIONS]
|
||||
If you tried to supply `--unknown` as a value rather than a flag, use `-- --unknown`
|
||||
|
||||
For more information, try '--help'.
|
||||
USAGE:
|
||||
interop_flatten_hand_args[EXE] [OPTIONS]
|
||||
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
|
|
@ -1,22 +1,26 @@
|
|||
**This requires enabling the [`derive` feature flag][crate::_features].**
|
||||
*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>...]
|
||||
USAGE:
|
||||
escaped-positional-derive[EXE] [OPTIONS] [-- <SLOP>...]
|
||||
|
||||
Arguments:
|
||||
[SLOP]...
|
||||
ARGS:
|
||||
<SLOP>...
|
||||
|
||||
Options:
|
||||
-f
|
||||
-p <PEAR>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
OPTIONS:
|
||||
-f
|
||||
-h, --help Print help information
|
||||
-p <PEAR>
|
||||
-V, --version Print version information
|
||||
|
||||
```
|
||||
|
||||
|
@ -33,11 +37,12 @@ Notice that we can't pass positional arguments before `--`:
|
|||
```console
|
||||
$ escaped-positional-derive foo bar
|
||||
? failed
|
||||
error: unexpected argument 'foo' found
|
||||
error: Found argument 'foo' which wasn't expected, or isn't valid in this context
|
||||
|
||||
Usage: escaped-positional-derive[EXE] [OPTIONS] [-- <SLOP>...]
|
||||
USAGE:
|
||||
escaped-positional-derive[EXE] [OPTIONS] [-- <SLOP>...]
|
||||
|
||||
For more information, try '--help'.
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
// Note: this requires the `derive` feature
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser)] // requires `derive` feature
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[derive(Parser)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[arg(short = 'f')]
|
||||
#[clap(short = 'f')]
|
||||
eff: bool,
|
||||
|
||||
#[arg(short = 'p', value_name = "PEAR")]
|
||||
#[clap(short = 'p', value_name = "PEAR")]
|
||||
pea: Option<String>,
|
||||
|
||||
#[arg(last = true)]
|
||||
#[clap(last = true)]
|
||||
slop: Vec<String>,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +1,26 @@
|
|||
**This requires enabling the [`cargo` feature flag][crate::_features].**
|
||||
*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>...]
|
||||
USAGE:
|
||||
escaped-positional[EXE] [OPTIONS] [-- <SLOP>...]
|
||||
|
||||
Arguments:
|
||||
[SLOP]...
|
||||
ARGS:
|
||||
<SLOP>...
|
||||
|
||||
Options:
|
||||
-f
|
||||
-p <PEAR>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
OPTIONS:
|
||||
-f
|
||||
-h, --help Print help information
|
||||
-p <PEAR>
|
||||
-V, --version Print version information
|
||||
|
||||
```
|
||||
|
||||
|
@ -33,11 +37,12 @@ Notice that we can't pass positional arguments before `--`:
|
|||
```console
|
||||
$ escaped-positional foo bar
|
||||
? failed
|
||||
error: unexpected argument 'foo' found
|
||||
error: Found argument 'foo' which wasn't expected, or isn't valid in this context
|
||||
|
||||
Usage: escaped-positional[EXE] [OPTIONS] [-- <SLOP>...]
|
||||
USAGE:
|
||||
escaped-positional[EXE] [OPTIONS] [-- <SLOP>...]
|
||||
|
||||
For more information, try '--help'.
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -1,32 +1,26 @@
|
|||
use clap::{arg, command, value_parser, ArgAction};
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
.arg(arg!(eff: -f).action(ArgAction::SetTrue))
|
||||
.arg(arg!(pea: -p <PEAR>).value_parser(value_parser!(String)))
|
||||
let matches = command!()
|
||||
.arg(arg!(eff: -f))
|
||||
.arg(arg!(pea: -p <PEAR>).required(false))
|
||||
.arg(
|
||||
// Indicates that `slop` is only accessible after `--`.
|
||||
arg!(slop: [SLOP])
|
||||
.num_args(1..)
|
||||
.last(true)
|
||||
.value_parser(value_parser!(String)),
|
||||
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`...
|
||||
|
||||
// -f used: true
|
||||
println!("-f used: {:?}", matches.get_flag("eff"));
|
||||
// -p's value: Some("bob")
|
||||
println!("-p's value: {:?}", matches.get_one::<String>("pea"));
|
||||
// 'slops' values: Some(["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
|
||||
.get_many::<String>("slop")
|
||||
.values_of("slop")
|
||||
.map(|vals| vals.collect::<Vec<_>>())
|
||||
.unwrap_or_default()
|
||||
);
|
||||
); // 'slops' values: Some(["sloppy", "slop", "slop"])
|
||||
|
||||
// Continued program logic goes here...
|
||||
}
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
`find` is an example of position-sensitive flags
|
||||
|
||||
```console
|
||||
$ find --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: find[EXE] [OPTIONS]
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
TESTS:
|
||||
--empty File is empty and is either a regular file or a directory
|
||||
--name <NAME> Base of file name (the path with the leading directories removed) matches shell
|
||||
pattern pattern
|
||||
|
||||
OPERATORS:
|
||||
-o, --or expr2 is not evaluate if exp1 is true
|
||||
-a, --and Same as `expr1 expr1`
|
||||
|
||||
$ find --empty -o --name .keep
|
||||
[
|
||||
(
|
||||
"empty",
|
||||
Bool(
|
||||
true,
|
||||
),
|
||||
),
|
||||
(
|
||||
"or",
|
||||
Bool(
|
||||
true,
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
String(
|
||||
".keep",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
```
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use clap::{arg, command, ArgGroup, ArgMatches, Command};
|
||||
|
||||
fn main() {
|
||||
let matches = cli().get_matches();
|
||||
let values = Value::from_matches(&matches);
|
||||
println!("{:#?}", values);
|
||||
}
|
||||
|
||||
fn cli() -> Command {
|
||||
command!()
|
||||
.group(ArgGroup::new("tests").multiple(true))
|
||||
.next_help_heading("TESTS")
|
||||
.args([
|
||||
arg!(--empty "File is empty and is either a regular file or a directory").group("tests"),
|
||||
arg!(--name <NAME> "Base of file name (the path with the leading directories removed) matches shell pattern pattern").group("tests"),
|
||||
])
|
||||
.group(ArgGroup::new("operators").multiple(true))
|
||||
.next_help_heading("OPERATORS")
|
||||
.args([
|
||||
arg!(-o - -or "expr2 is not evaluate if exp1 is true").group("operators"),
|
||||
arg!(-a - -and "Same as `expr1 expr1`").group("operators"),
|
||||
])
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Value {
|
||||
Bool(bool),
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn from_matches(matches: &ArgMatches) -> Vec<(clap::Id, Self)> {
|
||||
let mut values = BTreeMap::new();
|
||||
for id in matches.ids() {
|
||||
if matches.try_get_many::<clap::Id>(id.as_str()).is_ok() {
|
||||
// ignore groups
|
||||
continue;
|
||||
}
|
||||
let value_source = matches
|
||||
.value_source(id.as_str())
|
||||
.expect("id came from matches");
|
||||
if value_source != clap::parser::ValueSource::CommandLine {
|
||||
// Any other source just gets tacked on at the end (like default values)
|
||||
continue;
|
||||
}
|
||||
if Self::extract::<String>(matches, id, &mut values) {
|
||||
continue;
|
||||
}
|
||||
if Self::extract::<bool>(matches, id, &mut values) {
|
||||
continue;
|
||||
}
|
||||
unimplemented!("unknown type for {}: {:?}", id, matches);
|
||||
}
|
||||
values.into_values().collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn extract<T: Clone + Into<Value> + Send + Sync + 'static>(
|
||||
matches: &ArgMatches,
|
||||
id: &clap::Id,
|
||||
output: &mut BTreeMap<usize, (clap::Id, Self)>,
|
||||
) -> bool {
|
||||
match matches.try_get_many::<T>(id.as_str()) {
|
||||
Ok(Some(values)) => {
|
||||
for (value, index) in values.zip(
|
||||
matches
|
||||
.indices_of(id.as_str())
|
||||
.expect("id came from matches"),
|
||||
) {
|
||||
output.insert(index, (id.clone(), value.clone().into()));
|
||||
}
|
||||
true
|
||||
}
|
||||
Ok(None) => {
|
||||
unreachable!("`ids` only reports what is present")
|
||||
}
|
||||
Err(clap::parser::MatchesError::UnknownArgument { .. }) => {
|
||||
unreachable!("id came from matches")
|
||||
}
|
||||
Err(clap::parser::MatchesError::Downcast { .. }) => false,
|
||||
Err(_) => {
|
||||
unreachable!("id came from matches")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
fn from(other: String) -> Self {
|
||||
Self::String(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Value {
|
||||
fn from(other: bool) -> Self {
|
||||
Self::Bool(other)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
**This requires enabling the [`derive` feature flag][crate::_features].**
|
||||
*Jump to [source](git-derive.rs)*
|
||||
|
||||
**This requires enabling the `derive` feature flag.**
|
||||
|
||||
Git is an example of several common subcommand patterns.
|
||||
|
||||
|
@ -6,47 +8,51 @@ Help:
|
|||
```console
|
||||
$ git-derive
|
||||
? failed
|
||||
git
|
||||
A fictional versioning CLI
|
||||
|
||||
Usage: git-derive[EXE] <COMMAND>
|
||||
USAGE:
|
||||
git-derive[EXE] <SUBCOMMAND>
|
||||
|
||||
Commands:
|
||||
clone Clones repos
|
||||
diff Compare two commits
|
||||
push pushes things
|
||||
add adds things
|
||||
stash
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
SUBCOMMANDS:
|
||||
add adds things
|
||||
clone Clones repos
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
push pushes things
|
||||
stash
|
||||
|
||||
$ git-derive help
|
||||
git
|
||||
A fictional versioning CLI
|
||||
|
||||
Usage: git-derive[EXE] <COMMAND>
|
||||
USAGE:
|
||||
git-derive[EXE] <SUBCOMMAND>
|
||||
|
||||
Commands:
|
||||
clone Clones repos
|
||||
diff Compare two commits
|
||||
push pushes things
|
||||
add adds things
|
||||
stash
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
SUBCOMMANDS:
|
||||
add adds things
|
||||
clone Clones repos
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
push pushes things
|
||||
stash
|
||||
|
||||
$ git-derive help add
|
||||
git-derive[EXE]-add
|
||||
adds things
|
||||
|
||||
Usage: git-derive[EXE] add <PATH>...
|
||||
USAGE:
|
||||
git-derive[EXE] add <PATH>...
|
||||
|
||||
Arguments:
|
||||
<PATH>... Stuff to add
|
||||
ARGS:
|
||||
<PATH>... Stuff to add
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
|
||||
```
|
||||
|
||||
|
@ -54,15 +60,17 @@ A basic argument:
|
|||
```console
|
||||
$ git-derive add
|
||||
? failed
|
||||
git-derive[EXE]-add
|
||||
adds things
|
||||
|
||||
Usage: git-derive[EXE] add <PATH>...
|
||||
USAGE:
|
||||
git-derive[EXE] add <PATH>...
|
||||
|
||||
Arguments:
|
||||
<PATH>... Stuff to add
|
||||
ARGS:
|
||||
<PATH>... Stuff to add
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
|
||||
$ git-derive add Cargo.toml Cargo.lock
|
||||
Adding ["Cargo.toml", "Cargo.lock"]
|
||||
|
@ -72,43 +80,52 @@ Adding ["Cargo.toml", "Cargo.lock"]
|
|||
Default subcommand:
|
||||
```console
|
||||
$ git-derive stash -h
|
||||
Usage: git-derive[EXE] stash [OPTIONS]
|
||||
git-derive[EXE] stash <COMMAND>
|
||||
git-derive[EXE]-stash
|
||||
|
||||
Commands:
|
||||
push
|
||||
pop
|
||||
apply
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
USAGE:
|
||||
git-derive[EXE] stash [OPTIONS]
|
||||
git-derive[EXE] stash <SUBCOMMAND>
|
||||
|
||||
Options:
|
||||
-m, --message <MESSAGE>
|
||||
-h, --help Print help
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
-m, --message <MESSAGE>
|
||||
|
||||
SUBCOMMANDS:
|
||||
apply
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
pop
|
||||
push
|
||||
|
||||
$ git-derive stash push -h
|
||||
Usage: git-derive[EXE] stash push [OPTIONS]
|
||||
git-derive[EXE]-stash-push
|
||||
|
||||
Options:
|
||||
-m, --message <MESSAGE>
|
||||
-h, --help Print help
|
||||
USAGE:
|
||||
git-derive[EXE] stash push [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
-m, --message <MESSAGE>
|
||||
|
||||
$ git-derive stash pop -h
|
||||
Usage: git-derive[EXE] stash pop [STASH]
|
||||
git-derive[EXE]-stash-pop
|
||||
|
||||
Arguments:
|
||||
[STASH]
|
||||
USAGE:
|
||||
git-derive[EXE] stash pop [STASH]
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
ARGS:
|
||||
<STASH>
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
|
||||
$ git-derive stash -m "Prototype"
|
||||
Pushing StashPushArgs { message: Some("Prototype") }
|
||||
Pushing StashPush { message: Some("Prototype") }
|
||||
|
||||
$ git-derive stash pop
|
||||
Popping None
|
||||
|
||||
$ git-derive stash push -m "Prototype"
|
||||
Pushing StashPushArgs { message: Some("Prototype") }
|
||||
Pushing StashPush { message: Some("Prototype") }
|
||||
|
||||
$ git-derive stash pop
|
||||
Popping None
|
||||
|
@ -121,39 +138,3 @@ $ git-derive custom-tool arg1 --foo bar
|
|||
Calling out to "custom-tool" with ["arg1", "--foo", "bar"]
|
||||
|
||||
```
|
||||
|
||||
Last argument:
|
||||
```console
|
||||
$ git-derive diff --help
|
||||
Compare two commits
|
||||
|
||||
Usage: git-derive[EXE] diff [OPTIONS] [COMMIT] [COMMIT] [-- <PATH>]
|
||||
|
||||
Arguments:
|
||||
[COMMIT]
|
||||
[COMMIT]
|
||||
[PATH]
|
||||
|
||||
Options:
|
||||
--color[=<WHEN>] [default: auto] [possible values: always, auto, never]
|
||||
-h, --help Print help
|
||||
|
||||
$ git-derive diff
|
||||
Diffing stage..worktree (color=auto)
|
||||
|
||||
$ git-derive diff ./src
|
||||
Diffing stage..worktree ./src (color=auto)
|
||||
|
||||
$ git-derive diff HEAD ./src
|
||||
Diffing HEAD..worktree ./src (color=auto)
|
||||
|
||||
$ git-derive diff HEAD~~ -- HEAD
|
||||
Diffing HEAD~~..worktree HEAD (color=auto)
|
||||
|
||||
$ git-derive diff --color
|
||||
Diffing stage..worktree (color=always)
|
||||
|
||||
$ git-derive diff --color=never
|
||||
Diffing stage..worktree (color=never)
|
||||
|
||||
```
|
||||
|
|
|
@ -1,99 +1,65 @@
|
|||
use std::ffi::OsStr;
|
||||
// Note: this requires the `derive` feature
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{Args, Parser, Subcommand, ValueEnum};
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
|
||||
/// A fictional versioning CLI
|
||||
#[derive(Debug, Parser)] // requires `derive` feature
|
||||
#[command(name = "git")]
|
||||
#[command(about = "A fictional versioning CLI", long_about = None)]
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(name = "git")]
|
||||
#[clap(about = "A fictional versioning CLI", long_about = None)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
#[clap(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum Commands {
|
||||
/// Clones repos
|
||||
#[command(arg_required_else_help = true)]
|
||||
#[clap(arg_required_else_help = true)]
|
||||
Clone {
|
||||
/// The remote to clone
|
||||
remote: String,
|
||||
},
|
||||
/// Compare two commits
|
||||
Diff {
|
||||
#[arg(value_name = "COMMIT")]
|
||||
base: Option<OsString>,
|
||||
#[arg(value_name = "COMMIT")]
|
||||
head: Option<OsString>,
|
||||
#[arg(last = true)]
|
||||
path: Option<OsString>,
|
||||
#[arg(
|
||||
long,
|
||||
require_equals = true,
|
||||
value_name = "WHEN",
|
||||
num_args = 0..=1,
|
||||
default_value_t = ColorWhen::Auto,
|
||||
default_missing_value = "always",
|
||||
value_enum
|
||||
)]
|
||||
color: ColorWhen,
|
||||
},
|
||||
/// pushes things
|
||||
#[command(arg_required_else_help = true)]
|
||||
#[clap(arg_required_else_help = true)]
|
||||
Push {
|
||||
/// The remote to target
|
||||
remote: String,
|
||||
},
|
||||
/// adds things
|
||||
#[command(arg_required_else_help = true)]
|
||||
#[clap(arg_required_else_help = true)]
|
||||
Add {
|
||||
/// Stuff to add
|
||||
#[arg(required = true)]
|
||||
#[clap(required = true, parse(from_os_str))]
|
||||
path: Vec<PathBuf>,
|
||||
},
|
||||
Stash(StashArgs),
|
||||
#[command(external_subcommand)]
|
||||
Stash(Stash),
|
||||
#[clap(external_subcommand)]
|
||||
External(Vec<OsString>),
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum ColorWhen {
|
||||
Always,
|
||||
Auto,
|
||||
Never,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ColorWhen {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.to_possible_value()
|
||||
.expect("no values are skipped")
|
||||
.get_name()
|
||||
.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[command(args_conflicts_with_subcommands = true)]
|
||||
struct StashArgs {
|
||||
#[command(subcommand)]
|
||||
#[clap(args_conflicts_with_subcommands = true)]
|
||||
struct Stash {
|
||||
#[clap(subcommand)]
|
||||
command: Option<StashCommands>,
|
||||
|
||||
#[command(flatten)]
|
||||
push: StashPushArgs,
|
||||
#[clap(flatten)]
|
||||
push: StashPush,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum StashCommands {
|
||||
Push(StashPushArgs),
|
||||
Push(StashPush),
|
||||
Pop { stash: Option<String> },
|
||||
Apply { stash: Option<String> },
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct StashPushArgs {
|
||||
#[arg(short, long)]
|
||||
struct StashPush {
|
||||
#[clap(short, long)]
|
||||
message: Option<String>,
|
||||
}
|
||||
|
||||
|
@ -102,56 +68,25 @@ fn main() {
|
|||
|
||||
match args.command {
|
||||
Commands::Clone { remote } => {
|
||||
println!("Cloning {remote}");
|
||||
}
|
||||
Commands::Diff {
|
||||
mut base,
|
||||
mut head,
|
||||
mut path,
|
||||
color,
|
||||
} => {
|
||||
if path.is_none() {
|
||||
path = head;
|
||||
head = None;
|
||||
if path.is_none() {
|
||||
path = base;
|
||||
base = None;
|
||||
}
|
||||
}
|
||||
let base = base
|
||||
.as_deref()
|
||||
.map(|s| s.to_str().unwrap())
|
||||
.unwrap_or("stage");
|
||||
let head = head
|
||||
.as_deref()
|
||||
.map(|s| s.to_str().unwrap())
|
||||
.unwrap_or("worktree");
|
||||
let path = path.as_deref().unwrap_or_else(|| OsStr::new(""));
|
||||
println!(
|
||||
"Diffing {}..{} {} (color={})",
|
||||
base,
|
||||
head,
|
||||
path.to_string_lossy(),
|
||||
color
|
||||
);
|
||||
println!("Cloning {}", remote);
|
||||
}
|
||||
Commands::Push { remote } => {
|
||||
println!("Pushing to {remote}");
|
||||
println!("Pushing to {}", remote);
|
||||
}
|
||||
Commands::Add { path } => {
|
||||
println!("Adding {path:?}");
|
||||
println!("Adding {:?}", path);
|
||||
}
|
||||
Commands::Stash(stash) => {
|
||||
let stash_cmd = stash.command.unwrap_or(StashCommands::Push(stash.push));
|
||||
match stash_cmd {
|
||||
StashCommands::Push(push) => {
|
||||
println!("Pushing {push:?}");
|
||||
println!("Pushing {:?}", push);
|
||||
}
|
||||
StashCommands::Pop { stash } => {
|
||||
println!("Popping {stash:?}");
|
||||
println!("Popping {:?}", stash);
|
||||
}
|
||||
StashCommands::Apply { stash } => {
|
||||
println!("Applying {stash:?}");
|
||||
println!("Applying {:?}", stash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +1,56 @@
|
|||
*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] <COMMAND>
|
||||
USAGE:
|
||||
git[EXE] <SUBCOMMAND>
|
||||
|
||||
Commands:
|
||||
clone Clones repos
|
||||
diff Compare two commits
|
||||
push pushes things
|
||||
add adds things
|
||||
stash
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
SUBCOMMANDS:
|
||||
add adds things
|
||||
clone Clones repos
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
push pushes things
|
||||
stash
|
||||
|
||||
$ git help
|
||||
git
|
||||
A fictional versioning CLI
|
||||
|
||||
Usage: git[EXE] <COMMAND>
|
||||
USAGE:
|
||||
git[EXE] <SUBCOMMAND>
|
||||
|
||||
Commands:
|
||||
clone Clones repos
|
||||
diff Compare two commits
|
||||
push pushes things
|
||||
add adds things
|
||||
stash
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
SUBCOMMANDS:
|
||||
add adds things
|
||||
clone Clones repos
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
push pushes things
|
||||
stash
|
||||
|
||||
$ git help add
|
||||
git[EXE]-add
|
||||
adds things
|
||||
|
||||
Usage: git[EXE] add <PATH>...
|
||||
USAGE:
|
||||
git[EXE] add <PATH>...
|
||||
|
||||
Arguments:
|
||||
<PATH>... Stuff to add
|
||||
ARGS:
|
||||
<PATH>... Stuff to add
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
|
||||
```
|
||||
|
||||
|
@ -52,15 +58,17 @@ A basic argument:
|
|||
```console
|
||||
$ git add
|
||||
? failed
|
||||
git[EXE]-add
|
||||
adds things
|
||||
|
||||
Usage: git[EXE] add <PATH>...
|
||||
USAGE:
|
||||
git[EXE] add <PATH>...
|
||||
|
||||
Arguments:
|
||||
<PATH>... Stuff to add
|
||||
ARGS:
|
||||
<PATH>... Stuff to add
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
|
||||
$ git add Cargo.toml Cargo.lock
|
||||
Adding ["Cargo.toml", "Cargo.lock"]
|
||||
|
@ -70,34 +78,43 @@ Adding ["Cargo.toml", "Cargo.lock"]
|
|||
Default subcommand:
|
||||
```console
|
||||
$ git stash -h
|
||||
Usage: git[EXE] stash [OPTIONS]
|
||||
git[EXE] stash <COMMAND>
|
||||
git[EXE]-stash
|
||||
|
||||
Commands:
|
||||
push
|
||||
pop
|
||||
apply
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
USAGE:
|
||||
git[EXE] stash [OPTIONS]
|
||||
git[EXE] stash <SUBCOMMAND>
|
||||
|
||||
Options:
|
||||
-m, --message <MESSAGE>
|
||||
-h, --help Print help
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
-m, --message <MESSAGE>
|
||||
|
||||
SUBCOMMANDS:
|
||||
apply
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
pop
|
||||
push
|
||||
|
||||
$ git stash push -h
|
||||
Usage: git[EXE] stash push [OPTIONS]
|
||||
git[EXE]-stash-push
|
||||
|
||||
Options:
|
||||
-m, --message <MESSAGE>
|
||||
-h, --help Print help
|
||||
USAGE:
|
||||
git[EXE] stash push [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
-m, --message <MESSAGE>
|
||||
|
||||
$ git stash pop -h
|
||||
Usage: git[EXE] stash pop [STASH]
|
||||
git[EXE]-stash-pop
|
||||
|
||||
Arguments:
|
||||
[STASH]
|
||||
USAGE:
|
||||
git[EXE] stash pop [STASH]
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
ARGS:
|
||||
<STASH>
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
|
||||
$ git stash -m "Prototype"
|
||||
Pushing Some("Prototype")
|
||||
|
@ -119,39 +136,3 @@ $ git custom-tool arg1 --foo bar
|
|||
Calling out to "custom-tool" with ["arg1", "--foo", "bar"]
|
||||
|
||||
```
|
||||
|
||||
Last argument:
|
||||
```console
|
||||
$ git diff --help
|
||||
Compare two commits
|
||||
|
||||
Usage: git[EXE] diff [OPTIONS] [COMMIT] [COMMIT] [-- <PATH>]
|
||||
|
||||
Arguments:
|
||||
[COMMIT]
|
||||
[COMMIT]
|
||||
[PATH]
|
||||
|
||||
Options:
|
||||
--color[=<WHEN>] [default: auto] [possible values: always, auto, never]
|
||||
-h, --help Print help
|
||||
|
||||
$ git diff
|
||||
Diffing stage..worktree (color=auto)
|
||||
|
||||
$ git diff ./src
|
||||
Diffing stage..worktree ./src (color=auto)
|
||||
|
||||
$ git diff HEAD ./src
|
||||
Diffing HEAD..worktree ./src (color=auto)
|
||||
|
||||
$ git diff HEAD~~ -- HEAD
|
||||
Diffing HEAD~~..worktree HEAD (color=auto)
|
||||
|
||||
$ git diff --color
|
||||
Diffing stage..worktree (color=always)
|
||||
|
||||
$ git diff --color=never
|
||||
Diffing stage..worktree (color=never)
|
||||
|
||||
```
|
||||
|
|
|
@ -1,35 +1,22 @@
|
|||
use std::ffi::OsString;
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{arg, Command};
|
||||
|
||||
fn cli() -> Command {
|
||||
fn cli() -> Command<'static> {
|
||||
Command::new("git")
|
||||
.about("A fictional versioning CLI")
|
||||
.subcommand_required(true)
|
||||
.arg_required_else_help(true)
|
||||
.allow_external_subcommands(true)
|
||||
.allow_invalid_utf8_for_external_subcommands(true)
|
||||
.subcommand(
|
||||
Command::new("clone")
|
||||
.about("Clones repos")
|
||||
.arg(arg!(<REMOTE> "The remote to clone"))
|
||||
.arg_required_else_help(true),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("diff")
|
||||
.about("Compare two commits")
|
||||
.arg(arg!(base: [COMMIT]))
|
||||
.arg(arg!(head: [COMMIT]))
|
||||
.arg(arg!(path: [PATH]).last(true))
|
||||
.arg(
|
||||
arg!(--color <WHEN>)
|
||||
.value_parser(["always", "auto", "never"])
|
||||
.num_args(0..=1)
|
||||
.require_equals(true)
|
||||
.default_value("auto")
|
||||
.default_missing_value("always"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("push")
|
||||
.about("pushes things")
|
||||
|
@ -40,7 +27,7 @@ fn cli() -> Command {
|
|||
Command::new("add")
|
||||
.about("adds things")
|
||||
.arg_required_else_help(true)
|
||||
.arg(arg!(<PATH> ... "Stuff to add").value_parser(clap::value_parser!(PathBuf))),
|
||||
.arg(arg!(<PATH> ... "Stuff to add").allow_invalid_utf8(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("stash")
|
||||
|
@ -52,8 +39,8 @@ fn cli() -> Command {
|
|||
)
|
||||
}
|
||||
|
||||
fn push_args() -> Vec<clap::Arg> {
|
||||
vec![arg!(-m --message <MESSAGE>)]
|
||||
fn push_args() -> Vec<clap::Arg<'static>> {
|
||||
vec![arg!(-m --message <MESSAGE>).required(false)]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -63,59 +50,37 @@ fn main() {
|
|||
Some(("clone", sub_matches)) => {
|
||||
println!(
|
||||
"Cloning {}",
|
||||
sub_matches.get_one::<String>("REMOTE").expect("required")
|
||||
sub_matches.value_of("REMOTE").expect("required")
|
||||
);
|
||||
}
|
||||
Some(("diff", sub_matches)) => {
|
||||
let color = sub_matches
|
||||
.get_one::<String>("color")
|
||||
.map(|s| s.as_str())
|
||||
.expect("defaulted in clap");
|
||||
|
||||
let mut base = sub_matches.get_one::<String>("base").map(|s| s.as_str());
|
||||
let mut head = sub_matches.get_one::<String>("head").map(|s| s.as_str());
|
||||
let mut path = sub_matches.get_one::<String>("path").map(|s| s.as_str());
|
||||
if path.is_none() {
|
||||
path = head;
|
||||
head = None;
|
||||
if path.is_none() {
|
||||
path = base;
|
||||
base = None;
|
||||
}
|
||||
}
|
||||
let base = base.unwrap_or("stage");
|
||||
let head = head.unwrap_or("worktree");
|
||||
let path = path.unwrap_or("");
|
||||
println!("Diffing {base}..{head} {path} (color={color})");
|
||||
}
|
||||
Some(("push", sub_matches)) => {
|
||||
println!(
|
||||
"Pushing to {}",
|
||||
sub_matches.get_one::<String>("REMOTE").expect("required")
|
||||
sub_matches.value_of("REMOTE").expect("required")
|
||||
);
|
||||
}
|
||||
Some(("add", sub_matches)) => {
|
||||
let paths = sub_matches
|
||||
.get_many::<PathBuf>("PATH")
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.values_of_os("PATH")
|
||||
.unwrap_or_default()
|
||||
.map(PathBuf::from)
|
||||
.collect::<Vec<_>>();
|
||||
println!("Adding {paths:?}");
|
||||
println!("Adding {:?}", paths);
|
||||
}
|
||||
Some(("stash", sub_matches)) => {
|
||||
let stash_command = sub_matches.subcommand().unwrap_or(("push", sub_matches));
|
||||
match stash_command {
|
||||
("apply", sub_matches) => {
|
||||
let stash = sub_matches.get_one::<String>("STASH");
|
||||
println!("Applying {stash:?}");
|
||||
let stash = sub_matches.value_of("STASH");
|
||||
println!("Applying {:?}", stash);
|
||||
}
|
||||
("pop", sub_matches) => {
|
||||
let stash = sub_matches.get_one::<String>("STASH");
|
||||
println!("Popping {stash:?}");
|
||||
let stash = sub_matches.value_of("STASH");
|
||||
println!("Popping {:?}", stash);
|
||||
}
|
||||
("push", sub_matches) => {
|
||||
let message = sub_matches.get_one::<String>("message");
|
||||
println!("Pushing {message:?}");
|
||||
let message = sub_matches.value_of("message");
|
||||
println!("Pushing {:?}", message);
|
||||
}
|
||||
(name, _) => {
|
||||
unreachable!("Unsupported subcommand `{}`", name)
|
||||
|
@ -124,13 +89,12 @@ fn main() {
|
|||
}
|
||||
Some((ext, sub_matches)) => {
|
||||
let args = sub_matches
|
||||
.get_many::<OsString>("")
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.values_of_os("")
|
||||
.unwrap_or_default()
|
||||
.collect::<Vec<_>>();
|
||||
println!("Calling out to {ext:?} with {args:?}");
|
||||
println!("Calling out to {:?} with {:?}", ext, args);
|
||||
}
|
||||
_ => unreachable!(), // If all subcommands are defined above, anything else is unreachable!()
|
||||
_ => unreachable!(), // If all subcommands are defined above, anything else is unreachabe!()
|
||||
}
|
||||
|
||||
// Continued program logic goes here...
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
See the documentation for [`Command::multicall`][crate::Command::multicall] for rationale.
|
||||
*Jump to [source](multicall-busybox.rs)*
|
||||
|
||||
Example of a busybox-style multicall program
|
||||
|
||||
See the documentation for `clap::Command::multicall` for rationale.
|
||||
|
||||
This example omits every command except true and false,
|
||||
which are the most trivial to implement,
|
||||
|
@ -25,15 +29,18 @@ Though users must pass something:
|
|||
```console
|
||||
$ busybox
|
||||
? failed
|
||||
Usage: busybox [OPTIONS] [APPLET]
|
||||
busybox
|
||||
|
||||
USAGE:
|
||||
busybox [OPTIONS] [APPLET]
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
--install <install> Install hardlinks for all subcommands in path
|
||||
|
||||
APPLETS:
|
||||
true does nothing successfully
|
||||
false does nothing unsuccessfully
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
Options:
|
||||
--install <install> Install hardlinks for all subcommands in path
|
||||
-h, --help Print help
|
||||
false does nothing unsuccessfully
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
true does nothing successfully
|
||||
|
||||
```
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::path::PathBuf;
|
||||
// Note: this requires the `unstable-multicall` feature
|
||||
|
||||
use std::process::exit;
|
||||
|
||||
use clap::{value_parser, Arg, ArgAction, Command};
|
||||
use clap::{Arg, Command};
|
||||
|
||||
fn applet_commands() -> [Command; 2] {
|
||||
fn applet_commands() -> [Command<'static>; 2] {
|
||||
[
|
||||
Command::new("true").about("does nothing successfully"),
|
||||
Command::new("false").about("does nothing unsuccessfully"),
|
||||
|
@ -23,9 +24,9 @@ fn main() {
|
|||
.long("install")
|
||||
.help("Install hardlinks for all subcommands in path")
|
||||
.exclusive(true)
|
||||
.action(ArgAction::Set)
|
||||
.takes_value(true)
|
||||
.default_missing_value("/usr/local/bin")
|
||||
.value_parser(value_parser!(PathBuf)),
|
||||
.use_value_delimiter(false),
|
||||
)
|
||||
.subcommands(applet_commands()),
|
||||
)
|
||||
|
@ -34,7 +35,7 @@ fn main() {
|
|||
let matches = cmd.get_matches();
|
||||
let mut subcommand = matches.subcommand();
|
||||
if let Some(("busybox", cmd)) = subcommand {
|
||||
if cmd.contains_id("install") {
|
||||
if cmd.occurrences_of("install") > 0 {
|
||||
unimplemented!("Make hardlinks to the executable here");
|
||||
}
|
||||
subcommand = cmd.subcommand();
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
See the documentation for [`Command::multicall`][crate::Command::multicall] for rationale.
|
||||
*Jump to [source](multicall-hostname.rs)*
|
||||
|
||||
Example of a `hostname-style` multicall program
|
||||
|
||||
See the documentation for `clap::Command::multicall` for rationale.
|
||||
|
||||
This example omits the implementation of displaying address config
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// Note: this requires the `unstable-multicall` feature
|
||||
|
||||
use clap::Command;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
*Jump to [source](pacman.rs)*
|
||||
|
||||
[`pacman`](https://wiki.archlinux.org/index.php/pacman) defines subcommands via flags.
|
||||
|
||||
Here, `-S` is a short flag subcommand:
|
||||
|
@ -35,31 +37,36 @@ Searching for name...
|
|||
In the help, this looks like:
|
||||
```console
|
||||
$ pacman -h
|
||||
pacman 5.2.1
|
||||
Pacman Development Team
|
||||
package manager utility
|
||||
|
||||
Usage: pacman[EXE] <COMMAND>
|
||||
USAGE:
|
||||
pacman[EXE] <SUBCOMMAND>
|
||||
|
||||
Commands:
|
||||
query, -Q, --query Query the package database.
|
||||
sync, -S, --sync Synchronize packages.
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
-V, --version Print version information
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
SUBCOMMANDS:
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
query -Q --query Query the package database.
|
||||
sync -S --sync Synchronize packages.
|
||||
|
||||
$ pacman -S -h
|
||||
pacman[EXE]-sync
|
||||
Synchronize packages.
|
||||
|
||||
Usage: pacman[EXE] {sync|--sync|-S} [OPTIONS] [package]...
|
||||
USAGE:
|
||||
pacman[EXE] {sync|--sync|-S} [OPTIONS] [--] [package]...
|
||||
|
||||
Arguments:
|
||||
[package]... packages
|
||||
ARGS:
|
||||
<package>... packages
|
||||
|
||||
Options:
|
||||
-s, --search <search>... search remote repositories for matching strings
|
||||
-i, --info view package information
|
||||
-h, --help Print help
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
-i, --info view package information
|
||||
-s, --search <search>... search remote repositories for matching strings
|
||||
|
||||
```
|
||||
|
||||
|
@ -67,11 +74,12 @@ And errors:
|
|||
```console
|
||||
$ pacman -S -s foo -i bar
|
||||
? failed
|
||||
error: the argument '--search <search>...' cannot be used with '--info'
|
||||
error: The argument '--search <search>...' cannot be used with '--info'
|
||||
|
||||
Usage: pacman[EXE] {sync|--sync|-S} --search <search>... <package>...
|
||||
USAGE:
|
||||
pacman[EXE] {sync|--sync|-S} --search <search>... <package>...
|
||||
|
||||
For more information, try '--help'.
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use clap::{Arg, ArgAction, Command};
|
||||
use clap::{Arg, Command};
|
||||
|
||||
fn main() {
|
||||
let matches = Command::new("pacman")
|
||||
|
@ -21,8 +21,8 @@ fn main() {
|
|||
.long("search")
|
||||
.help("search locally installed packages for matching strings")
|
||||
.conflicts_with("info")
|
||||
.action(ArgAction::Set)
|
||||
.num_args(1..),
|
||||
.takes_value(true)
|
||||
.multiple_values(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("info")
|
||||
|
@ -30,8 +30,8 @@ fn main() {
|
|||
.short('i')
|
||||
.conflicts_with("search")
|
||||
.help("view package information")
|
||||
.action(ArgAction::Set)
|
||||
.num_args(1..),
|
||||
.takes_value(true)
|
||||
.multiple_values(true),
|
||||
),
|
||||
)
|
||||
// Sync subcommand
|
||||
|
@ -47,8 +47,8 @@ fn main() {
|
|||
.short('s')
|
||||
.long("search")
|
||||
.conflicts_with("info")
|
||||
.action(ArgAction::Set)
|
||||
.num_args(1..)
|
||||
.takes_value(true)
|
||||
.multiple_values(true)
|
||||
.help("search remote repositories for matching strings"),
|
||||
)
|
||||
.arg(
|
||||
|
@ -56,52 +56,43 @@ fn main() {
|
|||
.long("info")
|
||||
.conflicts_with("search")
|
||||
.short('i')
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("view package information"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("package")
|
||||
.help("packages")
|
||||
.required_unless_present("search")
|
||||
.action(ArgAction::Set)
|
||||
.num_args(1..),
|
||||
.takes_value(true)
|
||||
.multiple_values(true),
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
match matches.subcommand() {
|
||||
Some(("sync", sync_matches)) => {
|
||||
if sync_matches.contains_id("search") {
|
||||
let packages: Vec<_> = sync_matches
|
||||
.get_many::<String>("search")
|
||||
.expect("contains_id")
|
||||
.map(|s| s.as_str())
|
||||
.collect();
|
||||
if sync_matches.is_present("search") {
|
||||
let packages: Vec<_> = sync_matches.values_of("search").unwrap().collect();
|
||||
let values = packages.join(", ");
|
||||
println!("Searching for {values}...");
|
||||
println!("Searching for {}...", values);
|
||||
return;
|
||||
}
|
||||
|
||||
let packages: Vec<_> = sync_matches
|
||||
.get_many::<String>("package")
|
||||
.expect("is present")
|
||||
.map(|s| s.as_str())
|
||||
.collect();
|
||||
let packages: Vec<_> = sync_matches.values_of("package").unwrap().collect();
|
||||
let values = packages.join(", ");
|
||||
|
||||
if sync_matches.get_flag("info") {
|
||||
println!("Retrieving info for {values}...");
|
||||
if sync_matches.is_present("info") {
|
||||
println!("Retrieving info for {}...", values);
|
||||
} else {
|
||||
println!("Installing {values}...");
|
||||
println!("Installing {}...", values);
|
||||
}
|
||||
}
|
||||
Some(("query", query_matches)) => {
|
||||
if let Some(packages) = query_matches.get_many::<String>("info") {
|
||||
let comma_sep = packages.map(|s| s.as_str()).collect::<Vec<_>>().join(", ");
|
||||
println!("Retrieving info for {comma_sep}...");
|
||||
} else if let Some(queries) = query_matches.get_many::<String>("search") {
|
||||
let comma_sep = queries.map(|s| s.as_str()).collect::<Vec<_>>().join(", ");
|
||||
println!("Searching Locally for {comma_sep}...");
|
||||
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...");
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// Note: this requires the `unstable-multicall` feature
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
use clap::Command;
|
||||
|
@ -17,7 +19,7 @@ fn main() -> Result<(), String> {
|
|||
}
|
||||
}
|
||||
Err(err) => {
|
||||
write!(std::io::stdout(), "{err}").map_err(|e| e.to_string())?;
|
||||
write!(std::io::stdout(), "{}", err).map_err(|e| e.to_string())?;
|
||||
std::io::stdout().flush().map_err(|e| e.to_string())?;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +31,7 @@ fn main() -> Result<(), String> {
|
|||
fn respond(line: &str) -> Result<bool, String> {
|
||||
let args = shlex::split(line).ok_or("error: Invalid quoting")?;
|
||||
let matches = cli()
|
||||
.try_get_matches_from(args)
|
||||
.try_get_matches_from(&args)
|
||||
.map_err(|e| e.to_string())?;
|
||||
match matches.subcommand() {
|
||||
Some(("ping", _matches)) => {
|
||||
|
@ -48,7 +50,7 @@ fn respond(line: &str) -> Result<bool, String> {
|
|||
Ok(false)
|
||||
}
|
||||
|
||||
fn cli() -> Command {
|
||||
fn cli() -> Command<'static> {
|
||||
// strip out usage
|
||||
const PARSER_TEMPLATE: &str = "\
|
||||
{all-args}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
```console
|
||||
$ 01_quick --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 01_quick[EXE] [OPTIONS] [name] [COMMAND]
|
||||
|
||||
Commands:
|
||||
test does testing things
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
Arguments:
|
||||
[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
|
||||
-V, --version Print version
|
||||
|
||||
```
|
||||
|
||||
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...
|
||||
|
||||
```
|
|
@ -1,9 +1,10 @@
|
|||
use std::path::PathBuf;
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command, value_parser, ArgAction, Command};
|
||||
use clap::{arg, command, Command};
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
let matches = command!()
|
||||
.arg(arg!([name] "Optional name to operate on"))
|
||||
.arg(
|
||||
arg!(
|
||||
|
@ -11,7 +12,8 @@ fn main() {
|
|||
)
|
||||
// We don't have syntax yet for optional options, so manually calling `required`
|
||||
.required(false)
|
||||
.value_parser(value_parser!(PathBuf)),
|
||||
// Support non-UTF8 paths
|
||||
.allow_invalid_utf8(true),
|
||||
)
|
||||
.arg(arg!(
|
||||
-d --debug ... "Turn debugging information on"
|
||||
|
@ -19,25 +21,23 @@ fn main() {
|
|||
.subcommand(
|
||||
Command::new("test")
|
||||
.about("does testing things")
|
||||
.arg(arg!(-l --list "lists test values").action(ArgAction::SetTrue)),
|
||||
.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.get_one::<String>("name") {
|
||||
if let Some(name) = matches.value_of("name") {
|
||||
println!("Value for name: {}", name);
|
||||
}
|
||||
|
||||
if let Some(config_path) = matches.get_one::<PathBuf>("config") {
|
||||
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
|
||||
.get_one::<u8>("debug")
|
||||
.expect("Count's are defaulted")
|
||||
{
|
||||
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"),
|
||||
|
@ -48,7 +48,7 @@ fn main() {
|
|||
// matches just as you would the top level cmd
|
||||
if let Some(matches) = matches.subcommand_matches("test") {
|
||||
// "$ myapp test" was run
|
||||
if matches.get_flag("list") {
|
||||
if matches.is_present("list") {
|
||||
// "$ myapp test -l" was run
|
||||
println!("Printing testing lists...");
|
||||
} else {
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
```console
|
||||
$ 02_app_settings --help
|
||||
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
|
||||
-V, --version
|
||||
Print version
|
||||
|
||||
```
|
|
@ -1,18 +1,16 @@
|
|||
use clap::{arg, command, ArgAction};
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command, AppSettings};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
.next_line_help(true)
|
||||
.arg(arg!(--two <VALUE>).required(true).action(ArgAction::Set))
|
||||
.arg(arg!(--one <VALUE>).required(true).action(ArgAction::Set))
|
||||
let matches = command!()
|
||||
.args_override_self(true)
|
||||
.global_setting(AppSettings::DeriveDisplayOrder)
|
||||
.allow_negative_numbers(true)
|
||||
.arg(arg!(--two <VALUE>))
|
||||
.arg(arg!(--one <VALUE>))
|
||||
.get_matches();
|
||||
|
||||
println!(
|
||||
"two: {:?}",
|
||||
matches.get_one::<String>("two").expect("required")
|
||||
);
|
||||
println!(
|
||||
"one: {:?}",
|
||||
matches.get_one::<String>("one").expect("required")
|
||||
);
|
||||
println!("two: {:?}", matches.value_of("two").expect("required"));
|
||||
println!("one: {:?}", matches.value_of("one").expect("required"));
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
```console
|
||||
$ 02_apps --help
|
||||
Does awesome things
|
||||
|
||||
Usage: 02_apps[EXE] --two <VALUE> --one <VALUE>
|
||||
|
||||
Options:
|
||||
--two <VALUE>
|
||||
--one <VALUE>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 02_apps --version
|
||||
MyApp 1.0
|
||||
|
||||
```
|
|
@ -5,16 +5,10 @@ fn main() {
|
|||
.version("1.0")
|
||||
.author("Kevin K. <kbknapp@gmail.com>")
|
||||
.about("Does awesome things")
|
||||
.arg(arg!(--two <VALUE>).required(true))
|
||||
.arg(arg!(--one <VALUE>).required(true))
|
||||
.arg(arg!(--two <VALUE>))
|
||||
.arg(arg!(--one <VALUE>))
|
||||
.get_matches();
|
||||
|
||||
println!(
|
||||
"two: {:?}",
|
||||
matches.get_one::<String>("two").expect("required")
|
||||
);
|
||||
println!(
|
||||
"one: {:?}",
|
||||
matches.get_one::<String>("one").expect("required")
|
||||
);
|
||||
println!("two: {:?}", matches.value_of("two").expect("required"));
|
||||
println!("one: {:?}", matches.value_of("one").expect("required"));
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
```console
|
||||
$ 02_crate --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 02_crate[EXE] --two <VALUE> --one <VALUE>
|
||||
|
||||
Options:
|
||||
--two <VALUE>
|
||||
--one <VALUE>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 02_crate --version
|
||||
clap [..]
|
||||
|
||||
```
|
|
@ -1,18 +1,13 @@
|
|||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
// requires `cargo` feature, reading name, version, author, and description from `Cargo.toml`
|
||||
let matches = command!()
|
||||
.arg(arg!(--two <VALUE>).required(true))
|
||||
.arg(arg!(--one <VALUE>).required(true))
|
||||
.arg(arg!(--two <VALUE>))
|
||||
.arg(arg!(--one <VALUE>))
|
||||
.get_matches();
|
||||
|
||||
println!(
|
||||
"two: {:?}",
|
||||
matches.get_one::<String>("two").expect("required")
|
||||
);
|
||||
println!(
|
||||
"one: {:?}",
|
||||
matches.get_one::<String>("one").expect("required")
|
||||
);
|
||||
println!("two: {:?}", matches.value_of("two").expect("required"));
|
||||
println!("one: {:?}", matches.value_of("one").expect("required"));
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
```console
|
||||
$ 03_01_flag_bool --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 03_01_flag_bool[EXE] [OPTIONS]
|
||||
|
||||
Options:
|
||||
-v, --verbose
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 03_01_flag_bool
|
||||
verbose: false
|
||||
|
||||
$ 03_01_flag_bool --verbose
|
||||
verbose: true
|
||||
|
||||
$ 03_01_flag_bool --verbose --verbose
|
||||
? failed
|
||||
error: the argument '--verbose' cannot be used multiple times
|
||||
|
||||
Usage: 03_01_flag_bool[EXE] [OPTIONS]
|
||||
|
||||
For more information, try '--help'.
|
||||
|
||||
```
|
|
@ -1,14 +1,9 @@
|
|||
use clap::{command, Arg, ArgAction};
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
.arg(
|
||||
Arg::new("verbose")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.get_matches();
|
||||
let matches = command!().arg(arg!(-v - -verbose)).get_matches();
|
||||
|
||||
println!("verbose: {:?}", matches.get_flag("verbose"));
|
||||
println!("verbose: {:?}", matches.is_present("verbose"));
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
```console
|
||||
$ 03_01_flag_count --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 03_01_flag_count[EXE] [OPTIONS]
|
||||
|
||||
Options:
|
||||
-v, --verbose...
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 03_01_flag_count
|
||||
verbose: 0
|
||||
|
||||
$ 03_01_flag_count --verbose
|
||||
verbose: 1
|
||||
|
||||
$ 03_01_flag_count --verbose --verbose
|
||||
verbose: 2
|
||||
|
||||
```
|
|
@ -1,14 +1,9 @@
|
|||
use clap::{command, Arg, ArgAction};
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
.arg(
|
||||
Arg::new("verbose")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::Count),
|
||||
)
|
||||
.get_matches();
|
||||
let matches = command!().arg(arg!(-v --verbose ...)).get_matches();
|
||||
|
||||
println!("verbose: {:?}", matches.get_count("verbose"));
|
||||
println!("verbose: {:?}", matches.occurrences_of("verbose"));
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
```console
|
||||
$ 03_02_option --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 03_02_option[EXE] [OPTIONS]
|
||||
|
||||
Options:
|
||||
-n, --name <name>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 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")
|
||||
|
||||
```
|
|
@ -1,9 +1,11 @@
|
|||
use clap::{command, Arg};
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
.arg(Arg::new("name").short('n').long("name"))
|
||||
let matches = command!()
|
||||
.arg(arg!(-n --name <NAME>).required(false))
|
||||
.get_matches();
|
||||
|
||||
println!("name: {:?}", matches.get_one::<String>("name"));
|
||||
println!("name: {:?}", matches.value_of("name"));
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
```console
|
||||
$ 03_02_option_mult --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 03_02_option_mult[EXE] [OPTIONS]
|
||||
|
||||
Options:
|
||||
-n, --name <name>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 03_02_option_mult
|
||||
name: None
|
||||
|
||||
$ 03_02_option_mult --name bob
|
||||
name: Some("bob")
|
||||
|
||||
$ 03_02_option_mult --name=bob
|
||||
name: Some("bob")
|
||||
|
||||
$ 03_02_option_mult -n bob
|
||||
name: Some("bob")
|
||||
|
||||
$ 03_02_option_mult -n=bob
|
||||
name: Some("bob")
|
||||
|
||||
$ 03_02_option_mult -nbob
|
||||
name: Some("bob")
|
||||
|
||||
```
|
|
@ -1,14 +0,0 @@
|
|||
use clap::{command, Arg, ArgAction};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
.arg(
|
||||
Arg::new("name")
|
||||
.short('n')
|
||||
.long("name")
|
||||
.action(ArgAction::Append),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
println!("name: {:?}", matches.get_one::<String>("name"));
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
```console
|
||||
$ 03_03_positional --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 03_03_positional[EXE] [name]
|
||||
|
||||
Arguments:
|
||||
[name]
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 03_03_positional
|
||||
name: None
|
||||
|
||||
$ 03_03_positional bob
|
||||
name: Some("bob")
|
||||
|
||||
```
|
|
@ -1,9 +1,9 @@
|
|||
use clap::{command, Arg};
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
.arg(Arg::new("name"))
|
||||
.get_matches();
|
||||
let matches = command!().arg(arg!([NAME])).get_matches();
|
||||
|
||||
println!("name: {:?}", matches.get_one::<String>("name"));
|
||||
println!("NAME: {:?}", matches.value_of("NAME"));
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
```console
|
||||
$ 03_03_positional_mult --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 03_03_positional_mult[EXE] [name]...
|
||||
|
||||
Arguments:
|
||||
[name]...
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 03_03_positional_mult
|
||||
names: []
|
||||
|
||||
$ 03_03_positional_mult bob
|
||||
names: ["bob"]
|
||||
|
||||
$ 03_03_positional_mult bob john
|
||||
names: ["bob", "john"]
|
||||
|
||||
```
|
|
@ -1,15 +0,0 @@
|
|||
use clap::{command, Arg, ArgAction};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
.arg(Arg::new("name").action(ArgAction::Append))
|
||||
.get_matches();
|
||||
|
||||
let args = matches
|
||||
.get_many::<String>("name")
|
||||
.unwrap_or_default()
|
||||
.map(|v| v.as_str())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
println!("names: {:?}", &args);
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
```console
|
||||
$ 03_04_subcommands help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 03_04_subcommands[EXE] <COMMAND>
|
||||
|
||||
Commands:
|
||||
add Adds files to myapp
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 03_04_subcommands help add
|
||||
Adds files to myapp
|
||||
|
||||
Usage: 03_04_subcommands[EXE] add [NAME]
|
||||
|
||||
Arguments:
|
||||
[NAME]
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 03_04_subcommands add bob
|
||||
'myapp add' was used, name is: Some("bob")
|
||||
|
||||
```
|
||||
|
||||
Because we set [`Command::arg_required_else_help`][crate::Command::arg_required_else_help]:
|
||||
```console
|
||||
$ 03_04_subcommands
|
||||
? failed
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 03_04_subcommands[EXE] <COMMAND>
|
||||
|
||||
Commands:
|
||||
add Adds files to myapp
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
```
|
||||
|
||||
Because we set [`Command::propagate_version`][crate::Command::propagate_version]:
|
||||
```console
|
||||
$ 03_04_subcommands --version
|
||||
clap [..]
|
||||
|
||||
$ 03_04_subcommands add --version
|
||||
clap-add [..]
|
||||
|
||||
```
|
|
@ -1,7 +1,9 @@
|
|||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command, Command};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
let matches = command!()
|
||||
.propagate_version(true)
|
||||
.subcommand_required(true)
|
||||
.arg_required_else_help(true)
|
||||
|
@ -15,7 +17,7 @@ fn main() {
|
|||
match matches.subcommand() {
|
||||
Some(("add", sub_matches)) => println!(
|
||||
"'myapp add' was used, name is: {:?}",
|
||||
sub_matches.get_one::<String>("NAME")
|
||||
sub_matches.value_of("NAME")
|
||||
),
|
||||
_ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"),
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
```console
|
||||
$ 03_05_default_values --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 03_05_default_values[EXE] [PORT]
|
||||
|
||||
Arguments:
|
||||
[PORT] [default: 2020]
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 03_05_default_values
|
||||
port: 2020
|
||||
|
||||
$ 03_05_default_values 22
|
||||
port: 22
|
||||
|
||||
```
|
|
@ -1,18 +1,16 @@
|
|||
use clap::{arg, command, value_parser};
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
.arg(
|
||||
arg!([PORT])
|
||||
.value_parser(value_parser!(u16))
|
||||
.default_value("2020"),
|
||||
)
|
||||
let matches = command!()
|
||||
.arg(arg!([NAME]).default_value("alice"))
|
||||
.get_matches();
|
||||
|
||||
println!(
|
||||
"port: {:?}",
|
||||
"NAME: {:?}",
|
||||
matches
|
||||
.get_one::<u16>("PORT")
|
||||
.value_of("NAME")
|
||||
.expect("default ensures there is always a value")
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
```console
|
||||
$ 04_01_enum --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 04_01_enum[EXE] <MODE>
|
||||
|
||||
Arguments:
|
||||
<MODE>
|
||||
What mode to run the program in
|
||||
|
||||
Possible values:
|
||||
- fast: Run swiftly
|
||||
- slow: Crawl slowly but steadily
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help (see a summary with '-h')
|
||||
|
||||
-V, --version
|
||||
Print version
|
||||
|
||||
$ 04_01_enum -h
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 04_01_enum[EXE] <MODE>
|
||||
|
||||
Arguments:
|
||||
<MODE> What mode to run the program in [possible values: fast, slow]
|
||||
|
||||
Options:
|
||||
-h, --help Print help (see more with '--help')
|
||||
-V, --version Print version
|
||||
|
||||
$ 04_01_enum fast
|
||||
Hare
|
||||
|
||||
$ 04_01_enum slow
|
||||
Tortoise
|
||||
|
||||
$ 04_01_enum medium
|
||||
? failed
|
||||
error: invalid value 'medium' for '<MODE>'
|
||||
[possible values: fast, slow]
|
||||
|
||||
For more information, try '--help'.
|
||||
|
||||
```
|
|
@ -1,22 +1,18 @@
|
|||
use clap::{arg, builder::PossibleValue, command, value_parser, ValueEnum};
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
use clap::{arg, command, ArgEnum, PossibleValue};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ArgEnum)]
|
||||
enum Mode {
|
||||
Fast,
|
||||
Slow,
|
||||
}
|
||||
|
||||
// Can also be derived with feature flag `derive`
|
||||
impl ValueEnum for Mode {
|
||||
fn value_variants<'a>() -> &'a [Self] {
|
||||
&[Mode::Fast, Mode::Slow]
|
||||
}
|
||||
|
||||
fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
|
||||
Some(match self {
|
||||
Mode::Fast => PossibleValue::new("fast").help("Run swiftly"),
|
||||
Mode::Slow => PossibleValue::new("slow").help("Crawl slowly but steadily"),
|
||||
})
|
||||
impl Mode {
|
||||
pub fn possible_values() -> impl Iterator<Item = PossibleValue<'static>> {
|
||||
Mode::value_variants()
|
||||
.iter()
|
||||
.filter_map(ArgEnum::to_possible_value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,22 +34,22 @@ impl std::str::FromStr for Mode {
|
|||
return Ok(*variant);
|
||||
}
|
||||
}
|
||||
Err(format!("invalid variant: {}", s))
|
||||
Err(format!("Invalid variant: {}", s))
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
let matches = command!()
|
||||
.arg(
|
||||
arg!(<MODE>)
|
||||
.help("What mode to run the program in")
|
||||
.value_parser(value_parser!(Mode)),
|
||||
.possible_values(Mode::possible_values()),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
// Note, it's safe to call unwrap() because the arg is required
|
||||
match matches
|
||||
.get_one::<Mode>("MODE")
|
||||
.value_of_t("MODE")
|
||||
.expect("'MODE' is required and parsing will fail if its missing")
|
||||
{
|
||||
Mode::Fast => {
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
```console
|
||||
$ 04_01_possible --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 04_01_possible[EXE] <MODE>
|
||||
|
||||
Arguments:
|
||||
<MODE> What mode to run the program in [possible values: fast, slow]
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 04_01_possible fast
|
||||
Hare
|
||||
|
||||
$ 04_01_possible slow
|
||||
Tortoise
|
||||
|
||||
$ 04_01_possible medium
|
||||
? failed
|
||||
error: invalid value 'medium' for '<MODE>'
|
||||
[possible values: fast, slow]
|
||||
|
||||
For more information, try '--help'.
|
||||
|
||||
```
|
|
@ -1,19 +1,20 @@
|
|||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
let matches = command!()
|
||||
.arg(
|
||||
arg!(<MODE>)
|
||||
.help("What mode to run the program in")
|
||||
.value_parser(["fast", "slow"]),
|
||||
.possible_values(["fast", "slow"]),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
// Note, it's safe to call unwrap() because the arg is required
|
||||
match matches
|
||||
.get_one::<String>("MODE")
|
||||
.value_of("MODE")
|
||||
.expect("'MODE' is required and parsing will fail if its missing")
|
||||
.as_str()
|
||||
{
|
||||
"fast" => {
|
||||
println!("Hare");
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
```console
|
||||
$ 04_02_parse --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 04_02_parse[EXE] <PORT>
|
||||
|
||||
Arguments:
|
||||
<PORT> Network port to use
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 04_02_parse 22
|
||||
PORT = 22
|
||||
|
||||
$ 04_02_parse foobar
|
||||
? failed
|
||||
error: invalid value 'foobar' for '<PORT>': invalid digit found in string
|
||||
|
||||
For more information, try '--help'.
|
||||
|
||||
$ 04_02_parse_derive 0
|
||||
? failed
|
||||
error: invalid value '0' for '<PORT>': 0 is not in 1..=65535
|
||||
|
||||
For more information, try '--help'.
|
||||
|
||||
```
|
|
@ -1,17 +1,19 @@
|
|||
use clap::{arg, command, value_parser};
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
let matches = command!()
|
||||
.arg(
|
||||
arg!(<PORT>)
|
||||
.help("Network port to use")
|
||||
.value_parser(value_parser!(u16).range(1..)),
|
||||
.validator(|s| s.parse::<usize>()),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
// Note, it's safe to call unwrap() because the arg is required
|
||||
let port: u16 = *matches
|
||||
.get_one::<u16>("PORT")
|
||||
let port: usize = matches
|
||||
.value_of_t("PORT")
|
||||
.expect("'PORT' is required and parsing will fail if its missing");
|
||||
println!("PORT = {}", port);
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
```console
|
||||
$ 04_02_validate --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 04_02_validate[EXE] <PORT>
|
||||
|
||||
Arguments:
|
||||
<PORT> Network port to use
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 04_02_validate 22
|
||||
PORT = 22
|
||||
|
||||
$ 04_02_validate foobar
|
||||
? failed
|
||||
error: invalid value 'foobar' for '<PORT>': `foobar` isn't a port number
|
||||
|
||||
For more information, try '--help'.
|
||||
|
||||
$ 04_02_validate 0
|
||||
? failed
|
||||
error: invalid value '0' for '<PORT>': port not in range 1-65535
|
||||
|
||||
For more information, try '--help'.
|
||||
|
||||
```
|
|
@ -1,34 +1,36 @@
|
|||
// Note: this requires the `cargo` feature
|
||||
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
let matches = command!() // requires `cargo` feature
|
||||
let matches = command!()
|
||||
.arg(
|
||||
arg!(<PORT>)
|
||||
.help("Network port to use")
|
||||
.value_parser(port_in_range),
|
||||
.validator(port_in_range),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
// Note, it's safe to call unwrap() because the arg is required
|
||||
let port: u16 = *matches
|
||||
.get_one::<u16>("PORT")
|
||||
let port: usize = matches
|
||||
.value_of_t("PORT")
|
||||
.expect("'PORT' is required and parsing will fail if its missing");
|
||||
println!("PORT = {}", port);
|
||||
}
|
||||
|
||||
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
|
||||
|
||||
fn port_in_range(s: &str) -> Result<u16, String> {
|
||||
fn port_in_range(s: &str) -> Result<(), String> {
|
||||
let port: usize = s
|
||||
.parse()
|
||||
.map_err(|_| format!("`{}` isn't a port number", s))?;
|
||||
if PORT_RANGE.contains(&port) {
|
||||
Ok(port as u16)
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"port not in range {}-{}",
|
||||
"Port not in range {}-{}",
|
||||
PORT_RANGE.start(),
|
||||
PORT_RANGE.end()
|
||||
))
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
```console
|
||||
$ 04_03_relations --help
|
||||
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]
|
||||
|
||||
Arguments:
|
||||
[INPUT_FILE] some regular input
|
||||
|
||||
Options:
|
||||
--set-ver <VER> set version manually
|
||||
--major auto inc major
|
||||
--minor auto inc minor
|
||||
--patch auto inc patch
|
||||
--spec-in <SPEC_IN> some special input argument
|
||||
-c <CONFIG>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 04_03_relations
|
||||
? failed
|
||||
error: the following required arguments were not provided:
|
||||
<--set-ver <VER>|--major|--minor|--patch>
|
||||
|
||||
Usage: 04_03_relations[EXE] <--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> [INPUT_FILE]
|
||||
|
||||
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
|
||||
|
||||
```
|
|
@ -1,40 +1,32 @@
|
|||
use std::path::PathBuf;
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command, value_parser, ArgAction, ArgGroup};
|
||||
use clap::{arg, command, ArgGroup};
|
||||
|
||||
fn main() {
|
||||
// Create application like normal
|
||||
let matches = command!() // requires `cargo` feature
|
||||
let matches = command!()
|
||||
// Add the version arguments
|
||||
.arg(arg!(--"set-ver" <VER> "set version manually"))
|
||||
.arg(arg!(--major "auto inc major").action(ArgAction::SetTrue))
|
||||
.arg(arg!(--minor "auto inc minor").action(ArgAction::SetTrue))
|
||||
.arg(arg!(--patch "auto inc patch").action(ArgAction::SetTrue))
|
||||
.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"]),
|
||||
.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")
|
||||
.value_parser(value_parser!(PathBuf))
|
||||
.group("input"),
|
||||
)
|
||||
.arg(arg!([INPUT_FILE] "some regular input").group("input"))
|
||||
.arg(
|
||||
arg!(--"spec-in" <SPEC_IN> "some special input argument")
|
||||
.value_parser(value_parser!(PathBuf))
|
||||
.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>)
|
||||
.value_parser(value_parser!(PathBuf))
|
||||
.requires("input"),
|
||||
)
|
||||
.arg(arg!(config: -c <CONFIG>).required(false).requires("input"))
|
||||
.get_matches();
|
||||
|
||||
// Let's assume the old version 1.2.3
|
||||
|
@ -43,14 +35,14 @@ fn main() {
|
|||
let mut patch = 3;
|
||||
|
||||
// See if --set-ver was used to set the version manually
|
||||
let version = if let Some(ver) = matches.get_one::<String>("set-ver") {
|
||||
ver.to_owned()
|
||||
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.get_flag("major"),
|
||||
matches.get_flag("minor"),
|
||||
matches.get_flag("patch"),
|
||||
matches.is_present("major"),
|
||||
matches.is_present("minor"),
|
||||
matches.is_present("patch"),
|
||||
);
|
||||
match (maj, min, pat) {
|
||||
(true, _, _) => major += 1,
|
||||
|
@ -64,15 +56,14 @@ fn main() {
|
|||
println!("Version: {}", version);
|
||||
|
||||
// Check for usage of -c
|
||||
if matches.contains_id("config") {
|
||||
if matches.is_present("config") {
|
||||
let input = matches
|
||||
.get_one::<PathBuf>("INPUT_FILE")
|
||||
.unwrap_or_else(|| matches.get_one::<PathBuf>("spec-in").unwrap())
|
||||
.display();
|
||||
.value_of("INPUT_FILE")
|
||||
.unwrap_or_else(|| matches.value_of("spec-in").unwrap());
|
||||
println!(
|
||||
"Doing work using input {} and config {}",
|
||||
input,
|
||||
matches.get_one::<PathBuf>("config").unwrap().display()
|
||||
matches.value_of("config").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
```console
|
||||
$ 04_04_custom --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 04_04_custom[EXE] [OPTIONS] [INPUT_FILE]
|
||||
|
||||
Arguments:
|
||||
[INPUT_FILE] some regular input
|
||||
|
||||
Options:
|
||||
--set-ver <VER> set version manually
|
||||
--major auto inc major
|
||||
--minor auto inc minor
|
||||
--patch auto inc patch
|
||||
--spec-in <SPEC_IN> some special input argument
|
||||
-c <CONFIG>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 04_04_custom
|
||||
? failed
|
||||
error: Can 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: Can 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
|
||||
|
||||
```
|
|
@ -1,26 +1,22 @@
|
|||
use std::path::PathBuf;
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::error::ErrorKind;
|
||||
use clap::{arg, command, value_parser, ArgAction};
|
||||
use clap::{arg, command, ErrorKind};
|
||||
|
||||
fn main() {
|
||||
// Create application like normal
|
||||
let mut cmd = command!() // requires `cargo` feature
|
||||
let mut cmd = command!()
|
||||
// Add the version arguments
|
||||
.arg(arg!(--"set-ver" <VER> "set version manually"))
|
||||
.arg(arg!(--major "auto inc major").action(ArgAction::SetTrue))
|
||||
.arg(arg!(--minor "auto inc minor").action(ArgAction::SetTrue))
|
||||
.arg(arg!(--patch "auto inc patch").action(ArgAction::SetTrue))
|
||||
.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").value_parser(value_parser!(PathBuf)))
|
||||
.arg(
|
||||
arg!(--"spec-in" <SPEC_IN> "some special input argument")
|
||||
.value_parser(value_parser!(PathBuf)),
|
||||
)
|
||||
.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>).value_parser(value_parser!(PathBuf)));
|
||||
.arg(arg!(config: -c <CONFIG>).required(false));
|
||||
let matches = cmd.get_matches_mut();
|
||||
|
||||
// Let's assume the old version 1.2.3
|
||||
|
@ -29,8 +25,9 @@ fn main() {
|
|||
let mut patch = 3;
|
||||
|
||||
// See if --set-ver was used to set the version manually
|
||||
let version = if let Some(ver) = matches.get_one::<String>("set-ver") {
|
||||
if matches.get_flag("major") || matches.get_flag("minor") || matches.get_flag("patch") {
|
||||
let version = if let Some(ver) = matches.value_of("set-ver") {
|
||||
if matches.is_present("major") || matches.is_present("minor") || matches.is_present("patch")
|
||||
{
|
||||
cmd.error(
|
||||
ErrorKind::ArgumentConflict,
|
||||
"Can't do relative and absolute version change",
|
||||
|
@ -41,9 +38,9 @@ fn main() {
|
|||
} else {
|
||||
// Increment the one requested (in a real program, we'd reset the lower numbers)
|
||||
let (maj, min, pat) = (
|
||||
matches.get_flag("major"),
|
||||
matches.get_flag("minor"),
|
||||
matches.get_flag("patch"),
|
||||
matches.is_present("major"),
|
||||
matches.is_present("minor"),
|
||||
matches.is_present("patch"),
|
||||
);
|
||||
match (maj, min, pat) {
|
||||
(true, false, false) => major += 1,
|
||||
|
@ -63,22 +60,21 @@ fn main() {
|
|||
println!("Version: {}", version);
|
||||
|
||||
// Check for usage of -c
|
||||
if matches.contains_id("config") {
|
||||
if matches.is_present("config") {
|
||||
let input = matches
|
||||
.get_one::<PathBuf>("INPUT_FILE")
|
||||
.or_else(|| matches.get_one::<PathBuf>("spec-in"))
|
||||
.value_of("INPUT_FILE")
|
||||
.or_else(|| matches.value_of("spec-in"))
|
||||
.unwrap_or_else(|| {
|
||||
cmd.error(
|
||||
ErrorKind::MissingRequiredArgument,
|
||||
"INPUT_FILE or --spec-in is required when using --config",
|
||||
)
|
||||
.exit()
|
||||
})
|
||||
.display();
|
||||
});
|
||||
println!(
|
||||
"Doing work using input {} and config {}",
|
||||
input,
|
||||
matches.get_one::<PathBuf>("config").unwrap().display()
|
||||
matches.value_of("config").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
use clap::{arg, command, value_parser};
|
||||
// Note: this requires the `cargo` feature
|
||||
|
||||
use clap::{arg, command};
|
||||
|
||||
fn main() {
|
||||
let matches = cmd().get_matches();
|
||||
|
||||
// Note, it's safe to call unwrap() because the arg is required
|
||||
let port: usize = *matches
|
||||
.get_one::<usize>("PORT")
|
||||
let port: usize = matches
|
||||
.value_of_t("PORT")
|
||||
.expect("'PORT' is required and parsing will fail if its missing");
|
||||
println!("PORT = {}", port);
|
||||
}
|
||||
|
||||
fn cmd() -> clap::Command {
|
||||
command!() // requires `cargo` feature
|
||||
.arg(
|
||||
arg!(<PORT>)
|
||||
.help("Network port to use")
|
||||
.value_parser(value_parser!(usize)),
|
||||
)
|
||||
fn cmd() -> clap::Command<'static> {
|
||||
command!().arg(
|
||||
arg!(<PORT>)
|
||||
.help("Network port to use")
|
||||
.validator(|s| s.parse::<usize>()),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_cmd() {
|
||||
fn verify_app() {
|
||||
cmd().debug_assert();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,664 @@
|
|||
# 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. [Positionals](#positionals)
|
||||
2. [Options](#options)
|
||||
3. [Flags](#flags)
|
||||
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 `Command` 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 `command!()` 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 `Command` methods to change the application level behavior of clap.
|
||||
|
||||
[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
|
||||
|
||||
### Positionals
|
||||
|
||||
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")
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
You can name your arguments with a flag:
|
||||
- Order doesn't matter
|
||||
- They can be optional
|
||||
- Intent is clearer
|
||||
|
||||
[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")
|
||||
|
||||
```
|
||||
|
||||
### Flags
|
||||
|
||||
Flags can also be 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
|
||||
|
||||
```
|
||||
|
||||
### Subcommands
|
||||
|
||||
Subcommands are defined as `Command`s that get added via `Command::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 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 `Command::arg_required_else_help`:
|
||||
```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)
|
||||
|
||||
```
|
||||
|
||||
Because we set `Command::propagate_version`:
|
||||
```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 parse into any data type.
|
||||
|
||||
[Example:](04_02_parse.rs)
|
||||
```console
|
||||
$ 04_02_parse --help
|
||||
clap [..]
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
USAGE:
|
||||
04_02_parse[EXE] <PORT>
|
||||
|
||||
ARGS:
|
||||
<PORT> Network port to use
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
-V, --version Print version information
|
||||
|
||||
$ 04_02_parse 22
|
||||
PORT = 22
|
||||
|
||||
$ 04_02_parse foobar
|
||||
? failed
|
||||
error: Invalid value "foobar" for '<PORT>': invalid digit found in string
|
||||
|
||||
For more information try --help
|
||||
|
||||
```
|
||||
|
||||
A custom validator can be used to improve the error messages or provide additional validation:
|
||||
|
||||
[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 "foobar" for '<PORT>': `foobar` isn't a port number
|
||||
|
||||
For more information try --help
|
||||
|
||||
$ 04_02_validate 0
|
||||
? failed
|
||||
error: Invalid value "0" for '<PORT>': Port not in range 1-65535
|
||||
|
||||
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: Can 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: Can 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
|
||||
|
||||
- For more complex demonstration of features, see our [examples](../README.md).
|
||||
- Proactively check for bad `Command` configurations by calling `Command::debug_assert` in a test ([example](05_01_assert.rs))
|
||||
|
||||
## Contributing
|
||||
|
||||
New example code:
|
||||
- Please update the corresponding section in the [derive tutorial](../tutorial_derive/README.md)
|
||||
- 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).
|
|
@ -1,35 +0,0 @@
|
|||
```console
|
||||
$ 01_quick_derive --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 01_quick_derive[EXE] [OPTIONS] [NAME] [COMMAND]
|
||||
|
||||
Commands:
|
||||
test does testing things
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
Arguments:
|
||||
[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
|
||||
-V, --version Print version
|
||||
|
||||
```
|
||||
|
||||
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...
|
||||
|
||||
```
|
|
@ -3,20 +3,20 @@ use std::path::PathBuf;
|
|||
use clap::{Parser, Subcommand};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
/// Optional name to operate on
|
||||
name: Option<String>,
|
||||
|
||||
/// Sets a custom config file
|
||||
#[arg(short, long, value_name = "FILE")]
|
||||
#[clap(short, long, parse(from_os_str), value_name = "FILE")]
|
||||
config: Option<PathBuf>,
|
||||
|
||||
/// Turn debugging information on
|
||||
#[arg(short, long, action = clap::ArgAction::Count)]
|
||||
debug: u8,
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
debug: usize,
|
||||
|
||||
#[command(subcommand)]
|
||||
#[clap(subcommand)]
|
||||
command: Option<Commands>,
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ enum Commands {
|
|||
/// does testing things
|
||||
Test {
|
||||
/// lists test values
|
||||
#[arg(short, long)]
|
||||
#[clap(short, long)]
|
||||
list: bool,
|
||||
},
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ fn main() {
|
|||
|
||||
// You can check the value provided by positional arguments, or option arguments
|
||||
if let Some(name) = cli.name.as_deref() {
|
||||
println!("Value for name: {name}");
|
||||
println!("Value for name: {}", name);
|
||||
}
|
||||
|
||||
if let Some(config_path) = cli.config.as_deref() {
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
```console
|
||||
$ 02_app_settings_derive --help
|
||||
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
|
||||
-V, --version
|
||||
Print version
|
||||
|
||||
```
|
|
@ -1,12 +1,14 @@
|
|||
use clap::Parser;
|
||||
use clap::{AppSettings, Parser};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(next_line_help = true)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
#[clap(args_override_self = true)]
|
||||
#[clap(allow_negative_numbers = true)]
|
||||
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
|
||||
struct Cli {
|
||||
#[arg(long)]
|
||||
#[clap(long)]
|
||||
two: String,
|
||||
#[arg(long)]
|
||||
#[clap(long)]
|
||||
one: String,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
```console
|
||||
$ 02_apps_derive --help
|
||||
Does awesome things
|
||||
|
||||
Usage: 02_apps_derive[EXE] --two <TWO> --one <ONE>
|
||||
|
||||
Options:
|
||||
--two <TWO>
|
||||
--one <ONE>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 02_apps_derive --version
|
||||
MyApp 1.0
|
||||
|
||||
```
|
|
@ -1,14 +1,14 @@
|
|||
use clap::Parser;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "MyApp")]
|
||||
#[command(author = "Kevin K. <kbknapp@gmail.com>")]
|
||||
#[command(version = "1.0")]
|
||||
#[command(about = "Does awesome things", long_about = None)]
|
||||
#[clap(name = "MyApp")]
|
||||
#[clap(author = "Kevin K. <kbknapp@gmail.com>")]
|
||||
#[clap(version = "1.0")]
|
||||
#[clap(about = "Does awesome things", long_about = None)]
|
||||
struct Cli {
|
||||
#[arg(long)]
|
||||
#[clap(long)]
|
||||
two: String,
|
||||
#[arg(long)]
|
||||
#[clap(long)]
|
||||
one: String,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
```console
|
||||
$ 02_crate_derive --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 02_crate_derive[EXE] --two <TWO> --one <ONE>
|
||||
|
||||
Options:
|
||||
--two <TWO>
|
||||
--one <ONE>
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 02_crate_derive --version
|
||||
clap [..]
|
||||
|
||||
```
|
|
@ -1,11 +1,11 @@
|
|||
use clap::Parser;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)] // Read from `Cargo.toml`
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[arg(long)]
|
||||
#[clap(long)]
|
||||
two: String,
|
||||
#[arg(long)]
|
||||
#[clap(long)]
|
||||
one: String,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
```console
|
||||
$ 03_01_flag_bool_derive --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 03_01_flag_bool_derive[EXE] [OPTIONS]
|
||||
|
||||
Options:
|
||||
-v, --verbose
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 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' cannot be used multiple times
|
||||
|
||||
Usage: 03_01_flag_bool_derive[EXE] [OPTIONS]
|
||||
|
||||
For more information, try '--help'.
|
||||
|
||||
```
|
|
@ -1,9 +1,9 @@
|
|||
use clap::Parser;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[arg(short, long)]
|
||||
#[clap(short, long)]
|
||||
verbose: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
```console
|
||||
$ 03_01_flag_count_derive --help
|
||||
A simple to use, efficient, and full-featured Command Line Argument Parser
|
||||
|
||||
Usage: 03_01_flag_count_derive[EXE] [OPTIONS]
|
||||
|
||||
Options:
|
||||
-v, --verbose...
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
$ 03_01_flag_count_derive
|
||||
verbose: 0
|
||||
|
||||
$ 03_01_flag_count_derive --verbose
|
||||
verbose: 1
|
||||
|
||||
$ 03_01_flag_count_derive --verbose --verbose
|
||||
verbose: 2
|
||||
|
||||
```
|
|
@ -1,10 +1,10 @@
|
|||
use clap::Parser;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[arg(short, long, action = clap::ArgAction::Count)]
|
||||
verbose: u8,
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
verbose: usize,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче