diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/clap/examples | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/clap/examples')
104 files changed, 4383 insertions, 0 deletions
diff --git a/third_party/rust/clap/examples/README.md b/third_party/rust/clap/examples/README.md new file mode 100644 index 0000000000..f98c370c82 --- /dev/null +++ b/third_party/rust/clap/examples/README.md @@ -0,0 +1,16 @@ +# 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. + +## 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 diff --git a/third_party/rust/clap/examples/cargo-example-derive.md b/third_party/rust/clap/examples/cargo-example-derive.md new file mode 100644 index 0000000000..a3dcef386f --- /dev/null +++ b/third_party/rust/clap/examples/cargo-example-derive.md @@ -0,0 +1,38 @@ +For more on creating a custom subcommand, see [the cargo +book](https://doc.rust-lang.org/cargo/reference/external-tools.html#custom-subcommands). +The crate [`clap-cargo`](https://github.com/crate-ci/clap-cargo) can help in +mimicking cargo's interface. + +The help looks like: +```console +$ cargo-example-derive --help +Usage: cargo <COMMAND> + +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) + +Options: + -h, --help Print help + +$ cargo-example-derive example-derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: cargo example-derive [OPTIONS] + +Options: + --manifest-path <MANIFEST_PATH> + -h, --help Print help + -V, --version Print version + +``` + +Then to directly invoke the command, run: +```console +$ cargo-example-derive example-derive +None + +$ cargo-example-derive example-derive --manifest-path Cargo.toml +Some("Cargo.toml") + +``` diff --git a/third_party/rust/clap/examples/cargo-example-derive.rs b/third_party/rust/clap/examples/cargo-example-derive.rs new file mode 100644 index 0000000000..dfdd799fc1 --- /dev/null +++ b/third_party/rust/clap/examples/cargo-example-derive.rs @@ -0,0 +1,20 @@ +use clap::Parser; + +#[derive(Parser)] // requires `derive` feature +#[command(name = "cargo")] +#[command(bin_name = "cargo")] +enum CargoCli { + ExampleDerive(ExampleDeriveArgs), +} + +#[derive(clap::Args)] +#[command(author, version, about, long_about = None)] +struct ExampleDeriveArgs { + #[arg(long)] + manifest_path: Option<std::path::PathBuf>, +} + +fn main() { + let CargoCli::ExampleDerive(args) = CargoCli::parse(); + println!("{:?}", args.manifest_path); +} diff --git a/third_party/rust/clap/examples/cargo-example.md b/third_party/rust/clap/examples/cargo-example.md new file mode 100644 index 0000000000..edf8ee18a3 --- /dev/null +++ b/third_party/rust/clap/examples/cargo-example.md @@ -0,0 +1,38 @@ +For more on creating a custom subcommand, see [the cargo +book](https://doc.rust-lang.org/cargo/reference/external-tools.html#custom-subcommands). +The crate [`clap-cargo`](https://github.com/crate-ci/clap-cargo) can help in +mimicking cargo's interface. + +The help looks like: +```console +$ cargo-example --help +Usage: cargo <COMMAND> + +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) + +Options: + -h, --help Print help + +$ cargo-example example --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: cargo example [OPTIONS] + +Options: + --manifest-path <PATH> + -h, --help Print help + -V, --version Print version + +``` + +Then to directly invoke the command, run: +```console +$ cargo-example example +None + +$ cargo-example example --manifest-path Cargo.toml +Some("Cargo.toml") + +``` diff --git a/third_party/rust/clap/examples/cargo-example.rs b/third_party/rust/clap/examples/cargo-example.rs new file mode 100644 index 0000000000..1173365144 --- /dev/null +++ b/third_party/rust/clap/examples/cargo-example.rs @@ -0,0 +1,18 @@ +fn main() { + let cmd = clap::Command::new("cargo") + .bin_name("cargo") + .subcommand_required(true) + .subcommand( + clap::command!("example").arg( + clap::arg!(--"manifest-path" <PATH>) + .value_parser(clap::value_parser!(std::path::PathBuf)), + ), + ); + let matches = cmd.get_matches(); + let matches = match matches.subcommand() { + Some(("example", matches)) => matches, + _ => unreachable!("clap should ensure we don't get here"), + }; + let manifest_path = matches.get_one::<std::path::PathBuf>("manifest-path"); + println!("{manifest_path:?}"); +} diff --git a/third_party/rust/clap/examples/demo.md b/third_party/rust/clap/examples/demo.md new file mode 100644 index 0000000000..772d98dca0 --- /dev/null +++ b/third_party/rust/clap/examples/demo.md @@ -0,0 +1,17 @@ +```console +$ demo --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +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! + +``` +*(version number and `.exe` extension on windows replaced by placeholders)* diff --git a/third_party/rust/clap/examples/demo.rs b/third_party/rust/clap/examples/demo.rs new file mode 100644 index 0000000000..8e4304a2e6 --- /dev/null +++ b/third_party/rust/clap/examples/demo.rs @@ -0,0 +1,22 @@ +use clap::Parser; + +/// Simple program to greet a person +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// Name of the person to greet + #[arg(short, long)] + name: String, + + /// Number of times to greet + #[arg(short, long, default_value_t = 1)] + count: u8, +} + +fn main() { + let args = Args::parse(); + + for _ in 0..args.count { + println!("Hello {}!", args.name) + } +} diff --git a/third_party/rust/clap/examples/derive_ref/augment_args.rs b/third_party/rust/clap/examples/derive_ref/augment_args.rs new file mode 100644 index 0000000000..39d837cfaf --- /dev/null +++ b/third_party/rust/clap/examples/derive_ref/augment_args.rs @@ -0,0 +1,27 @@ +use clap::{arg, Args, Command, FromArgMatches as _}; + +#[derive(Args, Debug)] +struct DerivedArgs { + #[arg(short, long)] + derived: bool, +} + +fn main() { + let cli = Command::new("CLI").arg(arg!(-b - -built).action(clap::ArgAction::SetTrue)); + // 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 derived via ArgMatches: {:?}", + matches.get_flag("derived") + ); + + // Since DerivedArgs implements FromArgMatches, we can extract it from the unstructured ArgMatches. + // This is the main benefit of using derived arguments. + let derived_matches = DerivedArgs::from_arg_matches(&matches) + .map_err(|err| err.exit()) + .unwrap(); + println!("Value of derived: {derived_matches:#?}"); +} diff --git a/third_party/rust/clap/examples/derive_ref/augment_subcommands.rs b/third_party/rust/clap/examples/derive_ref/augment_subcommands.rs new file mode 100644 index 0000000000..51cbe75d2a --- /dev/null +++ b/third_party/rust/clap/examples/derive_ref/augment_subcommands.rs @@ -0,0 +1,21 @@ +use clap::{Command, FromArgMatches as _, Parser, Subcommand as _}; + +#[derive(Parser, Debug)] +enum Subcommands { + Derived { + #[arg(short, long)] + derived_flag: bool, + }, +} + +fn main() { + let cli = Command::new("Built CLI"); + // Augment with derived subcommands + let cli = Subcommands::augment_subcommands(cli); + + let matches = cli.get_matches(); + let derived_subcommands = Subcommands::from_arg_matches(&matches) + .map_err(|err| err.exit()) + .unwrap(); + println!("Derived subcommands: {derived_subcommands:#?}"); +} diff --git a/third_party/rust/clap/examples/derive_ref/flatten_hand_args.rs b/third_party/rust/clap/examples/derive_ref/flatten_hand_args.rs new file mode 100644 index 0000000000..36aac09323 --- /dev/null +++ b/third_party/rust/clap/examples/derive_ref/flatten_hand_args.rs @@ -0,0 +1,91 @@ +use clap::error::Error; +use clap::{Arg, ArgAction, ArgMatches, Args, Command, FromArgMatches, Parser}; + +#[derive(Debug)] +struct CliArgs { + foo: bool, + bar: bool, + quuz: Option<String>, +} + +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"), + }) + } + 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); + } + 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_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), + ) + } +} + +#[derive(Parser, Debug)] +struct Cli { + #[arg(short, long)] + top_level: bool, + #[command(flatten)] + more_args: CliArgs, +} + +fn main() { + let args = Cli::parse(); + println!("{args:#?}"); +} diff --git a/third_party/rust/clap/examples/derive_ref/hand_subcommand.rs b/third_party/rust/clap/examples/derive_ref/hand_subcommand.rs new file mode 100644 index 0000000000..ebaa60d5d2 --- /dev/null +++ b/third_party/rust/clap/examples/derive_ref/hand_subcommand.rs @@ -0,0 +1,79 @@ +use clap::error::{Error, ErrorKind}; +use clap::{ArgMatches, Args as _, Command, FromArgMatches, Parser, Subcommand}; + +#[derive(Parser, Debug)] +struct AddArgs { + name: Vec<String>, +} +#[derive(Parser, Debug)] +struct RemoveArgs { + #[arg(short, long)] + force: bool, + name: Vec<String>, +} + +#[derive(Debug)] +enum CliSub { + Add(AddArgs), + Remove(RemoveArgs), +} + +impl FromArgMatches for CliSub { + fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error> { + match matches.subcommand() { + 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, + "Valid subcommands are `add` and `remove`", + )), + None => Err(Error::raw( + ErrorKind::MissingSubcommand, + "Valid subcommands are `add` and `remove`", + )), + } + } + fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error> { + match matches.subcommand() { + Some(("add", args)) => *self = Self::Add(AddArgs::from_arg_matches(args)?), + Some(("remove", args)) => *self = Self::Remove(RemoveArgs::from_arg_matches(args)?), + Some((_, _)) => { + return Err(Error::raw( + ErrorKind::InvalidSubcommand, + "Valid subcommands are `add` and `remove`", + )) + } + None => (), + }; + Ok(()) + } +} + +impl Subcommand for CliSub { + 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 { + cmd.subcommand(AddArgs::augment_args(Command::new("add"))) + .subcommand(RemoveArgs::augment_args(Command::new("remove"))) + .subcommand_required(true) + } + fn has_subcommand(name: &str) -> bool { + matches!(name, "add" | "remove") + } +} + +#[derive(Parser, Debug)] +struct Cli { + #[arg(short, long)] + top_level: bool, + #[command(subcommand)] + subcommand: CliSub, +} + +fn main() { + let args = Cli::parse(); + println!("{args:#?}"); +} diff --git a/third_party/rust/clap/examples/derive_ref/interop_tests.md b/third_party/rust/clap/examples/derive_ref/interop_tests.md new file mode 100644 index 0000000000..b29e2d52d0 --- /dev/null +++ b/third_party/rust/clap/examples/derive_ref/interop_tests.md @@ -0,0 +1,248 @@ +Following are tests for the interop examples in this directory. + +## Augment Args + +```console +$ interop_augment_args +Value of built: false +Value of derived via ArgMatches: false +Value of derived: DerivedArgs { + derived: false, +} + +``` + +```console +$ interop_augment_args -b --derived +Value of built: true +Value of derived via ArgMatches: true +Value of derived: DerivedArgs { + derived: true, +} + +``` + +```console +$ interop_augment_args -d --built +Value of built: true +Value of derived via ArgMatches: true +Value of derived: DerivedArgs { + derived: true, +} + +``` + +```console +$ interop_augment_args --unknown +? failed +error: unexpected argument '--unknown' found + +Usage: interop_augment_args[EXE] [OPTIONS] + +For more information, try '--help'. + +``` + +## Augment Subcommands + +```console +$ interop_augment_subcommands +? failed +error: A subcommand is required but one was not provided. +``` + +```console +$ interop_augment_subcommands derived +Derived subcommands: Derived { + derived_flag: false, +} + +``` + +```console +$ interop_augment_subcommands derived --derived-flag +Derived subcommands: Derived { + derived_flag: true, +} + +``` + +```console +$ interop_augment_subcommands derived --unknown +? failed +error: unexpected argument '--unknown' found + +Usage: interop_augment_subcommands[EXE] derived [OPTIONS] + +For more information, try '--help'. + +``` + +```console +$ interop_augment_subcommands unknown +? failed +error: unrecognized subcommand 'unknown' + +Usage: interop_augment_subcommands[EXE] [COMMAND] + +For more information, try '--help'. + +``` + +## Hand-Implemented Subcommand + +```console +$ interop_hand_subcommand +? failed +Usage: interop_hand_subcommand[EXE] [OPTIONS] <COMMAND> + +Commands: + add + remove + help Print this message or the help of the given subcommand(s) + +Options: + -t, --top-level + -h, --help Print help + +``` + +```console +$ interop_hand_subcommand add +Cli { + top_level: false, + subcommand: Add( + AddArgs { + name: [], + }, + ), +} + +``` + +```console +$ interop_hand_subcommand add a b c +Cli { + top_level: false, + subcommand: Add( + AddArgs { + name: [ + "a", + "b", + "c", + ], + }, + ), +} + +``` + +```console +$ interop_hand_subcommand add --unknown +? failed +error: unexpected argument '--unknown' found + + tip: to pass '--unknown' as a value, use '-- --unknown' + +Usage: interop_hand_subcommand[EXE] add [NAME]... + +For more information, try '--help'. + +``` + +```console +$ interop_hand_subcommand remove +Cli { + top_level: false, + subcommand: Remove( + RemoveArgs { + force: false, + name: [], + }, + ), +} + +``` + +```console +$ interop_hand_subcommand remove --force a b c +Cli { + top_level: false, + subcommand: Remove( + RemoveArgs { + force: true, + name: [ + "a", + "b", + "c", + ], + }, + ), +} + +``` + +```console +$ interop_hand_subcommand unknown +? failed +error: unrecognized subcommand 'unknown' + +Usage: interop_hand_subcommand[EXE] [OPTIONS] <COMMAND> + +For more information, try '--help'. + +``` + +## Flatten Hand-Implemented Args + +```console +$ interop_flatten_hand_args +Cli { + top_level: false, + more_args: CliArgs { + foo: false, + bar: false, + quuz: None, + }, +} + +``` + +```console +$ interop_flatten_hand_args -f --bar +Cli { + top_level: false, + more_args: CliArgs { + foo: true, + bar: true, + quuz: None, + }, +} + +``` + +```console +$ interop_flatten_hand_args --quuz abc +Cli { + top_level: false, + more_args: CliArgs { + foo: false, + bar: false, + quuz: Some( + "abc", + ), + }, +} + +``` + +```console +$ interop_flatten_hand_args --unknown +? failed +error: unexpected argument '--unknown' found + +Usage: interop_flatten_hand_args[EXE] [OPTIONS] + +For more information, try '--help'. + +``` diff --git a/third_party/rust/clap/examples/escaped-positional-derive.md b/third_party/rust/clap/examples/escaped-positional-derive.md new file mode 100644 index 0000000000..82990b59f1 --- /dev/null +++ b/third_party/rust/clap/examples/escaped-positional-derive.md @@ -0,0 +1,60 @@ +**This requires enabling the [`derive` feature flag][crate::_features].** + +You can use `--` to escape further arguments. + +Let's see what this looks like in the help: +```console +$ escaped-positional-derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: escaped-positional-derive[EXE] [OPTIONS] [-- <SLOP>...] + +Arguments: + [SLOP]... + +Options: + -f + -p <PEAR> + -h, --help Print help + -V, --version Print version + +``` + +Here is a baseline without any arguments: +```console +$ escaped-positional-derive +-f used: false +-p's value: None +'slops' values: [] + +``` + +Notice that we can't pass positional arguments before `--`: +```console +$ escaped-positional-derive foo bar +? failed +error: unexpected argument 'foo' found + +Usage: escaped-positional-derive[EXE] [OPTIONS] [-- <SLOP>...] + +For more information, try '--help'. + +``` + +But you can after: +```console +$ escaped-positional-derive -f -p=bob -- sloppy slop slop +-f used: true +-p's value: Some("bob") +'slops' values: ["sloppy", "slop", "slop"] + +``` + +As mentioned, the parser will directly pass everything through: +```console +$ escaped-positional-derive -- -f -p=bob sloppy slop slop +-f used: false +-p's value: None +'slops' values: ["-f", "-p=bob", "sloppy", "slop", "slop"] + +``` diff --git a/third_party/rust/clap/examples/escaped-positional-derive.rs b/third_party/rust/clap/examples/escaped-positional-derive.rs new file mode 100644 index 0000000000..719b3d6c21 --- /dev/null +++ b/third_party/rust/clap/examples/escaped-positional-derive.rs @@ -0,0 +1,25 @@ +use clap::Parser; + +#[derive(Parser)] // requires `derive` feature +#[command(author, version, about, long_about = None)] +struct Cli { + #[arg(short = 'f')] + eff: bool, + + #[arg(short = 'p', value_name = "PEAR")] + pea: Option<String>, + + #[arg(last = true)] + slop: Vec<String>, +} + +fn main() { + let args = Cli::parse(); + + // This is what will happen with `myprog -f -p=bob -- sloppy slop slop`... + println!("-f used: {:?}", args.eff); // -f used: true + println!("-p's value: {:?}", args.pea); // -p's value: Some("bob") + println!("'slops' values: {:?}", args.slop); // 'slops' values: Some(["sloppy", "slop", "slop"]) + + // Continued program logic goes here... +} diff --git a/third_party/rust/clap/examples/escaped-positional.md b/third_party/rust/clap/examples/escaped-positional.md new file mode 100644 index 0000000000..d94e3993c8 --- /dev/null +++ b/third_party/rust/clap/examples/escaped-positional.md @@ -0,0 +1,60 @@ +**This requires enabling the [`cargo` feature flag][crate::_features].** + +You can use `--` to escape further arguments. + +Let's see what this looks like in the help: +```console +$ escaped-positional --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: escaped-positional[EXE] [OPTIONS] [-- <SLOP>...] + +Arguments: + [SLOP]... + +Options: + -f + -p <PEAR> + -h, --help Print help + -V, --version Print version + +``` + +Here is a baseline without any arguments: +```console +$ escaped-positional +-f used: false +-p's value: None +'slops' values: [] + +``` + +Notice that we can't pass positional arguments before `--`: +```console +$ escaped-positional foo bar +? failed +error: unexpected argument 'foo' found + +Usage: escaped-positional[EXE] [OPTIONS] [-- <SLOP>...] + +For more information, try '--help'. + +``` + +But you can after: +```console +$ escaped-positional -f -p=bob -- sloppy slop slop +-f used: true +-p's value: Some("bob") +'slops' values: ["sloppy", "slop", "slop"] + +``` + +As mentioned, the parser will directly pass everything through: +```console +$ escaped-positional -- -f -p=bob sloppy slop slop +-f used: false +-p's value: None +'slops' values: ["-f", "-p=bob", "sloppy", "slop", "slop"] + +``` diff --git a/third_party/rust/clap/examples/escaped-positional.rs b/third_party/rust/clap/examples/escaped-positional.rs new file mode 100644 index 0000000000..d107e47db4 --- /dev/null +++ b/third_party/rust/clap/examples/escaped-positional.rs @@ -0,0 +1,32 @@ +use clap::{arg, command, value_parser, ArgAction}; + +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))) + .arg( + // Indicates that `slop` is only accessible after `--`. + arg!(slop: [SLOP]) + .num_args(1..) + .last(true) + .value_parser(value_parser!(String)), + ) + .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!( + "'slops' values: {:?}", + matches + .get_many::<String>("slop") + .map(|vals| vals.collect::<Vec<_>>()) + .unwrap_or_default() + ); + + // Continued program logic goes here... +} diff --git a/third_party/rust/clap/examples/find.md b/third_party/rust/clap/examples/find.md new file mode 100644 index 0000000000..e9b1a86f89 --- /dev/null +++ b/third_party/rust/clap/examples/find.md @@ -0,0 +1,45 @@ +`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", + ), + ), +] + +``` + diff --git a/third_party/rust/clap/examples/find.rs b/third_party/rust/clap/examples/find.rs new file mode 100644 index 0000000000..0b7f2c7000 --- /dev/null +++ b/third_party/rust/clap/examples/find.rs @@ -0,0 +1,99 @@ +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) + } +} diff --git a/third_party/rust/clap/examples/git-derive.md b/third_party/rust/clap/examples/git-derive.md new file mode 100644 index 0000000000..57edb612e9 --- /dev/null +++ b/third_party/rust/clap/examples/git-derive.md @@ -0,0 +1,159 @@ +**This requires enabling the [`derive` feature flag][crate::_features].** + +Git is an example of several common subcommand patterns. + +Help: +```console +$ git-derive +? failed +A fictional versioning CLI + +Usage: git-derive[EXE] <COMMAND> + +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 + +$ git-derive help +A fictional versioning CLI + +Usage: git-derive[EXE] <COMMAND> + +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 + +$ git-derive help add +adds things + +Usage: git-derive[EXE] add <PATH>... + +Arguments: + <PATH>... Stuff to add + +Options: + -h, --help Print help + +``` + +A basic argument: +```console +$ git-derive add +? failed +adds things + +Usage: git-derive[EXE] add <PATH>... + +Arguments: + <PATH>... Stuff to add + +Options: + -h, --help Print help + +$ git-derive add Cargo.toml Cargo.lock +Adding ["Cargo.toml", "Cargo.lock"] + +``` + +Default subcommand: +```console +$ git-derive stash -h +Usage: git-derive[EXE] stash [OPTIONS] + git-derive[EXE] stash <COMMAND> + +Commands: + push + pop + apply + help Print this message or the help of the given subcommand(s) + +Options: + -m, --message <MESSAGE> + -h, --help Print help + +$ git-derive stash push -h +Usage: git-derive[EXE] stash push [OPTIONS] + +Options: + -m, --message <MESSAGE> + -h, --help Print help + +$ git-derive stash pop -h +Usage: git-derive[EXE] stash pop [STASH] + +Arguments: + [STASH] + +Options: + -h, --help Print help + +$ git-derive stash -m "Prototype" +Pushing StashPushArgs { message: Some("Prototype") } + +$ git-derive stash pop +Popping None + +$ git-derive stash push -m "Prototype" +Pushing StashPushArgs { message: Some("Prototype") } + +$ git-derive stash pop +Popping None + +``` + +External subcommands: +```console +$ 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) + +``` diff --git a/third_party/rust/clap/examples/git-derive.rs b/third_party/rust/clap/examples/git-derive.rs new file mode 100644 index 0000000000..ad82e0cea7 --- /dev/null +++ b/third_party/rust/clap/examples/git-derive.rs @@ -0,0 +1,164 @@ +use std::ffi::OsStr; +use std::ffi::OsString; +use std::path::PathBuf; + +use clap::{Args, Parser, Subcommand, ValueEnum}; + +/// A fictional versioning CLI +#[derive(Debug, Parser)] // requires `derive` feature +#[command(name = "git")] +#[command(about = "A fictional versioning CLI", long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Debug, Subcommand)] +enum Commands { + /// Clones repos + #[command(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)] + Push { + /// The remote to target + remote: String, + }, + /// adds things + #[command(arg_required_else_help = true)] + Add { + /// Stuff to add + #[arg(required = true)] + path: Vec<PathBuf>, + }, + Stash(StashArgs), + #[command(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)] + command: Option<StashCommands>, + + #[command(flatten)] + push: StashPushArgs, +} + +#[derive(Debug, Subcommand)] +enum StashCommands { + Push(StashPushArgs), + Pop { stash: Option<String> }, + Apply { stash: Option<String> }, +} + +#[derive(Debug, Args)] +struct StashPushArgs { + #[arg(short, long)] + message: Option<String>, +} + +fn main() { + let args = Cli::parse(); + + 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 + ); + } + Commands::Push { remote } => { + println!("Pushing to {remote}"); + } + Commands::Add { 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:?}"); + } + StashCommands::Pop { stash } => { + println!("Popping {stash:?}"); + } + StashCommands::Apply { stash } => { + println!("Applying {stash:?}"); + } + } + } + Commands::External(args) => { + println!("Calling out to {:?} with {:?}", &args[0], &args[1..]); + } + } + + // Continued program logic goes here... +} diff --git a/third_party/rust/clap/examples/git.md b/third_party/rust/clap/examples/git.md new file mode 100644 index 0000000000..d90b2e8e05 --- /dev/null +++ b/third_party/rust/clap/examples/git.md @@ -0,0 +1,157 @@ +Git is an example of several common subcommand patterns. + +Help: +```console +$ git +? failed +A fictional versioning CLI + +Usage: git[EXE] <COMMAND> + +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 + +$ git help +A fictional versioning CLI + +Usage: git[EXE] <COMMAND> + +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 + +$ git help add +adds things + +Usage: git[EXE] add <PATH>... + +Arguments: + <PATH>... Stuff to add + +Options: + -h, --help Print help + +``` + +A basic argument: +```console +$ git add +? failed +adds things + +Usage: git[EXE] add <PATH>... + +Arguments: + <PATH>... Stuff to add + +Options: + -h, --help Print help + +$ git add Cargo.toml Cargo.lock +Adding ["Cargo.toml", "Cargo.lock"] + +``` + +Default subcommand: +```console +$ git stash -h +Usage: git[EXE] stash [OPTIONS] + git[EXE] stash <COMMAND> + +Commands: + push + pop + apply + help Print this message or the help of the given subcommand(s) + +Options: + -m, --message <MESSAGE> + -h, --help Print help + +$ git stash push -h +Usage: git[EXE] stash push [OPTIONS] + +Options: + -m, --message <MESSAGE> + -h, --help Print help + +$ git stash pop -h +Usage: git[EXE] stash pop [STASH] + +Arguments: + [STASH] + +Options: + -h, --help Print help + +$ git stash -m "Prototype" +Pushing Some("Prototype") + +$ git stash pop +Popping None + +$ git stash push -m "Prototype" +Pushing Some("Prototype") + +$ git stash pop +Popping None + +``` + +External subcommands: +```console +$ 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) + +``` diff --git a/third_party/rust/clap/examples/git.rs b/third_party/rust/clap/examples/git.rs new file mode 100644 index 0000000000..02173675f9 --- /dev/null +++ b/third_party/rust/clap/examples/git.rs @@ -0,0 +1,137 @@ +use std::ffi::OsString; +use std::path::PathBuf; + +use clap::{arg, Command}; + +fn cli() -> Command { + Command::new("git") + .about("A fictional versioning CLI") + .subcommand_required(true) + .arg_required_else_help(true) + .allow_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") + .arg(arg!(<REMOTE> "The remote to target")) + .arg_required_else_help(true), + ) + .subcommand( + Command::new("add") + .about("adds things") + .arg_required_else_help(true) + .arg(arg!(<PATH> ... "Stuff to add").value_parser(clap::value_parser!(PathBuf))), + ) + .subcommand( + Command::new("stash") + .args_conflicts_with_subcommands(true) + .args(push_args()) + .subcommand(Command::new("push").args(push_args())) + .subcommand(Command::new("pop").arg(arg!([STASH]))) + .subcommand(Command::new("apply").arg(arg!([STASH]))), + ) +} + +fn push_args() -> Vec<clap::Arg> { + vec![arg!(-m --message <MESSAGE>)] +} + +fn main() { + let matches = cli().get_matches(); + + match matches.subcommand() { + Some(("clone", sub_matches)) => { + println!( + "Cloning {}", + sub_matches.get_one::<String>("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") + ); + } + Some(("add", sub_matches)) => { + let paths = sub_matches + .get_many::<PathBuf>("PATH") + .into_iter() + .flatten() + .collect::<Vec<_>>(); + 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:?}"); + } + ("pop", sub_matches) => { + let stash = sub_matches.get_one::<String>("STASH"); + println!("Popping {stash:?}"); + } + ("push", sub_matches) => { + let message = sub_matches.get_one::<String>("message"); + println!("Pushing {message:?}"); + } + (name, _) => { + unreachable!("Unsupported subcommand `{name}`") + } + } + } + Some((ext, sub_matches)) => { + let args = sub_matches + .get_many::<OsString>("") + .into_iter() + .flatten() + .collect::<Vec<_>>(); + println!("Calling out to {ext:?} with {args:?}"); + } + _ => unreachable!(), // If all subcommands are defined above, anything else is unreachable!() + } + + // Continued program logic goes here... +} diff --git a/third_party/rust/clap/examples/multicall-busybox.md b/third_party/rust/clap/examples/multicall-busybox.md new file mode 100644 index 0000000000..e84b263514 --- /dev/null +++ b/third_party/rust/clap/examples/multicall-busybox.md @@ -0,0 +1,39 @@ +See the documentation for [`Command::multicall`][crate::Command::multicall] for rationale. + +This example omits every command except true and false, +which are the most trivial to implement, +```console +$ busybox true +? 0 + +$ busybox false +? 1 + +``` +*Note: without the links setup, we can't demonstrate the multicall behavior* + +But includes the `--install` option as an example of why it can be useful +for the main program to take arguments that aren't applet subcommands. +```console +$ busybox --install +? failed +... + +``` + +Though users must pass something: +```console +$ busybox +? failed +Usage: busybox [OPTIONS] [APPLET] + +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 + +``` diff --git a/third_party/rust/clap/examples/multicall-busybox.rs b/third_party/rust/clap/examples/multicall-busybox.rs new file mode 100644 index 0000000000..9260e1d70e --- /dev/null +++ b/third_party/rust/clap/examples/multicall-busybox.rs @@ -0,0 +1,47 @@ +use std::path::PathBuf; +use std::process::exit; + +use clap::{value_parser, Arg, ArgAction, Command}; + +fn applet_commands() -> [Command; 2] { + [ + Command::new("true").about("does nothing successfully"), + Command::new("false").about("does nothing unsuccessfully"), + ] +} + +fn main() { + let cmd = Command::new(env!("CARGO_CRATE_NAME")) + .multicall(true) + .subcommand( + Command::new("busybox") + .arg_required_else_help(true) + .subcommand_value_name("APPLET") + .subcommand_help_heading("APPLETS") + .arg( + Arg::new("install") + .long("install") + .help("Install hardlinks for all subcommands in path") + .exclusive(true) + .action(ArgAction::Set) + .default_missing_value("/usr/local/bin") + .value_parser(value_parser!(PathBuf)), + ) + .subcommands(applet_commands()), + ) + .subcommands(applet_commands()); + + let matches = cmd.get_matches(); + let mut subcommand = matches.subcommand(); + if let Some(("busybox", cmd)) = subcommand { + if cmd.contains_id("install") { + unimplemented!("Make hardlinks to the executable here"); + } + subcommand = cmd.subcommand(); + } + match subcommand { + Some(("false", _)) => exit(1), + Some(("true", _)) => exit(0), + _ => unreachable!("parser should ensure only valid subcommand names are used"), + } +} diff --git a/third_party/rust/clap/examples/multicall-hostname.md b/third_party/rust/clap/examples/multicall-hostname.md new file mode 100644 index 0000000000..0df1274e15 --- /dev/null +++ b/third_party/rust/clap/examples/multicall-hostname.md @@ -0,0 +1,10 @@ +See the documentation for [`Command::multicall`][crate::Command::multicall] for rationale. + +This example omits the implementation of displaying address config + +```console +$ hostname +www + +``` +*Note: without the links setup, we can't demonstrate the multicall behavior* diff --git a/third_party/rust/clap/examples/multicall-hostname.rs b/third_party/rust/clap/examples/multicall-hostname.rs new file mode 100644 index 0000000000..b57680a5c1 --- /dev/null +++ b/third_party/rust/clap/examples/multicall-hostname.rs @@ -0,0 +1,17 @@ +use clap::Command; + +fn main() { + let cmd = Command::new(env!("CARGO_CRATE_NAME")) + .multicall(true) + .arg_required_else_help(true) + .subcommand_value_name("APPLET") + .subcommand_help_heading("APPLETS") + .subcommand(Command::new("hostname").about("show hostname part of FQDN")) + .subcommand(Command::new("dnsdomainname").about("show domain name part of FQDN")); + + match cmd.get_matches().subcommand_name() { + Some("hostname") => println!("www"), + Some("dnsdomainname") => println!("example.com"), + _ => unreachable!("parser should ensure only valid subcommand names are used"), + } +} diff --git a/third_party/rust/clap/examples/pacman.md b/third_party/rust/clap/examples/pacman.md new file mode 100644 index 0000000000..b02cf8f1be --- /dev/null +++ b/third_party/rust/clap/examples/pacman.md @@ -0,0 +1,79 @@ +[`pacman`](https://wiki.archlinux.org/index.php/pacman) defines subcommands via flags. + +Here, `-S` is a short flag subcommand: +```console +$ pacman -S package +Installing package... + +``` + +Here `--sync` is a long flag subcommand: +```console +$ pacman --sync package +Installing package... + +``` + +Now the short flag subcommand (`-S`) with a long flag: +```console +$ pacman -S --search name +Searching for name... + +``` + +And the various forms of short flags that work: +```console +$ pacman -S -s name +Searching for name... + +$ pacman -Ss name +Searching for name... + +``` +*(users can "stack" short subcommands with short flags or with other short flag subcommands)* + +In the help, this looks like: +```console +$ pacman -h +package manager utility + +Usage: pacman[EXE] <COMMAND> + +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 + -V, --version Print version + +$ pacman -S -h +Synchronize packages. + +Usage: pacman[EXE] {sync|--sync|-S} [OPTIONS] [package]... + +Arguments: + [package]... packages + +Options: + -s, --search <search>... search remote repositories for matching strings + -i, --info view package information + -h, --help Print help + +``` + +And errors: +```console +$ pacman -S -s foo -i bar +? failed +error: the argument '--search <search>...' cannot be used with '--info' + +Usage: pacman[EXE] {sync|--sync|-S} --search <search>... <package>... + +For more information, try '--help'. + +``` + +**NOTE:** Keep in mind that subcommands, flags, and long flags are *case sensitive*: `-Q` and `-q` are different flags/subcommands. For example, you can have both `-Q` subcommand and `-q` flag, and they will be properly disambiguated. +Let's make a quick program to illustrate. diff --git a/third_party/rust/clap/examples/pacman.rs b/third_party/rust/clap/examples/pacman.rs new file mode 100644 index 0000000000..7ab30db953 --- /dev/null +++ b/third_party/rust/clap/examples/pacman.rs @@ -0,0 +1,111 @@ +use clap::{Arg, ArgAction, Command}; + +fn main() { + let matches = Command::new("pacman") + .about("package manager utility") + .version("5.2.1") + .subcommand_required(true) + .arg_required_else_help(true) + .author("Pacman Development Team") + // Query subcommand + // + // Only a few of its arguments are implemented below. + .subcommand( + Command::new("query") + .short_flag('Q') + .long_flag("query") + .about("Query the package database.") + .arg( + Arg::new("search") + .short('s') + .long("search") + .help("search locally installed packages for matching strings") + .conflicts_with("info") + .action(ArgAction::Set) + .num_args(1..), + ) + .arg( + Arg::new("info") + .long("info") + .short('i') + .conflicts_with("search") + .help("view package information") + .action(ArgAction::Set) + .num_args(1..), + ), + ) + // Sync subcommand + // + // Only a few of its arguments are implemented below. + .subcommand( + Command::new("sync") + .short_flag('S') + .long_flag("sync") + .about("Synchronize packages.") + .arg( + Arg::new("search") + .short('s') + .long("search") + .conflicts_with("info") + .action(ArgAction::Set) + .num_args(1..) + .help("search remote repositories for matching strings"), + ) + .arg( + Arg::new("info") + .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..), + ), + ) + .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(); + let values = packages.join(", "); + println!("Searching for {values}..."); + return; + } + + let packages: Vec<_> = sync_matches + .get_many::<String>("package") + .expect("is present") + .map(|s| s.as_str()) + .collect(); + let values = packages.join(", "); + + if sync_matches.get_flag("info") { + println!("Retrieving info for {values}..."); + } else { + 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}..."); + } else { + println!("Displaying all locally installed packages..."); + } + } + _ => unreachable!(), // If all subcommands are defined above, anything else is unreachable + } +} diff --git a/third_party/rust/clap/examples/repl.rs b/third_party/rust/clap/examples/repl.rs new file mode 100644 index 0000000000..d0baaed8d2 --- /dev/null +++ b/third_party/rust/clap/examples/repl.rs @@ -0,0 +1,92 @@ +use std::io::Write; + +use clap::Command; + +fn main() -> Result<(), String> { + loop { + let line = readline()?; + let line = line.trim(); + if line.is_empty() { + continue; + } + + match respond(line) { + Ok(quit) => { + if quit { + break; + } + } + Err(err) => { + write!(std::io::stdout(), "{err}").map_err(|e| e.to_string())?; + std::io::stdout().flush().map_err(|e| e.to_string())?; + } + } + } + + Ok(()) +} + +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) + .map_err(|e| e.to_string())?; + match matches.subcommand() { + Some(("ping", _matches)) => { + write!(std::io::stdout(), "Pong").map_err(|e| e.to_string())?; + std::io::stdout().flush().map_err(|e| e.to_string())?; + } + Some(("quit", _matches)) => { + write!(std::io::stdout(), "Exiting ...").map_err(|e| e.to_string())?; + std::io::stdout().flush().map_err(|e| e.to_string())?; + return Ok(true); + } + Some((name, _matches)) => unimplemented!("{name}"), + None => unreachable!("subcommand required"), + } + + Ok(false) +} + +fn cli() -> Command { + // strip out usage + const PARSER_TEMPLATE: &str = "\ + {all-args} + "; + // strip out name/version + const APPLET_TEMPLATE: &str = "\ + {about-with-newline}\n\ + {usage-heading}\n {usage}\n\ + \n\ + {all-args}{after-help}\ + "; + + Command::new("repl") + .multicall(true) + .arg_required_else_help(true) + .subcommand_required(true) + .subcommand_value_name("APPLET") + .subcommand_help_heading("APPLETS") + .help_template(PARSER_TEMPLATE) + .subcommand( + Command::new("ping") + .about("Get a response") + .help_template(APPLET_TEMPLATE), + ) + .subcommand( + Command::new("quit") + .alias("exit") + .about("Quit the REPL") + .help_template(APPLET_TEMPLATE), + ) +} + +fn readline() -> Result<String, String> { + write!(std::io::stdout(), "$ ").map_err(|e| e.to_string())?; + std::io::stdout().flush().map_err(|e| e.to_string())?; + let mut buffer = String::new(); + std::io::stdin() + .read_line(&mut buffer) + .map_err(|e| e.to_string())?; + Ok(buffer) +} diff --git a/third_party/rust/clap/examples/tutorial_builder/01_quick.md b/third_party/rust/clap/examples/tutorial_builder/01_quick.md new file mode 100644 index 0000000000..bdba15cd2c --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/01_quick.md @@ -0,0 +1,35 @@ +```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... + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/01_quick.rs b/third_party/rust/clap/examples/tutorial_builder/01_quick.rs new file mode 100644 index 0000000000..328ce55199 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/01_quick.rs @@ -0,0 +1,60 @@ +use std::path::PathBuf; + +use clap::{arg, command, value_parser, ArgAction, Command}; + +fn main() { + let matches = command!() // requires `cargo` feature + .arg(arg!([name] "Optional name to operate on")) + .arg( + arg!( + -c --config <FILE> "Sets a custom config file" + ) + // We don't have syntax yet for optional options, so manually calling `required` + .required(false) + .value_parser(value_parser!(PathBuf)), + ) + .arg(arg!( + -d --debug ... "Turn debugging information on" + )) + .subcommand( + Command::new("test") + .about("does testing things") + .arg(arg!(-l --list "lists test values").action(ArgAction::SetTrue)), + ) + .get_matches(); + + // You can check the value provided by positional arguments, or option arguments + if let Some(name) = matches.get_one::<String>("name") { + println!("Value for name: {name}"); + } + + if let Some(config_path) = matches.get_one::<PathBuf>("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") + { + 0 => println!("Debug mode is off"), + 1 => println!("Debug mode is kind of on"), + 2 => println!("Debug mode is on"), + _ => println!("Don't be crazy"), + } + + // You can check for the existence of subcommands, and if found use their + // matches just as you would the top level cmd + if let Some(matches) = matches.subcommand_matches("test") { + // "$ myapp test" was run + if matches.get_flag("list") { + // "$ myapp test -l" was run + println!("Printing testing lists..."); + } else { + println!("Not printing testing lists..."); + } + } + + // Continued program logic goes here... +} diff --git a/third_party/rust/clap/examples/tutorial_builder/02_app_settings.md b/third_party/rust/clap/examples/tutorial_builder/02_app_settings.md new file mode 100644 index 0000000000..3ac9a95e78 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/02_app_settings.md @@ -0,0 +1,17 @@ +```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 + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/02_app_settings.rs b/third_party/rust/clap/examples/tutorial_builder/02_app_settings.rs new file mode 100644 index 0000000000..4e30ec9a52 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/02_app_settings.rs @@ -0,0 +1,18 @@ +use clap::{arg, command, ArgAction}; + +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)) + .get_matches(); + + println!( + "two: {:?}", + matches.get_one::<String>("two").expect("required") + ); + println!( + "one: {:?}", + matches.get_one::<String>("one").expect("required") + ); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/02_apps.md b/third_party/rust/clap/examples/tutorial_builder/02_apps.md new file mode 100644 index 0000000000..1f8b071b19 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/02_apps.md @@ -0,0 +1,16 @@ +```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 + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/02_apps.rs b/third_party/rust/clap/examples/tutorial_builder/02_apps.rs new file mode 100644 index 0000000000..7d98e6cfe4 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/02_apps.rs @@ -0,0 +1,20 @@ +use clap::{arg, Command}; + +fn main() { + let matches = Command::new("MyApp") + .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)) + .get_matches(); + + println!( + "two: {:?}", + matches.get_one::<String>("two").expect("required") + ); + println!( + "one: {:?}", + matches.get_one::<String>("one").expect("required") + ); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/02_crate.md b/third_party/rust/clap/examples/tutorial_builder/02_crate.md new file mode 100644 index 0000000000..a0d21b3ece --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/02_crate.md @@ -0,0 +1,16 @@ +```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 [..] + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/02_crate.rs b/third_party/rust/clap/examples/tutorial_builder/02_crate.rs new file mode 100644 index 0000000000..ad6bb4713b --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/02_crate.rs @@ -0,0 +1,18 @@ +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)) + .get_matches(); + + println!( + "two: {:?}", + matches.get_one::<String>("two").expect("required") + ); + println!( + "one: {:?}", + matches.get_one::<String>("one").expect("required") + ); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/03_01_flag_bool.md b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_bool.md new file mode 100644 index 0000000000..feec8e0a7a --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_bool.md @@ -0,0 +1,26 @@ +```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'. + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/03_01_flag_bool.rs b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_bool.rs new file mode 100644 index 0000000000..03f2f1756f --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_bool.rs @@ -0,0 +1,14 @@ +use clap::{command, Arg, ArgAction}; + +fn main() { + let matches = command!() // requires `cargo` feature + .arg( + Arg::new("verbose") + .short('v') + .long("verbose") + .action(ArgAction::SetTrue), + ) + .get_matches(); + + println!("verbose: {:?}", matches.get_flag("verbose")); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/03_01_flag_count.md b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_count.md new file mode 100644 index 0000000000..5e964b67a7 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_count.md @@ -0,0 +1,21 @@ +```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 + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/03_01_flag_count.rs b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_count.rs new file mode 100644 index 0000000000..492f7d4c1f --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_count.rs @@ -0,0 +1,14 @@ +use clap::{command, Arg, ArgAction}; + +fn main() { + let matches = command!() // requires `cargo` feature + .arg( + Arg::new("verbose") + .short('v') + .long("verbose") + .action(ArgAction::Count), + ) + .get_matches(); + + println!("verbose: {:?}", matches.get_count("verbose")); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/03_02_option.md b/third_party/rust/clap/examples/tutorial_builder/03_02_option.md new file mode 100644 index 0000000000..42b81fd426 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_02_option.md @@ -0,0 +1,30 @@ +```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") + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/03_02_option.rs b/third_party/rust/clap/examples/tutorial_builder/03_02_option.rs new file mode 100644 index 0000000000..e9ba3e41a9 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_02_option.rs @@ -0,0 +1,9 @@ +use clap::{command, Arg}; + +fn main() { + let matches = command!() // requires `cargo` feature + .arg(Arg::new("name").short('n').long("name")) + .get_matches(); + + println!("name: {:?}", matches.get_one::<String>("name")); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/03_02_option_mult.md b/third_party/rust/clap/examples/tutorial_builder/03_02_option_mult.md new file mode 100644 index 0000000000..659748e4de --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_02_option_mult.md @@ -0,0 +1,30 @@ +```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") + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/03_02_option_mult.rs b/third_party/rust/clap/examples/tutorial_builder/03_02_option_mult.rs new file mode 100644 index 0000000000..1370477eb4 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_02_option_mult.rs @@ -0,0 +1,14 @@ +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")); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/03_03_positional.md b/third_party/rust/clap/examples/tutorial_builder/03_03_positional.md new file mode 100644 index 0000000000..d8d49f39b6 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_03_positional.md @@ -0,0 +1,20 @@ +```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") + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/03_03_positional.rs b/third_party/rust/clap/examples/tutorial_builder/03_03_positional.rs new file mode 100644 index 0000000000..f652d27ca5 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_03_positional.rs @@ -0,0 +1,9 @@ +use clap::{command, Arg}; + +fn main() { + let matches = command!() // requires `cargo` feature + .arg(Arg::new("name")) + .get_matches(); + + println!("name: {:?}", matches.get_one::<String>("name")); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/03_03_positional_mult.md b/third_party/rust/clap/examples/tutorial_builder/03_03_positional_mult.md new file mode 100644 index 0000000000..174ddd9f7b --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_03_positional_mult.md @@ -0,0 +1,23 @@ +```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"] + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/03_03_positional_mult.rs b/third_party/rust/clap/examples/tutorial_builder/03_03_positional_mult.rs new file mode 100644 index 0000000000..bcd288d8dc --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_03_positional_mult.rs @@ -0,0 +1,15 @@ +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); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/03_04_subcommands.md b/third_party/rust/clap/examples/tutorial_builder/03_04_subcommands.md new file mode 100644 index 0000000000..a32b776855 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_04_subcommands.md @@ -0,0 +1,59 @@ +```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 + +``` + +Since we specified [`Command::propagate_version`][crate::Command::propagate_version], the `--version` flag +is available in all subcommands: +```console +$ 03_04_subcommands --version +clap [..] + +$ 03_04_subcommands add --version +clap-add [..] + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/03_04_subcommands.rs b/third_party/rust/clap/examples/tutorial_builder/03_04_subcommands.rs new file mode 100644 index 0000000000..fbe23809e9 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_04_subcommands.rs @@ -0,0 +1,22 @@ +use clap::{arg, command, Command}; + +fn main() { + let matches = command!() // requires `cargo` feature + .propagate_version(true) + .subcommand_required(true) + .arg_required_else_help(true) + .subcommand( + Command::new("add") + .about("Adds files to myapp") + .arg(arg!([NAME])), + ) + .get_matches(); + + match matches.subcommand() { + Some(("add", sub_matches)) => println!( + "'myapp add' was used, name is: {:?}", + sub_matches.get_one::<String>("NAME") + ), + _ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"), + } +} diff --git a/third_party/rust/clap/examples/tutorial_builder/03_05_default_values.md b/third_party/rust/clap/examples/tutorial_builder/03_05_default_values.md new file mode 100644 index 0000000000..b62a415508 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_05_default_values.md @@ -0,0 +1,20 @@ +```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 + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/03_05_default_values.rs b/third_party/rust/clap/examples/tutorial_builder/03_05_default_values.rs new file mode 100644 index 0000000000..d259ee06ed --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/03_05_default_values.rs @@ -0,0 +1,18 @@ +use clap::{arg, command, value_parser}; + +fn main() { + let matches = command!() // requires `cargo` feature + .arg( + arg!([PORT]) + .value_parser(value_parser!(u16)) + .default_value("2020"), + ) + .get_matches(); + + println!( + "port: {:?}", + matches + .get_one::<u16>("PORT") + .expect("default ensures there is always a value") + ); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/04_01_enum.md b/third_party/rust/clap/examples/tutorial_builder/04_01_enum.md new file mode 100644 index 0000000000..ec4c0aea1d --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_01_enum.md @@ -0,0 +1,47 @@ +```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'. + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/04_01_enum.rs b/third_party/rust/clap/examples/tutorial_builder/04_01_enum.rs new file mode 100644 index 0000000000..aa9f41ce6d --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_01_enum.rs @@ -0,0 +1,66 @@ +use clap::{arg, builder::PossibleValue, command, value_parser, ValueEnum}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +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 std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.to_possible_value() + .expect("no values are skipped") + .get_name() + .fmt(f) + } +} + +impl std::str::FromStr for Mode { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + for variant in Self::value_variants() { + if variant.to_possible_value().unwrap().matches(s, false) { + return Ok(*variant); + } + } + Err(format!("invalid variant: {s}")) + } +} + +fn main() { + let matches = command!() // requires `cargo` feature + .arg( + arg!(<MODE>) + .help("What mode to run the program in") + .value_parser(value_parser!(Mode)), + ) + .get_matches(); + + // Note, it's safe to call unwrap() because the arg is required + match matches + .get_one::<Mode>("MODE") + .expect("'MODE' is required and parsing will fail if its missing") + { + Mode::Fast => { + println!("Hare"); + } + Mode::Slow => { + println!("Tortoise"); + } + } +} diff --git a/third_party/rust/clap/examples/tutorial_builder/04_01_possible.md b/third_party/rust/clap/examples/tutorial_builder/04_01_possible.md new file mode 100644 index 0000000000..fa2c835390 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_01_possible.md @@ -0,0 +1,27 @@ +```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'. + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/04_01_possible.rs b/third_party/rust/clap/examples/tutorial_builder/04_01_possible.rs new file mode 100644 index 0000000000..3da7aca74b --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_01_possible.rs @@ -0,0 +1,26 @@ +use clap::{arg, command}; + +fn main() { + let matches = command!() // requires `cargo` feature + .arg( + arg!(<MODE>) + .help("What mode to run the program in") + .value_parser(["fast", "slow"]), + ) + .get_matches(); + + // Note, it's safe to call unwrap() because the arg is required + match matches + .get_one::<String>("MODE") + .expect("'MODE' is required and parsing will fail if its missing") + .as_str() + { + "fast" => { + println!("Hare"); + } + "slow" => { + println!("Tortoise"); + } + _ => unreachable!(), + } +} diff --git a/third_party/rust/clap/examples/tutorial_builder/04_02_parse.md b/third_party/rust/clap/examples/tutorial_builder/04_02_parse.md new file mode 100644 index 0000000000..af03e95cbc --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_02_parse.md @@ -0,0 +1,29 @@ +```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'. + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/04_02_parse.rs b/third_party/rust/clap/examples/tutorial_builder/04_02_parse.rs new file mode 100644 index 0000000000..a3f79040d7 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_02_parse.rs @@ -0,0 +1,17 @@ +use clap::{arg, command, value_parser}; + +fn main() { + let matches = command!() // requires `cargo` feature + .arg( + arg!(<PORT>) + .help("Network port to use") + .value_parser(value_parser!(u16).range(1..)), + ) + .get_matches(); + + // Note, it's safe to call unwrap() because the arg is required + let port: u16 = *matches + .get_one::<u16>("PORT") + .expect("'PORT' is required and parsing will fail if its missing"); + println!("PORT = {port}"); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/04_02_validate.md b/third_party/rust/clap/examples/tutorial_builder/04_02_validate.md new file mode 100644 index 0000000000..949072bee5 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_02_validate.md @@ -0,0 +1,29 @@ +```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'. + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/04_02_validate.rs b/third_party/rust/clap/examples/tutorial_builder/04_02_validate.rs new file mode 100644 index 0000000000..0ce53d6544 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_02_validate.rs @@ -0,0 +1,36 @@ +use std::ops::RangeInclusive; + +use clap::{arg, command}; + +fn main() { + let matches = command!() // requires `cargo` feature + .arg( + arg!(<PORT>) + .help("Network port to use") + .value_parser(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") + .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> { + let port: usize = s + .parse() + .map_err(|_| format!("`{s}` isn't a port number"))?; + if PORT_RANGE.contains(&port) { + Ok(port as u16) + } else { + Err(format!( + "port not in range {}-{}", + PORT_RANGE.start(), + PORT_RANGE.end() + )) + } +} diff --git a/third_party/rust/clap/examples/tutorial_builder/04_03_relations.md b/third_party/rust/clap/examples/tutorial_builder/04_03_relations.md new file mode 100644 index 0000000000..dd0c35a683 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_03_relations.md @@ -0,0 +1,53 @@ +```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 + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/04_03_relations.rs b/third_party/rust/clap/examples/tutorial_builder/04_03_relations.rs new file mode 100644 index 0000000000..935e7a334c --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_03_relations.rs @@ -0,0 +1,78 @@ +use std::path::PathBuf; + +use clap::{arg, command, value_parser, ArgAction, ArgGroup}; + +fn main() { + // Create application like normal + let matches = command!() // requires `cargo` feature + // 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)) + // Create a group, make it required, and add the above arguments + .group( + ArgGroup::new("vers") + .required(true) + .args(["set-ver", "major", "minor", "patch"]), + ) + // Arguments can also be added to a group individually, these two arguments + // are part of the "input" group which is not required + .arg( + arg!([INPUT_FILE] "some regular input") + .value_parser(value_parser!(PathBuf)) + .group("input"), + ) + .arg( + arg!(--"spec-in" <SPEC_IN> "some special input argument") + .value_parser(value_parser!(PathBuf)) + .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"), + ) + .get_matches(); + + // Let's assume the old version 1.2.3 + let mut major = 1; + let mut minor = 2; + let mut patch = 3; + + // See if --set-ver was used to set the version manually + let version = if let Some(ver) = matches.get_one::<String>("set-ver") { + ver.to_owned() + } 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"), + ); + match (maj, min, pat) { + (true, _, _) => major += 1, + (_, true, _) => minor += 1, + (_, _, true) => patch += 1, + _ => unreachable!(), + }; + format!("{major}.{minor}.{patch}") + }; + + println!("Version: {version}"); + + // Check for usage of -c + if matches.contains_id("config") { + let input = matches + .get_one::<PathBuf>("INPUT_FILE") + .unwrap_or_else(|| matches.get_one::<PathBuf>("spec-in").unwrap()) + .display(); + println!( + "Doing work using input {} and config {}", + input, + matches.get_one::<PathBuf>("config").unwrap().display() + ); + } +} diff --git a/third_party/rust/clap/examples/tutorial_builder/04_04_custom.md b/third_party/rust/clap/examples/tutorial_builder/04_04_custom.md new file mode 100644 index 0000000000..0a83eb9b8e --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_04_custom.md @@ -0,0 +1,52 @@ +```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 + +``` diff --git a/third_party/rust/clap/examples/tutorial_builder/04_04_custom.rs b/third_party/rust/clap/examples/tutorial_builder/04_04_custom.rs new file mode 100644 index 0000000000..840b3aa4f7 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_04_custom.rs @@ -0,0 +1,84 @@ +use std::path::PathBuf; + +use clap::error::ErrorKind; +use clap::{arg, command, value_parser, ArgAction}; + +fn main() { + // Create application like normal + let mut cmd = command!() // requires `cargo` feature + // 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)) + // 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)), + ) + // 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))); + let matches = cmd.get_matches_mut(); + + // Let's assume the old version 1.2.3 + let mut major = 1; + let mut minor = 2; + let mut patch = 3; + + // See if --set-ver was used to set the version manually + let version = if let Some(ver) = matches.get_one::<String>("set-ver") { + if matches.get_flag("major") || matches.get_flag("minor") || matches.get_flag("patch") { + cmd.error( + ErrorKind::ArgumentConflict, + "Can't do relative and absolute version change", + ) + .exit(); + } + ver.to_string() + } else { + // Increment the one requested (in a real program, we'd reset the lower numbers) + let (maj, min, pat) = ( + matches.get_flag("major"), + matches.get_flag("minor"), + matches.get_flag("patch"), + ); + match (maj, min, pat) { + (true, false, false) => major += 1, + (false, true, false) => minor += 1, + (false, false, true) => patch += 1, + _ => { + cmd.error( + ErrorKind::ArgumentConflict, + "Can only modify one version field", + ) + .exit(); + } + }; + format!("{major}.{minor}.{patch}") + }; + + println!("Version: {version}"); + + // Check for usage of -c + if matches.contains_id("config") { + let input = matches + .get_one::<PathBuf>("INPUT_FILE") + .or_else(|| matches.get_one::<PathBuf>("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() + ); + } +} diff --git a/third_party/rust/clap/examples/tutorial_builder/05_01_assert.rs b/third_party/rust/clap/examples/tutorial_builder/05_01_assert.rs new file mode 100644 index 0000000000..b42c5762e7 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/05_01_assert.rs @@ -0,0 +1,25 @@ +use clap::{arg, command, value_parser}; + +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") + .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)), + ) +} + +#[test] +fn verify_cmd() { + cmd().debug_assert(); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/01_quick.md b/third_party/rust/clap/examples/tutorial_derive/01_quick.md new file mode 100644 index 0000000000..21bbfe1a65 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/01_quick.md @@ -0,0 +1,35 @@ +```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... + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/01_quick.rs b/third_party/rust/clap/examples/tutorial_derive/01_quick.rs new file mode 100644 index 0000000000..0edcd251f9 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/01_quick.rs @@ -0,0 +1,68 @@ +use std::path::PathBuf; + +use clap::{Parser, Subcommand}; + +#[derive(Parser)] +#[command(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")] + config: Option<PathBuf>, + + /// Turn debugging information on + #[arg(short, long, action = clap::ArgAction::Count)] + debug: u8, + + #[command(subcommand)] + command: Option<Commands>, +} + +#[derive(Subcommand)] +enum Commands { + /// does testing things + Test { + /// lists test values + #[arg(short, long)] + list: bool, + }, +} + +fn main() { + let cli = Cli::parse(); + + // You can check the value provided by positional arguments, or option arguments + if let Some(name) = cli.name.as_deref() { + println!("Value for name: {name}"); + } + + if let Some(config_path) = cli.config.as_deref() { + println!("Value for config: {}", config_path.display()); + } + + // You can see how many times a particular flag or argument occurred + // Note, only flags can have multiple occurrences + match cli.debug { + 0 => println!("Debug mode is off"), + 1 => println!("Debug mode is kind of on"), + 2 => println!("Debug mode is on"), + _ => println!("Don't be crazy"), + } + + // You can check for the existence of subcommands, and if found use their + // matches just as you would the top level cmd + match &cli.command { + Some(Commands::Test { list }) => { + if *list { + println!("Printing testing lists..."); + } else { + println!("Not printing testing lists..."); + } + } + None => {} + } + + // Continued program logic goes here... +} diff --git a/third_party/rust/clap/examples/tutorial_derive/02_app_settings.md b/third_party/rust/clap/examples/tutorial_derive/02_app_settings.md new file mode 100644 index 0000000000..90da3ae19b --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/02_app_settings.md @@ -0,0 +1,17 @@ +```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 + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/02_app_settings.rs b/third_party/rust/clap/examples/tutorial_derive/02_app_settings.rs new file mode 100644 index 0000000000..abea13d19f --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/02_app_settings.rs @@ -0,0 +1,18 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +#[command(next_line_help = true)] +struct Cli { + #[arg(long)] + two: String, + #[arg(long)] + one: String, +} + +fn main() { + let cli = Cli::parse(); + + println!("two: {:?}", cli.two); + println!("one: {:?}", cli.one); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/02_apps.md b/third_party/rust/clap/examples/tutorial_derive/02_apps.md new file mode 100644 index 0000000000..b5ae1147fd --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/02_apps.md @@ -0,0 +1,16 @@ +```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 + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/02_apps.rs b/third_party/rust/clap/examples/tutorial_derive/02_apps.rs new file mode 100644 index 0000000000..75455efcee --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/02_apps.rs @@ -0,0 +1,20 @@ +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)] +struct Cli { + #[arg(long)] + two: String, + #[arg(long)] + one: String, +} + +fn main() { + let cli = Cli::parse(); + + println!("two: {:?}", cli.two); + println!("one: {:?}", cli.one); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/02_crate.md b/third_party/rust/clap/examples/tutorial_derive/02_crate.md new file mode 100644 index 0000000000..3f64460640 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/02_crate.md @@ -0,0 +1,16 @@ +```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 [..] + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/02_crate.rs b/third_party/rust/clap/examples/tutorial_derive/02_crate.rs new file mode 100644 index 0000000000..33a7a4ee0f --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/02_crate.rs @@ -0,0 +1,17 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] // Read from `Cargo.toml` +struct Cli { + #[arg(long)] + two: String, + #[arg(long)] + one: String, +} + +fn main() { + let cli = Cli::parse(); + + println!("two: {:?}", cli.two); + println!("one: {:?}", cli.one); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/03_01_flag_bool.md b/third_party/rust/clap/examples/tutorial_derive/03_01_flag_bool.md new file mode 100644 index 0000000000..976226003b --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_01_flag_bool.md @@ -0,0 +1,26 @@ +```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'. + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/03_01_flag_bool.rs b/third_party/rust/clap/examples/tutorial_derive/03_01_flag_bool.rs new file mode 100644 index 0000000000..4f68fc834a --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_01_flag_bool.rs @@ -0,0 +1,14 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[arg(short, long)] + verbose: bool, +} + +fn main() { + let cli = Cli::parse(); + + println!("verbose: {:?}", cli.verbose); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/03_01_flag_count.md b/third_party/rust/clap/examples/tutorial_derive/03_01_flag_count.md new file mode 100644 index 0000000000..56ef9cafd9 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_01_flag_count.md @@ -0,0 +1,21 @@ +```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 + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/03_01_flag_count.rs b/third_party/rust/clap/examples/tutorial_derive/03_01_flag_count.rs new file mode 100644 index 0000000000..2b8a453ed2 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_01_flag_count.rs @@ -0,0 +1,14 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[arg(short, long, action = clap::ArgAction::Count)] + verbose: u8, +} + +fn main() { + let cli = Cli::parse(); + + println!("verbose: {:?}", cli.verbose); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/03_02_option.md b/third_party/rust/clap/examples/tutorial_derive/03_02_option.md new file mode 100644 index 0000000000..24e272aa81 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_02_option.md @@ -0,0 +1,30 @@ +```console +$ 03_02_option_derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 03_02_option_derive[EXE] [OPTIONS] + +Options: + -n, --name <NAME> + -h, --help Print help + -V, --version Print version + +$ 03_02_option_derive +name: None + +$ 03_02_option_derive --name bob +name: Some("bob") + +$ 03_02_option_derive --name=bob +name: Some("bob") + +$ 03_02_option_derive -n bob +name: Some("bob") + +$ 03_02_option_derive -n=bob +name: Some("bob") + +$ 03_02_option_derive -nbob +name: Some("bob") + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/03_02_option.rs b/third_party/rust/clap/examples/tutorial_derive/03_02_option.rs new file mode 100644 index 0000000000..aad8ef10b2 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_02_option.rs @@ -0,0 +1,14 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[arg(short, long)] + name: Option<String>, +} + +fn main() { + let cli = Cli::parse(); + + println!("name: {:?}", cli.name.as_deref()); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/03_02_option_mult.md b/third_party/rust/clap/examples/tutorial_derive/03_02_option_mult.md new file mode 100644 index 0000000000..cd2fe1301f --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_02_option_mult.md @@ -0,0 +1,30 @@ +```console +$ 03_02_option_mult_derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 03_02_option_mult_derive[EXE] [OPTIONS] + +Options: + -n, --name <NAME> + -h, --help Print help + -V, --version Print version + +$ 03_02_option_mult_derive +name: [] + +$ 03_02_option_mult_derive --name bob +name: ["bob"] + +$ 03_02_option_mult_derive --name=bob +name: ["bob"] + +$ 03_02_option_mult_derive -n bob +name: ["bob"] + +$ 03_02_option_mult_derive -n=bob +name: ["bob"] + +$ 03_02_option_mult_derive -nbob +name: ["bob"] + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/03_02_option_mult.rs b/third_party/rust/clap/examples/tutorial_derive/03_02_option_mult.rs new file mode 100644 index 0000000000..1caa440aaa --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_02_option_mult.rs @@ -0,0 +1,14 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[arg(short, long)] + name: Vec<String>, +} + +fn main() { + let cli = Cli::parse(); + + println!("name: {:?}", cli.name); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/03_03_positional.md b/third_party/rust/clap/examples/tutorial_derive/03_03_positional.md new file mode 100644 index 0000000000..9437c246f2 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_03_positional.md @@ -0,0 +1,20 @@ +```console +$ 03_03_positional_derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 03_03_positional_derive[EXE] [NAME] + +Arguments: + [NAME] + +Options: + -h, --help Print help + -V, --version Print version + +$ 03_03_positional_derive +name: None + +$ 03_03_positional_derive bob +name: Some("bob") + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/03_03_positional.rs b/third_party/rust/clap/examples/tutorial_derive/03_03_positional.rs new file mode 100644 index 0000000000..cf5f4054fb --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_03_positional.rs @@ -0,0 +1,13 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + name: Option<String>, +} + +fn main() { + let cli = Cli::parse(); + + println!("name: {:?}", cli.name.as_deref()); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/03_03_positional_mult.md b/third_party/rust/clap/examples/tutorial_derive/03_03_positional_mult.md new file mode 100644 index 0000000000..8f295e02ba --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_03_positional_mult.md @@ -0,0 +1,23 @@ +```console +$ 03_03_positional_mult_derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 03_03_positional_mult_derive[EXE] [NAME]... + +Arguments: + [NAME]... + +Options: + -h, --help Print help + -V, --version Print version + +$ 03_03_positional_mult_derive +name: [] + +$ 03_03_positional_mult_derive bob +name: ["bob"] + +$ 03_03_positional_mult_derive bob john +name: ["bob", "john"] + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/03_03_positional_mult.rs b/third_party/rust/clap/examples/tutorial_derive/03_03_positional_mult.rs new file mode 100644 index 0000000000..bd57a55c81 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_03_positional_mult.rs @@ -0,0 +1,13 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + name: Vec<String>, +} + +fn main() { + let cli = Cli::parse(); + + println!("name: {:?}", cli.name); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands.md b/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands.md new file mode 100644 index 0000000000..4d57dfeeeb --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands.md @@ -0,0 +1,60 @@ +```console +$ 03_04_subcommands_derive help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 03_04_subcommands_derive[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_derive help add +Adds files to myapp + +Usage: 03_04_subcommands_derive[EXE] add [NAME] + +Arguments: + [NAME] + +Options: + -h, --help Print help + -V, --version Print version + +$ 03_04_subcommands_derive add bob +'myapp add' was used, name is: Some("bob") + +``` + +When specifying commands with `command: Commands`, they are required. +Alternatively, you could do `command: Option<Commands>` to make it optional. +```console +$ 03_04_subcommands_derive +? failed +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 03_04_subcommands_derive[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 + +``` + +Since we specified [`#[command(propagate_version = true)]`][crate::Command::propagate_version], +the `--version` flag is available in all subcommands: +```console +$ 03_04_subcommands_derive --version +clap [..] + +$ 03_04_subcommands_derive add --version +clap-add [..] + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands.rs b/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands.rs new file mode 100644 index 0000000000..da7c644c70 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands.rs @@ -0,0 +1,27 @@ +use clap::{Parser, Subcommand}; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +#[command(propagate_version = true)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Adds files to myapp + Add { name: Option<String> }, +} + +fn main() { + let cli = Cli::parse(); + + // You can check for the existence of subcommands, and if found use their + // matches just as you would the top level cmd + match &cli.command { + Commands::Add { name } => { + println!("'myapp add' was used, name is: {name:?}") + } + } +} diff --git a/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands_alt.rs b/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands_alt.rs new file mode 100644 index 0000000000..80b0ec6b95 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands_alt.rs @@ -0,0 +1,32 @@ +use clap::{Args, Parser, Subcommand}; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +#[command(propagate_version = true)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Adds files to myapp + Add(AddArgs), +} + +#[derive(Args)] +struct AddArgs { + name: Option<String>, +} + +fn main() { + let cli = Cli::parse(); + + // You can check for the existence of subcommands, and if found use their + // matches just as you would the top level cmd + match &cli.command { + Commands::Add(name) => { + println!("'myapp add' was used, name is: {:?}", name.name) + } + } +} diff --git a/third_party/rust/clap/examples/tutorial_derive/03_05_default_values.md b/third_party/rust/clap/examples/tutorial_derive/03_05_default_values.md new file mode 100644 index 0000000000..994cb33282 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_05_default_values.md @@ -0,0 +1,20 @@ +```console +$ 03_05_default_values_derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 03_05_default_values_derive[EXE] [PORT] + +Arguments: + [PORT] [default: 2020] + +Options: + -h, --help Print help + -V, --version Print version + +$ 03_05_default_values_derive +port: 2020 + +$ 03_05_default_values_derive 22 +port: 22 + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/03_05_default_values.rs b/third_party/rust/clap/examples/tutorial_derive/03_05_default_values.rs new file mode 100644 index 0000000000..bc33a931ab --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_05_default_values.rs @@ -0,0 +1,14 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[arg(default_value_t = 2020)] + port: u16, +} + +fn main() { + let cli = Cli::parse(); + + println!("port: {:?}", cli.port); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/04_01_enum.md b/third_party/rust/clap/examples/tutorial_derive/04_01_enum.md new file mode 100644 index 0000000000..89db08c96e --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/04_01_enum.md @@ -0,0 +1,47 @@ +```console +$ 04_01_enum_derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 04_01_enum_derive[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_derive -h +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 04_01_enum_derive[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_derive fast +Hare + +$ 04_01_enum_derive slow +Tortoise + +$ 04_01_enum_derive medium +? failed +error: invalid value 'medium' for '<MODE>' + [possible values: fast, slow] + +For more information, try '--help'. + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/04_01_enum.rs b/third_party/rust/clap/examples/tutorial_derive/04_01_enum.rs new file mode 100644 index 0000000000..41410a3bdc --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/04_01_enum.rs @@ -0,0 +1,32 @@ +use clap::{Parser, ValueEnum}; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + /// What mode to run the program in + #[arg(value_enum)] + mode: Mode, +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +enum Mode { + /// Run swiftly + Fast, + /// Crawl slowly but steadily + /// + /// This paragraph is ignored because there is no long help text for possible values. + Slow, +} + +fn main() { + let cli = Cli::parse(); + + match cli.mode { + Mode::Fast => { + println!("Hare"); + } + Mode::Slow => { + println!("Tortoise"); + } + } +} diff --git a/third_party/rust/clap/examples/tutorial_derive/04_02_parse.md b/third_party/rust/clap/examples/tutorial_derive/04_02_parse.md new file mode 100644 index 0000000000..573b43cc79 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/04_02_parse.md @@ -0,0 +1,29 @@ +```console +$ 04_02_parse_derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 04_02_parse_derive[EXE] <PORT> + +Arguments: + <PORT> Network port to use + +Options: + -h, --help Print help + -V, --version Print version + +$ 04_02_parse_derive 22 +PORT = 22 + +$ 04_02_parse_derive 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'. + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/04_02_parse.rs b/third_party/rust/clap/examples/tutorial_derive/04_02_parse.rs new file mode 100644 index 0000000000..a40c6828b6 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/04_02_parse.rs @@ -0,0 +1,15 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + /// Network port to use + #[arg(value_parser = clap::value_parser!(u16).range(1..))] + port: u16, +} + +fn main() { + let cli = Cli::parse(); + + println!("PORT = {}", cli.port); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/04_02_validate.md b/third_party/rust/clap/examples/tutorial_derive/04_02_validate.md new file mode 100644 index 0000000000..f8835fdca0 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/04_02_validate.md @@ -0,0 +1,29 @@ +```console +$ 04_02_validate_derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 04_02_validate_derive[EXE] <PORT> + +Arguments: + <PORT> Network port to use + +Options: + -h, --help Print help + -V, --version Print version + +$ 04_02_validate_derive 22 +PORT = 22 + +$ 04_02_validate_derive foobar +? failed +error: invalid value 'foobar' for '<PORT>': `foobar` isn't a port number + +For more information, try '--help'. + +$ 04_02_validate_derive 0 +? failed +error: invalid value '0' for '<PORT>': port not in range 1-65535 + +For more information, try '--help'. + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/04_02_validate.rs b/third_party/rust/clap/examples/tutorial_derive/04_02_validate.rs new file mode 100644 index 0000000000..3391d5b72d --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/04_02_validate.rs @@ -0,0 +1,34 @@ +use std::ops::RangeInclusive; + +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + /// Network port to use + #[arg(value_parser = port_in_range)] + port: u16, +} + +fn main() { + let cli = Cli::parse(); + + println!("PORT = {}", cli.port); +} + +const PORT_RANGE: RangeInclusive<usize> = 1..=65535; + +fn port_in_range(s: &str) -> Result<u16, String> { + let port: usize = s + .parse() + .map_err(|_| format!("`{s}` isn't a port number"))?; + if PORT_RANGE.contains(&port) { + Ok(port as u16) + } else { + Err(format!( + "port not in range {}-{}", + PORT_RANGE.start(), + PORT_RANGE.end() + )) + } +} diff --git a/third_party/rust/clap/examples/tutorial_derive/04_03_relations.md b/third_party/rust/clap/examples/tutorial_derive/04_03_relations.md new file mode 100644 index 0000000000..6eafb9fbbd --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/04_03_relations.md @@ -0,0 +1,53 @@ +```console +$ 04_03_relations_derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 04_03_relations_derive[EXE] [OPTIONS] <--set-ver <VER>|--major|--minor|--patch> [INPUT_FILE] + +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_derive +? failed +error: the following required arguments were not provided: + <--set-ver <VER>|--major|--minor|--patch> + +Usage: 04_03_relations_derive[EXE] <--set-ver <VER>|--major|--minor|--patch> [INPUT_FILE] + +For more information, try '--help'. + +$ 04_03_relations_derive --major +Version: 2.2.3 + +$ 04_03_relations_derive --major --minor +? failed +error: the argument '--major' cannot be used with '--minor' + +Usage: 04_03_relations_derive[EXE] <--set-ver <VER>|--major|--minor|--patch> [INPUT_FILE] + +For more information, try '--help'. + +$ 04_03_relations_derive --major -c config.toml +? failed +error: the following required arguments were not provided: + <INPUT_FILE|--spec-in <SPEC_IN>> + +Usage: 04_03_relations_derive[EXE] -c <CONFIG> <--set-ver <VER>|--major|--minor|--patch> <INPUT_FILE|--spec-in <SPEC_IN>> + +For more information, try '--help'. + +$ 04_03_relations_derive --major -c config.toml --spec-in input.txt +Version: 2.2.3 +Doing work using input input.txt and config config.toml + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/04_03_relations.rs b/third_party/rust/clap/examples/tutorial_derive/04_03_relations.rs new file mode 100644 index 0000000000..8657ebe837 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/04_03_relations.rs @@ -0,0 +1,75 @@ +use clap::{Args, Parser}; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[command(flatten)] + vers: Vers, + + /// some regular input + #[arg(group = "input")] + input_file: Option<String>, + + /// some special input argument + #[arg(long, group = "input")] + spec_in: Option<String>, + + #[arg(short, requires = "input")] + config: Option<String>, +} + +#[derive(Args)] +#[group(required = true, multiple = false)] +struct Vers { + /// set version manually + #[arg(long, value_name = "VER")] + set_ver: Option<String>, + + /// auto inc major + #[arg(long)] + major: bool, + + /// auto inc minor + #[arg(long)] + minor: bool, + + /// auto inc patch + #[arg(long)] + patch: bool, +} + +fn main() { + let cli = Cli::parse(); + + // Let's assume the old version 1.2.3 + let mut major = 1; + let mut minor = 2; + let mut patch = 3; + + // See if --set_ver was used to set the version manually + let vers = &cli.vers; + let version = if let Some(ver) = vers.set_ver.as_deref() { + ver.to_string() + } else { + // Increment the one requested (in a real program, we'd reset the lower numbers) + let (maj, min, pat) = (vers.major, vers.minor, vers.patch); + match (maj, min, pat) { + (true, _, _) => major += 1, + (_, true, _) => minor += 1, + (_, _, true) => patch += 1, + _ => unreachable!(), + }; + format!("{major}.{minor}.{patch}") + }; + + println!("Version: {version}"); + + // Check for usage of -c + if let Some(config) = cli.config.as_deref() { + let input = cli + .input_file + .as_deref() + .unwrap_or_else(|| cli.spec_in.as_deref().unwrap()); + println!("Doing work using input {input} and config {config}"); + } +} diff --git a/third_party/rust/clap/examples/tutorial_derive/04_04_custom.md b/third_party/rust/clap/examples/tutorial_derive/04_04_custom.md new file mode 100644 index 0000000000..c601e9fb4d --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/04_04_custom.md @@ -0,0 +1,52 @@ +```console +$ 04_04_custom_derive --help +A simple to use, efficient, and full-featured Command Line Argument Parser + +Usage: 04_04_custom_derive[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_derive +? failed +error: Can only modify one version field + +Usage: clap [OPTIONS] [INPUT_FILE] + +For more information, try '--help'. + +$ 04_04_custom_derive --major +Version: 2.2.3 + +$ 04_04_custom_derive --major --minor +? failed +error: Can only modify one version field + +Usage: clap [OPTIONS] [INPUT_FILE] + +For more information, try '--help'. + +$ 04_04_custom_derive --major -c config.toml +? failed +Version: 2.2.3 +error: INPUT_FILE or --spec-in is required when using --config + +Usage: clap [OPTIONS] [INPUT_FILE] + +For more information, try '--help'. + +$ 04_04_custom_derive --major -c config.toml --spec-in input.txt +Version: 2.2.3 +Doing work using input input.txt and config config.toml + +``` diff --git a/third_party/rust/clap/examples/tutorial_derive/04_04_custom.rs b/third_party/rust/clap/examples/tutorial_derive/04_04_custom.rs new file mode 100644 index 0000000000..a84b5ed5fe --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/04_04_custom.rs @@ -0,0 +1,91 @@ +use clap::error::ErrorKind; +use clap::{CommandFactory, Parser}; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + /// set version manually + #[arg(long, value_name = "VER")] + set_ver: Option<String>, + + /// auto inc major + #[arg(long)] + major: bool, + + /// auto inc minor + #[arg(long)] + minor: bool, + + /// auto inc patch + #[arg(long)] + patch: bool, + + /// some regular input + input_file: Option<String>, + + /// some special input argument + #[arg(long)] + spec_in: Option<String>, + + #[arg(short)] + config: Option<String>, +} + +fn main() { + let cli = Cli::parse(); + + // Let's assume the old version 1.2.3 + let mut major = 1; + let mut minor = 2; + let mut patch = 3; + + // See if --set-ver was used to set the version manually + let version = if let Some(ver) = cli.set_ver.as_deref() { + if cli.major || cli.minor || cli.patch { + let mut cmd = Cli::command(); + cmd.error( + ErrorKind::ArgumentConflict, + "Can't do relative and absolute version change", + ) + .exit(); + } + ver.to_string() + } else { + // Increment the one requested (in a real program, we'd reset the lower numbers) + let (maj, min, pat) = (cli.major, cli.minor, cli.patch); + match (maj, min, pat) { + (true, false, false) => major += 1, + (false, true, false) => minor += 1, + (false, false, true) => patch += 1, + _ => { + let mut cmd = Cli::command(); + cmd.error( + ErrorKind::ArgumentConflict, + "Can only modify one version field", + ) + .exit(); + } + }; + format!("{major}.{minor}.{patch}") + }; + + println!("Version: {version}"); + + // Check for usage of -c + if let Some(config) = cli.config.as_deref() { + let input = cli + .input_file + .as_deref() + // 'or' is preferred to 'or_else' here since `Option::as_deref` is 'const' + .or(cli.spec_in.as_deref()) + .unwrap_or_else(|| { + let mut cmd = Cli::command(); + cmd.error( + ErrorKind::MissingRequiredArgument, + "INPUT_FILE or --spec-in is required when using --config", + ) + .exit() + }); + println!("Doing work using input {input} and config {config}"); + } +} diff --git a/third_party/rust/clap/examples/tutorial_derive/05_01_assert.rs b/third_party/rust/clap/examples/tutorial_derive/05_01_assert.rs new file mode 100644 index 0000000000..42ebda9a1e --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/05_01_assert.rs @@ -0,0 +1,20 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + /// Network port to use + port: u16, +} + +fn main() { + let cli = Cli::parse(); + + println!("PORT = {}", cli.port); +} + +#[test] +fn verify_cli() { + use clap::CommandFactory; + Cli::command().debug_assert() +} diff --git a/third_party/rust/clap/examples/typed-derive.md b/third_party/rust/clap/examples/typed-derive.md new file mode 100644 index 0000000000..9221a310da --- /dev/null +++ b/third_party/rust/clap/examples/typed-derive.md @@ -0,0 +1,131 @@ +**This requires enabling the [`derive` feature flag][crate::_features].** + +Help: +```console +$ typed-derive --help +Usage: typed-derive[EXE] [OPTIONS] + +Options: + -O <OPTIMIZATION> Implicitly using `std::str::FromStr` + -I <DIR> Allow invalid UTF-8 paths + --bind <BIND> Handle IP addresses + --sleep <SLEEP> Allow human-readable durations + -D <DEFINES> Hand-written parser for tuples + --port <PORT> Support for discrete numbers [default: 22] [possible values: 22, 80] + --log-level <LOG_LEVEL> Support enums from a foreign crate that don't implement `ValueEnum` [default: info] [possible values: trace, debug, info, warn, error] + -h, --help Print help + +``` + +Optimization-level (number) +```console +$ typed-derive -O 1 +Args { optimization: Some(1), include: None, bind: None, sleep: None, defines: [], port: 22, log_level: Info } + +$ typed-derive -O plaid +? failed +error: invalid value 'plaid' for '-O <OPTIMIZATION>': invalid digit found in string + +For more information, try '--help'. + +``` + +Include (path) +```console +$ typed-derive -I../hello +Args { optimization: None, include: Some("../hello"), bind: None, sleep: None, defines: [], port: 22, log_level: Info } + +``` + +IP Address +```console +$ typed-derive --bind 192.0.0.1 +Args { optimization: None, include: None, bind: Some(192.0.0.1), sleep: None, defines: [], port: 22, log_level: Info } + +$ typed-derive --bind localhost +? failed +error: invalid value 'localhost' for '--bind <BIND>': invalid IP address syntax + +For more information, try '--help'. + +``` + +Time +```console +$ typed-derive --sleep 10s +Args { optimization: None, include: None, bind: None, sleep: Some(Duration(10s)), defines: [], port: 22, log_level: Info } + +$ typed-derive --sleep forever +? failed +error: invalid value 'forever' for '--sleep <SLEEP>': expected number at 0 + +For more information, try '--help'. + +``` + +Defines (key-value pairs) +```console +$ typed-derive -D Foo=10 -D Alice=30 +Args { optimization: None, include: None, bind: None, sleep: None, defines: [("Foo", 10), ("Alice", 30)], port: 22, log_level: Info } + +$ typed-derive -D Foo +? failed +error: invalid value 'Foo' for '-D <DEFINES>': invalid KEY=value: no `=` found in `Foo` + +For more information, try '--help'. + +$ typed-derive -D Foo=Bar +? failed +error: invalid value 'Foo=Bar' for '-D <DEFINES>': invalid digit found in string + +For more information, try '--help'. + +``` + +Discrete numbers +```console +$ typed-derive --port 22 +Args { optimization: None, include: None, bind: None, sleep: None, defines: [], port: 22, log_level: Info } + +$ typed-derive --port 80 +Args { optimization: None, include: None, bind: None, sleep: None, defines: [], port: 80, log_level: Info } + +$ typed-derive --port +? failed +error: a value is required for '--port <PORT>' but none was supplied + [possible values: 22, 80] + +For more information, try '--help'. + +$ typed-derive --port 3000 +? failed +error: invalid value '3000' for '--port <PORT>' + [possible values: 22, 80] + +For more information, try '--help'. + +``` + +Enums from crates that can't implement `ValueEnum` +```console +$ typed-derive --log-level debug +Args { optimization: None, include: None, bind: None, sleep: None, defines: [], port: 22, log_level: Debug } + +$ typed-derive --log-level error +Args { optimization: None, include: None, bind: None, sleep: None, defines: [], port: 22, log_level: Error } + +$ typed-derive --log-level +? failed +error: a value is required for '--log-level <LOG_LEVEL>' but none was supplied + [possible values: trace, debug, info, warn, error] + +For more information, try '--help'. + +$ typed-derive --log-level critical +? failed +error: invalid value 'critical' for '--log-level <LOG_LEVEL>' + [possible values: trace, debug, info, warn, error] + +For more information, try '--help'. + +``` diff --git a/third_party/rust/clap/examples/typed-derive.rs b/third_party/rust/clap/examples/typed-derive.rs new file mode 100644 index 0000000000..1a36f11d5b --- /dev/null +++ b/third_party/rust/clap/examples/typed-derive.rs @@ -0,0 +1,102 @@ +use clap::builder::TypedValueParser as _; +use clap::Parser; +use std::error::Error; + +#[derive(Parser, Debug)] // requires `derive` feature +#[command(term_width = 0)] // Just to make testing across clap features easier +struct Args { + /// Implicitly using `std::str::FromStr` + #[arg(short = 'O')] + optimization: Option<usize>, + + /// Allow invalid UTF-8 paths + #[arg(short = 'I', value_name = "DIR", value_hint = clap::ValueHint::DirPath)] + include: Option<std::path::PathBuf>, + + /// Handle IP addresses + #[arg(long)] + bind: Option<std::net::IpAddr>, + + /// Allow human-readable durations + #[arg(long)] + sleep: Option<humantime::Duration>, + + /// Hand-written parser for tuples + #[arg(short = 'D', value_parser = parse_key_val::<String, i32>)] + defines: Vec<(String, i32)>, + + /// Support for discrete numbers + #[arg( + long, + default_value_t = 22, + value_parser = clap::builder::PossibleValuesParser::new(["22", "80"]) + .map(|s| s.parse::<usize>().unwrap()), + )] + port: usize, + + /// Support enums from a foreign crate that don't implement `ValueEnum` + #[arg( + long, + default_value_t = foreign_crate::LogLevel::Info, + value_parser = clap::builder::PossibleValuesParser::new(["trace", "debug", "info", "warn", "error"]) + .map(|s| s.parse::<foreign_crate::LogLevel>().unwrap()), + )] + log_level: foreign_crate::LogLevel, +} + +/// Parse a single key-value pair +fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>> +where + T: std::str::FromStr, + T::Err: Error + Send + Sync + 'static, + U: std::str::FromStr, + U::Err: Error + Send + Sync + 'static, +{ + let pos = s + .find('=') + .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?; + Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) +} + +mod foreign_crate { + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub enum LogLevel { + Trace, + Debug, + Info, + Warn, + Error, + } + + impl std::fmt::Display for LogLevel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::Trace => "trace", + Self::Debug => "debug", + Self::Info => "info", + Self::Warn => "warn", + Self::Error => "error", + }; + s.fmt(f) + } + } + impl std::str::FromStr for LogLevel { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "trace" => Ok(Self::Trace), + "debug" => Ok(Self::Debug), + "info" => Ok(Self::Info), + "warn" => Ok(Self::Warn), + "error" => Ok(Self::Error), + _ => Err(format!("Unknown log level: {s}")), + } + } + } +} + +fn main() { + let args = Args::parse(); + println!("{args:?}"); +} |