From 5e20a29b4fdc8a2d442d1093681b396dcb4b816b Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 7 Jan 2020 11:18:04 +0000 Subject: Add structopt dependency in version 0.3.7 This patch series replaces argparse with structopt in the argument handling code. As a first step, we need structopt as a dependency. Import subrepo structopt/:structopt at efbdda4753592e27bc430fb01f7b9650b2f3174d Import subrepo bitflags/:bitflags at 30668016aca6bd3b02c766e8347e0b4080d4c296 Import subrepo clap/:clap at 784524f7eb193e35f81082cc69454c8c21b948f7 Import subrepo heck/:heck at 093d56fbf001e1506e56dbfa38631d99b1066df1 Import subrepo proc-macro-error/:proc-macro-error at 6c4cfe79a622c5de8ae68557993542be46eacae2 Import subrepo proc-macro2/:proc-macro2 at d5d48eddca4566e5438e8a2cbed4a74e049544de Import subrepo quote/:quote at 727436c6c137b20f0f34dde5d8fda2679b9747ad Import subrepo rustversion/:rustversion at 0c5663313516263059ce9059ef81fc7a1cf655ca Import subrepo syn-mid/:syn-mid at 5d3d85414a9e6674e1857ec22a87b96e04a6851a Import subrepo syn/:syn at e87c27e87f6f4ef8919d0372bdb056d53ef0d8f3 Import subrepo textwrap/:textwrap at abcd618beae3f74841032aa5b53c1086b0a57ca2 Import subrepo unicode-segmentation/:unicode-segmentation at 637c9874c4fe0c205ff27787faf150a40295c6c3 Import subrepo unicode-width/:unicode-width at 3033826f8bf05e82724140a981d5941e48fce393 Import subrepo unicode-xid/:unicode-xid at 4baae9fffb156ba229665b972a9cd5991787ceb7 --- clap/examples/01a_quick_example.rs | 79 ++++++++++++++++ clap/examples/01b_quick_example.rs | 93 +++++++++++++++++++ clap/examples/01c_quick_example.rs | 75 +++++++++++++++ clap/examples/02_apps.rs | 30 ++++++ clap/examples/03_args.rs | 84 +++++++++++++++++ clap/examples/04_using_matches.rs | 54 +++++++++++ clap/examples/05_flag_args.rs | 56 +++++++++++ clap/examples/06_positional_args.rs | 56 +++++++++++ clap/examples/07_option_args.rs | 71 ++++++++++++++ clap/examples/08_subcommands.rs | 57 ++++++++++++ clap/examples/09_auto_version.rs | 29 ++++++ clap/examples/10_default_values.rs | 40 ++++++++ clap/examples/11_only_specific_values.rs | 33 +++++++ clap/examples/12_typed_values.rs | 50 ++++++++++ clap/examples/13a_enum_values_automatic.rs | 68 ++++++++++++++ clap/examples/13b_enum_values_manual.rs | 54 +++++++++++ clap/examples/14_groups.rs | 87 ++++++++++++++++++ clap/examples/15_custom_validator.rs | 37 ++++++++ clap/examples/16_app_settings.rs | 41 +++++++++ clap/examples/17_yaml.rs | 53 +++++++++++ clap/examples/17_yaml.yml | 97 +++++++++++++++++++ clap/examples/18_builder_macro.rs | 84 +++++++++++++++++ clap/examples/19_auto_authors.rs | 15 +++ clap/examples/20_subcommands.rs | 143 +++++++++++++++++++++++++++++ clap/examples/21_aliases.rs | 39 ++++++++ clap/examples/22_stop_parsing_with_--.rs | 25 +++++ 26 files changed, 1550 insertions(+) create mode 100644 clap/examples/01a_quick_example.rs create mode 100644 clap/examples/01b_quick_example.rs create mode 100644 clap/examples/01c_quick_example.rs create mode 100644 clap/examples/02_apps.rs create mode 100644 clap/examples/03_args.rs create mode 100644 clap/examples/04_using_matches.rs create mode 100644 clap/examples/05_flag_args.rs create mode 100644 clap/examples/06_positional_args.rs create mode 100644 clap/examples/07_option_args.rs create mode 100644 clap/examples/08_subcommands.rs create mode 100644 clap/examples/09_auto_version.rs create mode 100644 clap/examples/10_default_values.rs create mode 100644 clap/examples/11_only_specific_values.rs create mode 100644 clap/examples/12_typed_values.rs create mode 100644 clap/examples/13a_enum_values_automatic.rs create mode 100644 clap/examples/13b_enum_values_manual.rs create mode 100644 clap/examples/14_groups.rs create mode 100644 clap/examples/15_custom_validator.rs create mode 100644 clap/examples/16_app_settings.rs create mode 100644 clap/examples/17_yaml.rs create mode 100644 clap/examples/17_yaml.yml create mode 100644 clap/examples/18_builder_macro.rs create mode 100644 clap/examples/19_auto_authors.rs create mode 100644 clap/examples/20_subcommands.rs create mode 100644 clap/examples/21_aliases.rs create mode 100644 clap/examples/22_stop_parsing_with_--.rs (limited to 'clap/examples') diff --git a/clap/examples/01a_quick_example.rs b/clap/examples/01a_quick_example.rs new file mode 100644 index 0000000..c7fa20f --- /dev/null +++ b/clap/examples/01a_quick_example.rs @@ -0,0 +1,79 @@ +extern crate clap; + +use clap::{App, SubCommand}; + +fn main() { + + // This example shows how to create an application with several arguments using usage strings, which can be + // far less verbose that shown in 01b_QuickExample.rs, but is more readable. The downside is you cannot set + // the more advanced configuration options using this method (well...actually you can, you'll see ;) ) + // + // The example below is functionally identical to the 01b_quick_example.rs and 01c_quick_example.rs + // + // Create an application with 5 possible arguments (2 auto generated) and 2 subcommands (1 auto generated) + // - A config file + // + Uses "-c filename" or "--config filename" + // - An output file + // + A positional argument (i.e. "$ myapp output_filename") + // - A debug flag + // + Uses "-d" or "--debug" + // + Allows multiple occurrences of such as "-dd" (for vary levels of debugging, as an example) + // - A help flag (automatically generated by clap) + // + Uses "-h" or "--help" (Only autogenerated if you do NOT specify your own "-h" or "--help") + // - A version flag (automatically generated by clap) + // + Uses "-V" or "--version" (Only autogenerated if you do NOT specify your own "-V" or "--version") + // - A subcommand "test" (subcommands behave like their own apps, with their own arguments + // + Used by "$ myapp test" with the following arguments + // > A list flag + // = Uses "-l" (usage is "$ myapp test -l" + // > A help flag (automatically generated by clap + // = Uses "-h" or "--help" (full usage "$ myapp test -h" or "$ myapp test --help") + // > A version flag (automatically generated by clap + // = Uses "-V" or "--version" (full usage "$ myapp test -V" or "$ myapp test --version") + // - A subcommand "help" (automatically generated by clap because we specified a subcommand of our own) + // + Used by "$ myapp help" (same functionality as "-h" or "--help") + let matches = App::new("MyApp") + .version("1.0") + .author("Kevin K. ") + .about("Does awesome things") + .args_from_usage("-c, --config=[FILE] 'Sets a custom config file' + 'Sets an optional output file' + -d... 'Turn debugging information on'") + .subcommand(SubCommand::with_name("test") + .about("does testing things") + .arg_from_usage("-l, --list 'lists test values'")) + .get_matches(); + + // You can check the value provided by positional arguments, or option arguments + if let Some(o) = matches.value_of("output") { + println!("Value for output: {}", o); + } + + if let Some(c) = matches.value_of("config") { + println!("Value for config: {}", c); + } + + // You can see how many times a particular flag or argument occurred + // Note, only flags can have multiple occurrences + match matches.occurrences_of("d") { + 0 => println!("Debug mode is off"), + 1 => println!("Debug mode is kind of on"), + 2 => println!("Debug mode is on"), + 3 | _ => println!("Don't be crazy"), + } + + // You can check for the existence of subcommands, and if found use their + // matches just as you would the top level app + if let Some(matches) = matches.subcommand_matches("test") { + // "$ myapp test" was run + if matches.is_present("list") { + // "$ myapp test -l" was run + println!("Printing testing lists..."); + } else { + println!("Not printing testing lists..."); + } + } + + + // Continued program logic goes here... +} diff --git a/clap/examples/01b_quick_example.rs b/clap/examples/01b_quick_example.rs new file mode 100644 index 0000000..7f455a8 --- /dev/null +++ b/clap/examples/01b_quick_example.rs @@ -0,0 +1,93 @@ +extern crate clap; + +use clap::{App, Arg, SubCommand}; + +fn main() { + + // This method shows the traditional, and slightly more configurable way to set up arguments. This method is + // more verbose, but allows setting more configuration options, and even supports easier dynamic generation. + // + // The example below is functionally identical to the 01a_quick_example.rs and 01c_quick_example.rs + // + // *NOTE:* You can actually achieve the best of both worlds by using Arg::from_usage() (instead of Arg::with_name()) + // and *then* setting any additional properties. + // + // Create an application with 5 possible arguments (2 auto generated) and 2 subcommands (1 auto generated) + // - A config file + // + Uses "-c filename" or "--config filename" + // - An output file + // + A positional argument (i.e. "$ myapp output_filename") + // - A debug flag + // + Uses "-d" or "--debug" + // + Allows multiple occurrences of such as "-dd" (for vary levels of debugging, as an example) + // - A help flag (automatically generated by clap) + // + Uses "-h" or "--help" (Only autogenerated if you do NOT specify your own "-h" or "--help") + // - A version flag (automatically generated by clap) + // + Uses "-V" or "--version" (Only autogenerated if you do NOT specify your own "-V" or "--version") + // - A subcommand "test" (subcommands behave like their own apps, with their own arguments + // + Used by "$ myapp test" with the following arguments + // > A list flag + // = Uses "-l" (usage is "$ myapp test -l" + // > A help flag (automatically generated by clap + // = Uses "-h" or "--help" (full usage "$ myapp test -h" or "$ myapp test --help") + // > A version flag (automatically generated by clap + // = Uses "-V" or "--version" (full usage "$ myapp test -V" or "$ myapp test --version") + // - A subcommand "help" (automatically generated by clap because we specified a subcommand of our own) + // + Used by "$ myapp help" (same functionality as "-h" or "--help") + let matches = App::new("MyApp") + .version("1.0") + .author("Kevin K. ") + .about("Does awesome things") + .arg(Arg::with_name("config") + .short("c") + .long("config") + .value_name("FILE") + .help("Sets a custom config file") + .takes_value(true)) + .arg(Arg::with_name("output") + .help("Sets an optional output file") + .index(1)) + .arg(Arg::with_name("debug") + .short("d") + .multiple(true) + .help("Turn debugging information on")) + .subcommand(SubCommand::with_name("test") + .about("does testing things") + .arg(Arg::with_name("list") + .short("l") + .help("lists test values"))) + .get_matches(); + + // You can check the value provided by positional arguments, or option arguments + if let Some(o) = matches.value_of("output") { + println!("Value for output: {}", o); + } + + if let Some(c) = matches.value_of("config") { + println!("Value for config: {}", c); + } + + // You can see how many times a particular flag or argument occurred + // Note, only flags can have multiple occurrences + match matches.occurrences_of("debug") { + 0 => println!("Debug mode is off"), + 1 => println!("Debug mode is kind of on"), + 2 => println!("Debug mode is on"), + 3 | _ => println!("Don't be crazy"), + } + + // You can check for the existence of subcommands, and if found use their + // matches just as you would the top level app + if let Some(matches) = matches.subcommand_matches("test") { + // "$ myapp test" was run + if matches.is_present("list") { + // "$ myapp test -l" was run + println!("Printing testing lists..."); + } else { + println!("Not printing testing lists..."); + } + } + + + // Continued program logic goes here... +} diff --git a/clap/examples/01c_quick_example.rs b/clap/examples/01c_quick_example.rs new file mode 100644 index 0000000..071bdc0 --- /dev/null +++ b/clap/examples/01c_quick_example.rs @@ -0,0 +1,75 @@ +#[macro_use] +extern crate clap; + +fn main() { + // This example shows how to create an application with several arguments using macro builder. + // It combines the simplicity of the from_usage methods and the performance of the Builder Pattern. + // + // The example below is functionally identical to the one in 01a_quick_example.rs and 01b_quick_example.rs + // + // Create an application with 5 possible arguments (2 auto generated) and 2 subcommands (1 auto generated) + // - A config file + // + Uses "-c filename" or "--config filename" + // - An output file + // + A positional argument (i.e. "$ myapp output_filename") + // - A debug flag + // + Uses "-d" or "--debug" + // + Allows multiple occurrences of such as "-dd" (for vary levels of debugging, as an example) + // - A help flag (automatically generated by clap) + // + Uses "-h" or "--help" (Only autogenerated if you do NOT specify your own "-h" or "--help") + // - A version flag (automatically generated by clap) + // + Uses "-V" or "--version" (Only autogenerated if you do NOT specify your own "-V" or "--version") + // - A subcommand "test" (subcommands behave like their own apps, with their own arguments + // + Used by "$ myapp test" with the following arguments + // > A list flag + // = Uses "-l" (usage is "$ myapp test -l" + // > A help flag (automatically generated by clap + // = Uses "-h" or "--help" (full usage "$ myapp test -h" or "$ myapp test --help") + // > A version flag (automatically generated by clap + // = Uses "-V" or "--version" (full usage "$ myapp test -V" or "$ myapp test --version") + // - A subcommand "help" (automatically generated by clap because we specified a subcommand of our own) + // + Used by "$ myapp help" (same functionality as "-h" or "--help") + let matches = clap_app!(myapp => + (version: "1.0") + (author: "Kevin K. ") + (about: "Does awesome things") + (@arg CONFIG: -c --config +takes_value "Sets a custom config file") + (@arg INPUT: +required "Sets the input file to use") + (@arg debug: -d ... "Sets the level of debugging information") + (@subcommand test => + (about: "controls testing features") + (version: "1.3") + (author: "Someone E. ") + (@arg verbose: -v --verbose "Print test information verbosely") + ) + ).get_matches(); + + // Calling .unwrap() is safe here because "INPUT" is required (if "INPUT" wasn't + // required we could have used an 'if let' to conditionally get the value) + println!("Using input file: {}", matches.value_of("INPUT").unwrap()); + + // Gets a value for config if supplied by user, or defaults to "default.conf" + let config = matches.value_of("CONFIG").unwrap_or("default.conf"); + println!("Value for config: {}", config); + + // Vary the output based on how many times the user used the "debug" flag + // (i.e. 'myapp -d -d -d' or 'myapp -ddd' vs 'myapp -d' + match matches.occurrences_of("debug") { + 0 => println!("Debug mode is off"), + 1 => println!("Debug mode is kind of on"), + 2 => println!("Debug mode is on"), + 3 | _ => println!("Don't be crazy"), + } + + // You can information about subcommands by requesting their matches by name + // (as below), requesting just the name used, or both at the same time + if let Some(matches) = matches.subcommand_matches("test") { + if matches.is_present("verbose") { + println!("Printing verbosely..."); + } else { + println!("Printing normally..."); + } + } + + // more program logic goes here... +} diff --git a/clap/examples/02_apps.rs b/clap/examples/02_apps.rs new file mode 100644 index 0000000..8e45640 --- /dev/null +++ b/clap/examples/02_apps.rs @@ -0,0 +1,30 @@ +extern crate clap; + +use clap::App; + +fn main() { + // Apps describe the top level application + // + // You create an App and set various options on that App using the "builder pattern" + // + // The options (version(), author(), about()) aren't mandatory, but recommended. There is + // another option, usage(), which is an exception to the rule. This should only be used when + // the default usage string automatically generated by clap doesn't suffice. + // + // You also set all the valid arguments your App should accept via the arg(), args(), arg_from_usage() + // and args_from_usage() (as well as subcommands via the subcommand() and subcommands() methods) which + // will be covered later. + // + // Once all options have been set, call one of the .get_matches* family of methods in order to + // start the parsing and find all valid command line arguments that supplied by the user at + // runtime. The name given to new() will be displayed when the version or help flags are used. + App::new("MyApp") + .version("1.0") + .author("Kevin K. ") + .about("Does awesome things") + .get_matches(); + + // This example doesn't do much, but it *does* give automatic -h, --help, -V, and --version functionality ;) + + // Continued program logic goes here... +} diff --git a/clap/examples/03_args.rs b/clap/examples/03_args.rs new file mode 100644 index 0000000..c62d576 --- /dev/null +++ b/clap/examples/03_args.rs @@ -0,0 +1,84 @@ +extern crate clap; + +use clap::{App, Arg}; + +fn main() { + // Args describe a possible valid argument which may be supplied by the user at runtime. There + // are three different types of arguments (flags, options, and positional) as well as a fourth + // special type of argument, called SubCommands (which will be discussed separately). + // + // Args are described in the same manner as Apps using the "builder pattern" with multiple + // methods describing various settings for the individual arguments. Or by supplying a "usage" + // string. Both methods have their pros and cons. + // + // Arguments can be added to applications in two manners, one at a time with the arg(), and + // arg_from_usage() method, or multiple arguments at once via a Vec inside the args() method, + // or a single &str describing multiple Args (one per line) supplied to args_from_usage(). + // + // There are various options which can be set for a given argument, some apply to any of the + // three types of arguments, some only apply one or two of the types. *NOTE* if you set + // incompatible options on a single argument, clap will panic! at runtime. This is by design, + // so that you know right away an error was made by the developer, not the end user. + // + // # Help and Version + // clap automatically generates a help and version flag for you, unless you specify your + // own. By default help uses "-h" and "--help", and version uses "-V" and "--version". You can + // safely override "-V" and "-h" to your own arguments, and "--help" and "--version" will still + // be automatically generated for you. + let matches = App::new("MyApp") + // All application settings go here... + + // A simple "Flag" argument example (i.e. "-d") using the builder pattern + .arg(Arg::with_name("debug") + .help("turn on debugging information") + .short("d")) + + // Two arguments, one "Option" argument (i.e. one that takes a value) such + // as "-c some", and one positional argument (i.e. "myapp some_file") + .args(&[ + Arg::with_name("config") + .help("sets the config file to use") + .takes_value(true) + .short("c") + .long("config"), + Arg::with_name("input") + .help("the input file to use") + .index(1) + .required(true) + ]) + + // *Note* the following two examples are convenience methods, if you wish + // to still get the full configurability of Arg::with_name() and the readability + // of arg_from_usage(), you can instantiate a new Arg with Arg::from_usage() and + // still be able to set all the additional properties, just like Arg::with_name() + // + // + // One "Flag" using a usage string + .arg_from_usage("--license 'display the license file'") + + // Two args, one "Positional", and one "Option" using a usage string + .args_from_usage("[output] 'Supply an output file to use' + -i, --int=[IFACE] 'Set an interface to use'") + .get_matches(); + + // Here are some examples of using the arguments defined above. Keep in mind that this is only + // an example, and may be somewhat contrived + // + // First we check if debugging should be on or not + println!("Debugging mode is: {}", if matches.is_present("debug") { "ON" } else { "OFF" }); + + // Next we print the config file we're using, if any was defined with either -c or + // --config + if let Some(config) = matches.value_of("config") { + println!("A config file was passed in: {}", config); + } + + // Let's print the file the user passed in. We can use .unwrap() here becase the arg is + // required, and parsing would have failed if the user forgot it + println!("Using input file: {}", matches.value_of("input").unwrap()); + + // We could continue checking for and using arguments in this manner, such as "license", + // "output", and "interface". Keep in mind that "output" and "interface" are optional, so you + // shouldn't call .unwrap(). Instead, prefer using an 'if let' expression as we did with + // "config" +} diff --git a/clap/examples/04_using_matches.rs b/clap/examples/04_using_matches.rs new file mode 100644 index 0000000..a0a986f --- /dev/null +++ b/clap/examples/04_using_matches.rs @@ -0,0 +1,54 @@ +extern crate clap; + +use clap::{App, Arg}; + +fn main() { + + // Once all App settings (including all arguments) have been set, you call get_matches() which + // parses the string provided by the user, and returns all the valid matches to the ones you + // specified. + // + // You can then query the matches struct to get information about how the user ran the program + // at startup. + // + // For this example, let's assume you created an App which accepts three arguments (plus two + // generated by clap), a flag to display debugging information triggered with "-d" or + // "--debug" as well as an option argument which specifies a custom configuration file to use + // triggered with "-c file" or "--config file" or "--config=file" and finally a positional + // argument which is the input file we want to work with, this will be the only required + // argument. + let matches = App::new("MyApp") + .about("Parses an input file to do awesome things") + .version("1.0") + .author("Kevin K. ") + .arg(Arg::with_name("debug") + .help("turn on debugging information") + .short("d") + .long("debug")) + .arg(Arg::with_name("config") + .help("sets the config file to use") + .short("c") + .long("config")) + .arg(Arg::with_name("input") + .help("the input file to use") + .index(1) + .required(true)) + .get_matches(); + + // We can find out whether or not debugging was turned on + if matches.is_present("debug") { + println!("Debugging is turned on"); + } + + // If we wanted to do some custom initialization based off some configuration file + // provided by the user, we could get the file (A string of the file) + if let Some(file) = matches.value_of("config") { + println!("Using config file: {}", file); + } + + // Because "input" is required we can safely call unwrap() because had the user NOT + // specified a value, clap would have explained the error the user, and exited. + println!("Doing real work with file: {}", matches.value_of("input").unwrap() ); + + // Continued program logic goes here... +} diff --git a/clap/examples/05_flag_args.rs b/clap/examples/05_flag_args.rs new file mode 100644 index 0000000..a6b8945 --- /dev/null +++ b/clap/examples/05_flag_args.rs @@ -0,0 +1,56 @@ +extern crate clap; + +use clap::{App, Arg}; + +fn main() { + + // Of the three argument types, flags are the most simple. Flags are simple switches which can + // be either "on" or "off" + // + // clap also supports multiple occurrences of flags, the common example is "verbosity" where a + // user could want a little information with "-v" or tons of information with "-v -v" or "-vv" + let matches = App::new("MyApp") + // Regular App configuration goes here... + + // We'll add a flag that represents an awesome meter... + // + // I'll explain each possible setting that "flags" accept. Keep in mind + // that you DO NOT need to set each of these for every flag, only the ones + // you want for your individual case. + .arg(Arg::with_name("awesome") + .help("turns up the awesome") // Displayed when showing help info + .short("a") // Trigger this arg with "-a" + .long("awesome") // Trigger this arg with "--awesome" + .multiple(true) // This flag should allow multiple + // occurrences such as "-aaa" or "-a -a" + .requires("config") // Says, "If the user uses -a, they MUST + // also use this other 'config' arg too" + // Can also specify a list using + // requires_all(Vec<&str>) + .conflicts_with("output") // Opposite of requires(), says "if the + // user uses -a, they CANNOT use 'output'" + // also has a conflicts_with_all(Vec<&str>) + ) + // NOTE: In order to compile this example, comment out requires() and + // conflicts_with() because we have not defined an "output" or "config" + // argument. + .get_matches(); + + // We can find out whether or not awesome was used + if matches.is_present("awesome") { + println!("Awesomeness is turned on"); + } + + // If we set the multiple() option of a flag we can check how many times the user specified + // + // Note: if we did not specify the multiple() option, and the user used "awesome" we would get + // a 1 (no matter how many times they actually used it), or a 0 if they didn't use it at all + match matches.occurrences_of("awesome") { + 0 => println!("Nothing is awesome"), + 1 => println!("Some things are awesome"), + 2 => println!("Lots of things are awesome"), + 3 | _ => println!("EVERYTHING is awesome!"), + } + + // Continued program logic goes here... +} diff --git a/clap/examples/06_positional_args.rs b/clap/examples/06_positional_args.rs new file mode 100644 index 0000000..1f29612 --- /dev/null +++ b/clap/examples/06_positional_args.rs @@ -0,0 +1,56 @@ +extern crate clap; + +use clap::{App, Arg}; + +fn main() { + + // Positional arguments are those values after the program name which are not preceded by any + // identifier (such as "myapp some_file"). Positionals support many of the same options as + // flags, as well as a few additional ones. + let matches = App::new("MyApp") + // Regular App configuration goes here... + + // We'll add two positional arguments, a input file, and a config file. + // + // I'll explain each possible setting that "positionals" accept. Keep in + // mind that you DO NOT need to set each of these for every flag, only the + // ones that apply to your individual case. + .arg(Arg::with_name("input") + .help("the input file to use") // Displayed when showing help info + .index(1) // Set the order in which the user must + // specify this argument (Starts at 1) + .requires("config") // Says, "If the user uses "input", they MUST + // also use this other 'config' arg too" + // Can also specify a list using + // requires_all(Vec<&str>) + .conflicts_with("output") // Opposite of requires(), says "if the + // user uses -a, they CANNOT use 'output'" + // also has a conflicts_with_all(Vec<&str>) + .required(true) // By default this argument MUST be present + // NOTE: mutual exclusions take precedence over + // required arguments + ) + .arg(Arg::with_name("config") + .help("the config file to use") + .index(2)) // Note, we do not need to specify required(true) + // if we don't want to, because "input" already + // requires "config" + // Note, we also do not need to specify requires("input") + // because requires lists are automatically two-way + + // NOTE: In order to compile this example, comment out conflicts_with() + // because we have not defined an "output" argument. + .get_matches(); + + // We can find out whether or not "input" or "config" were used + if matches.is_present("input") { + println!("An input file was specified"); + } + + // We can also get the values for those arguments + if let Some(in_file) = matches.value_of("input") { + // It's safe to call unwrap() because of the required options we set above + println!("Doing work with {} and {}", in_file, matches.value_of("config").unwrap()); + } + // Continued program logic goes here... +} diff --git a/clap/examples/07_option_args.rs b/clap/examples/07_option_args.rs new file mode 100644 index 0000000..85ff0e5 --- /dev/null +++ b/clap/examples/07_option_args.rs @@ -0,0 +1,71 @@ +extern crate clap; + +use clap::{App, Arg}; + +fn main() { + + // Option arguments are those that take an additional value, such as "-c value". In clap they + // support three types of specification, those with short() as "-o some", or those with long() + // as "--option value" or "--option=value" + // + // Options also support a multiple setting, which is discussed in the example below. + let matches = App::new("MyApp") + // Regular App configuration goes here... + + // Assume we have an application that accepts an input file via the "-i file" + // or the "--input file" (as well as "--input=file"). + // Below every setting supported by option arguments is discussed. + // NOTE: You DO NOT need to specify each setting, only those which apply + // to your particular case. + .arg(Arg::with_name("input") + .help("the input file to use") // Displayed when showing help info + .takes_value(true) // MUST be set to true in order to be an "option" argument + .short("i") // This argument is triggered with "-i" + .long("input") // This argument is triggered with "--input" + .multiple(true) // Set to true if you wish to allow multiple occurrences + // such as "-i file -i other_file -i third_file" + .required(true) // By default this argument MUST be present + // NOTE: mutual exclusions take precedence over + // required arguments + .requires("config") // Says, "If the user uses "input", they MUST + // also use this other 'config' arg too" + // Can also specify a list using + // requires_all(Vec<&str>) + .conflicts_with("output") // Opposite of requires(), says "if the + // user uses -a, they CANNOT use 'output'" + // also has a conflicts_with_all(Vec<&str>) + ) + // NOTE: In order to compile this example, comment out conflicts_with() + // and requires() because we have not defined an "output" or "config" + // argument. + .get_matches(); + + // We can find out whether or not "input" was used + if matches.is_present("input") { + println!("An input file was specified"); + } + + // We can also get the value for "input" + // + // NOTE: If we specified multiple(), this will only return the _FIRST_ + // occurrence + if let Some(in_file) = matches.value_of("input") { + println!("An input file: {}", in_file); + } + + // If we specified the multiple() setting we can get all the values + if let Some(in_v) = matches.values_of("input") { + for in_file in in_v { + println!("An input file: {}", in_file); + } + } + + // We can see how many times the option was used with the occurrences_of() method + // + // NOTE: Just like with flags, if we did not specify the multiple() setting this will only + // return 1 no matter how many times the argument was used (unless it wasn't used at all, in + // in which case 0 is returned) + println!("The \"input\" argument was used {} times", matches.occurrences_of("input")); + + // Continued program logic goes here... +} diff --git a/clap/examples/08_subcommands.rs b/clap/examples/08_subcommands.rs new file mode 100644 index 0000000..73bd098 --- /dev/null +++ b/clap/examples/08_subcommands.rs @@ -0,0 +1,57 @@ +extern crate clap; + +use clap::{App, Arg, SubCommand}; + +fn main() { + + // SubCommands function exactly like sub-Apps, because that's exactly what they are. Each + // instance of a SubCommand can have it's own version, author(s), Args, and even it's own + // subcommands. + // + // # Help and Version + // Just like Apps, each subcommand will get it's own "help" and "version" flags automatically + // generated. Also, like Apps, you can override "-V" or "-h" safely and still get "--help" and + // "--version" auto generated. + // + // NOTE: If you specify a subcommand for your App, clap will also autogenerate a "help" + // subcommand along with "-h" and "--help" (applies to sub-subcommands as well). + // + // Just like arg() and args(), subcommands can be specified one at a time via subcommand() or + // multiple ones at once with a Vec provided to subcommands(). + let matches = App::new("MyApp") + // Normal App and Arg configuration goes here... + + // In the following example assume we wanted an application which + // supported an "add" subcommand, this "add" subcommand also took + // one positional argument of a file to add: + .subcommand(SubCommand::with_name("add") // The name we call argument with + .about("Adds files to myapp") // The message displayed in "myapp -h" + // or "myapp help" + .version("0.1") // Subcommands can have independent version + .author("Kevin K.") // And authors + .arg(Arg::with_name("input") // And their own arguments + .help("the file to add") + .index(1) + .required(true))) + .get_matches(); + + // You can check if a subcommand was used like normal + if matches.is_present("add") { + println!("'myapp add' was run."); + } + + // You can get the independent subcommand matches (which function exactly like App matches) + if let Some(matches) = matches.subcommand_matches("add") { + // Safe to use unwrap() because of the required() option + println!("Adding file: {}", matches.value_of("input").unwrap()); + } + + // You can also match on a subcommand's name + match matches.subcommand_name() { + Some("add") => println!("'myapp add' was used"), + None => println!("No subcommand was used"), + _ => println!("Some other subcommand was used"), + } + + // Continued program logic goes here... +} diff --git a/clap/examples/09_auto_version.rs b/clap/examples/09_auto_version.rs new file mode 100644 index 0000000..dfd221f --- /dev/null +++ b/clap/examples/09_auto_version.rs @@ -0,0 +1,29 @@ +#[macro_use] +extern crate clap; + +use clap::App; + +fn main() { + // You can have clap pull the application version directly from your Cargo.toml starting with + // clap v0.4.14 on crates.io (or master#a81f915 on github). Using Rust's env! macro like this: + // + // let version = format!("{}.{}.{}{}", + // env!("CARGO_PKG_VERSION_MAJOR"), + // env!("CARGO_PKG_VERSION_MINOR"), + // env!("CARGO_PKG_VERSION_PATCH"), + // option_env!("CARGO_PKG_VERSION_PRE").unwrap_or("")); + // + // Starting from v0.6.6 on crates.io you can also use the crate_version!() macro instead of + // manually using the env!() macros. Under the hood, the macro uses this exact method to get + // the version. + // + // Thanks to https://github.com/jhelwig for pointing this out + App::new("myapp") + .about("does awesome things") + // use crate_version! to pull the version number + .version(crate_version!()) + .get_matches(); + + // running this app with the -V or --version will display whatever version is in your + // Cargo.toml, the default being: myapp 0.0.1 +} diff --git a/clap/examples/10_default_values.rs b/clap/examples/10_default_values.rs new file mode 100644 index 0000000..ca50981 --- /dev/null +++ b/clap/examples/10_default_values.rs @@ -0,0 +1,40 @@ +extern crate clap; + +use clap::{App, Arg}; + +fn main() { + // There are two ways in which to get a default value, one is to use claps Arg::default_value + // method, and the other is to use Rust's built in Option::unwrap_or method. + // + // I'll demo both here. + // + // First, we'll use clap's Arg::default_value with an "INPUT" file. + let matches = App::new("myapp").about("does awesome things") + .arg(Arg::with_name("INPUT") + .help("The input file to use") // Note, we don't need to specify + // anything like, "Defaults to..." + // because clap will automatically + // generate that for us, and place + // it in the help text + .default_value("input.txt") + .index(1)) + + // Next we'll use the Option::unwrap_or method on this "CONFIG" option + .arg(Arg::with_name("CONFIG") + // Note that we have to manually include some verbiage to the user + // telling them what the default will be. + .help("The config file to use (default is \"config.json\")") + .short("c") + .takes_value(true)) + .get_matches(); + + // It's safe to call unwrap because the value with either be what the user input at runtime + // or "input.txt" + let input = matches.value_of("INPUT").unwrap(); + + // Using Option::unwrap_or we get the same affect, but without the added help text injection + let config_file = matches.value_of("CONFIG").unwrap_or("config.json"); + + println!("The input file is: {}", input); + println!("The config file is: {}", config_file); +} diff --git a/clap/examples/11_only_specific_values.rs b/clap/examples/11_only_specific_values.rs new file mode 100644 index 0000000..3445218 --- /dev/null +++ b/clap/examples/11_only_specific_values.rs @@ -0,0 +1,33 @@ +extern crate clap; + +use clap::{App, Arg}; + +fn main() { + // If you have arguments of specific values you want to test for, you can use the + // .possible_values() method of Arg + // + // This allows you specify the valid values for that argument. If the user does not use one of + // those specific values, they will receive a graceful exit with error message informing them + // of the mistake, and what the possible valid values are + // + // For this example, assume you want one positional argument of either "fast" or "slow" + // i.e. the only possible ways to run the program are "myprog fast" or "myprog slow" + let matches = App::new("myapp").about("does awesome things") + .arg(Arg::with_name("MODE") + .help("What mode to run the program in") + .index(1) + .possible_values(&["fast", "slow"]) + .required(true)) + .get_matches(); + + // Note, it's safe to call unwrap() because the arg is required + match matches.value_of("MODE").unwrap() { + "fast" => { + // Do fast things... + }, + "slow" => { + // Do slow things... + }, + _ => unreachable!() + } +} diff --git a/clap/examples/12_typed_values.rs b/clap/examples/12_typed_values.rs new file mode 100644 index 0000000..3d03e4f --- /dev/null +++ b/clap/examples/12_typed_values.rs @@ -0,0 +1,50 @@ +#[macro_use] +extern crate clap; + +use clap::App; + +fn main() { + // You can use some convenience macros provided by clap to get typed values, so long as the + // type you specify implements std::str::FromStr + // + // This works for both single, and multiple values (multiple values returns a Vec) + // + // There are also two ways in which to get types, those where failures cause the program to exit + // with an error and usage string, and those which return a Result or Result,String> + // respectively. Both methods support single and multiple values. + // + // The macro which returns a Result allows you decide what to do upon a failure, exit, provide a + // default value, etc. You have control. But it also means you have to write the code or boiler plate + // to handle those instances. + // + // That is why the second method exists, so you can simply get a T or Vec back, or be sure the + // program will exit gracefully. The catch is, the second method should *only* be used on required + // arguments, because if the argument isn't found, it exits. Just FYI ;) + // + // The following example shows both methods. + // + // **NOTE:** to use the macros, you must include #[macro_use] just above the 'extern crate clap;' + // declaration in your crate root. + let matches = App::new("myapp") + // Create two arguments, a required positional which accepts multiple values + // and an optional '-l value' + .args_from_usage( + "... 'A sequence of whole positive numbers, i.e. 20 25 30' + -l [len] 'A length to use, defaults to 10 when omitted'") + .get_matches(); + + // Here we get a value of type u32 from our optional -l argument. + // If the value provided to len fails to parse, we default to 10 + // + // Using other methods such as unwrap_or_else(|e| println!("{}",e)) + // are possible too. + let len = value_t!(matches, "len", u32).unwrap_or(10); + + println!("len ({}) + 2 = {}", len, len + 2); + + // This code loops through all the values provided to "seq" and adds 2 + // If seq fails to parse, the program exits, you don't have an option + for v in values_t!(matches, "seq", u32).unwrap_or_else(|e| e.exit()) { + println!("Sequence part {} + 2: {}", v, v + 2); + } +} diff --git a/clap/examples/13a_enum_values_automatic.rs b/clap/examples/13a_enum_values_automatic.rs new file mode 100644 index 0000000..1abe5cb --- /dev/null +++ b/clap/examples/13a_enum_values_automatic.rs @@ -0,0 +1,68 @@ +// You can use clap's value_t! macro with a custom enum by implementing the std::str::FromStr +// trait which is very straight forward. There are three ways to do this, for simple enums +// meaning those that don't require 'pub' or any '#[derive()]' directives you can use clap's +// simple_enum! macro. For those that require 'pub' or any '#[derive()]'s you can use clap's +// arg_enum! macro. The third way is to implement std::str::FromStr manually. +// +// In most circumstances using either simple_enum! or arg_enum! is fine. +// +// In the following example we will create two enums using macros, assign a positional argument +// that accepts only one of those values, and use clap to parse the argument. + +// Add clap like normal +#[macro_use] +extern crate clap; + +use clap::{App, Arg}; + +// Using arg_enum! is more like traditional enum declarations +// +// **NOTE:** Only bare variants are supported +arg_enum!{ + #[derive(Debug)] + pub enum Oof { + Rab, + Zab, + Xuq + } +} + +arg_enum!{ + #[derive(Debug)] + enum Foo { + Bar, + Baz, + Qux + } +} + +fn main() { + // Create the application like normal + let enum_vals = ["fast", "slow"]; + let m = App::new("myapp") + // Use a single positional argument that is required + .arg(Arg::from_usage(" 'The Foo to use'") + .possible_values(&Foo::variants())) + .arg(Arg::from_usage(" 'The speed to use'") + // You can define a list of possible values if you want the values to be + // displayed in the help information. Whether you use possible_values() or + // not, the valid values will ALWAYS be displayed on a failed parse. + .possible_values(&enum_vals)) + // For the second positional, lets not use possible_values() just to show the difference + .arg_from_usage(" 'The Oof to use'") + .get_matches(); + + let t = value_t!(m.value_of("foo"), Foo).unwrap_or_else(|e| e.exit()); + let t2 = value_t!(m.value_of("oof"), Oof).unwrap_or_else(|e| e.exit()); + + + // Now we can use our enum like normal. + match t { + Foo::Bar => println!("Found a Bar"), + Foo::Baz => println!("Found a Baz"), + Foo::Qux => println!("Found a Qux") + } + + // Since our Oof derives Debug, we can do this: + println!("Oof: {:?}", t2); +} diff --git a/clap/examples/13b_enum_values_manual.rs b/clap/examples/13b_enum_values_manual.rs new file mode 100644 index 0000000..81ffe5e --- /dev/null +++ b/clap/examples/13b_enum_values_manual.rs @@ -0,0 +1,54 @@ +// In the following example we will create an enum with 4 values, assign a positional argument +// that accepts only one of those values, and use clap to parse the argument. +// +// Start with bringing the trait into scope. +use std::str::FromStr; + +// Add clap like normal +#[macro_use] +extern crate clap; + +use clap::{App, Arg}; + +// Define your enum +enum Vals { + Foo, + Bar, + Baz, + Qux +} + +// Implement the trait +impl FromStr for Vals { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "Foo" => Ok(Vals::Foo), + "Bar" => Ok(Vals::Bar), + "Baz" => Ok(Vals::Baz), + "Qux" => Ok(Vals::Qux), + _ => Err("no match") + } + } +} + +fn main() { + // Create the application like normal + let m = App::new("myapp") + // Use a single positional argument that is required + .arg(Arg::from_usage(" 'The type to use'") + // Define the list of possible values + .possible_values(&["Foo", "Bar", "Baz", "Qux"])) + .get_matches(); + + let t = value_t!(m, "type", Vals).unwrap_or_else(|e| e.exit()); + + // Now we can use our enum like normal. + match t { + Vals::Foo => println!("Found a Foo"), + Vals::Bar => println!("Found a Bar"), + Vals::Baz => println!("Found a Baz"), + Vals::Qux => println!("Found a Qux") + } +} diff --git a/clap/examples/14_groups.rs b/clap/examples/14_groups.rs new file mode 100644 index 0000000..e160464 --- /dev/null +++ b/clap/examples/14_groups.rs @@ -0,0 +1,87 @@ +/// `ArgGroup`s are a family of related arguments and way for you to say, "Any of these arguments". +/// By placing arguments in a logical group, you can make easier requirement and exclusion rules +/// instead of having to list each individually, or when you want a rule to apply "any but not all" +/// arguments. +/// +/// For instance, you can make an entire `ArgGroup` required, this means that one (and *only* one) +/// argument from that group must be present. Using more than one argument from an `ArgGroup` causes +/// a failure (graceful exit). +/// +/// You can also do things such as name an `ArgGroup` as a confliction or requirement, meaning any +/// of the arguments that belong to that group will cause a failure if present, or must present +/// respectively. +/// +/// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be +/// present out of a given set. Imagine that you had multiple arguments, and you want one of them to +/// be required, but making all of them required isn't feasible because perhaps they conflict with +/// each other. For example, lets say that you were building an application where one could set a +/// given version number by supplying a string with an option argument, i.e. `--set-ver v1.2.3`, you +/// also wanted to support automatically using a previous version number and simply incrementing one +/// of the three numbers. So you create three flags `--major`, `--minor`, and `--patch`. All of +/// these arguments shouldn't be used at one time but you want to specify that *at least one* of +/// them is used. For this, you can create a group. + +extern crate clap; + +use clap::{App, Arg, ArgGroup}; + +fn main() { + // Create application like normal + let matches = App::new("myapp") + // Add the version arguments + .args_from_usage("--set-ver [ver] 'set version manually' + --major 'auto inc major' + --minor 'auto inc minor' + --patch 'auto inc patch'") + // Create a group, make it required, and add the above arguments + .group(ArgGroup::with_name("vers") + .required(true) + .args(&["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::from_usage("[INPUT_FILE] 'some regular input'") + .group("input")) + .arg(Arg::from_usage("--spec-in [SPEC_IN] 'some special input argument'") + .group("input")) + // Now let's assume we have a -c [config] argument which requires one of + // (but **not** both) the "input" arguments + .arg(Arg::with_name("config") + .short("c") + .takes_value(true) + .requires("input")) + .get_matches(); + + // Let's assume the old version 1.2.3 + let mut major = 1; + let mut minor = 2; + let mut patch = 3; + + // See if --set-ver was used to set the version manually + let version = if let Some(ver) = matches.value_of("ver") { + format!("{}", ver) + } else { + // Increment the one requested (in a real program, we'd reset the lower numbers) + let (maj, min, pat) = (matches.is_present("major"), + matches.is_present("minor"), + matches.is_present("patch")); + match (maj, min, pat) { + (true, _, _) => major += 1, + (_, true, _) => minor += 1, + (_, _, true) => patch += 1, + _ => unreachable!(), + }; + format!("{}.{}.{}", major, minor, patch) + }; + + println!("Version: {}", version); + + // Check for usage of -c + if matches.is_present("config") { + let input = matches.value_of("INPUT_FILE").unwrap_or(matches.value_of("SPEC_IN").unwrap()); + println!("Doing work using input {} and config {}", + input, + matches.value_of("config").unwrap()); + } + + +} diff --git a/clap/examples/15_custom_validator.rs b/clap/examples/15_custom_validator.rs new file mode 100644 index 0000000..a5c0d42 --- /dev/null +++ b/clap/examples/15_custom_validator.rs @@ -0,0 +1,37 @@ +extern crate clap; + +use clap::{App, Arg}; + +fn main() { + // You can define a function (or a closure) to use as a validator to argument values. The + // function must accept a String and return Result<(), String> where Err(String) is the message + // displayed to the user. + + let matches = App::new("myapp") + // Application logic goes here... + .arg(Arg::with_name("input") + .help("the input file to use") + .index(1) + .required(true) + // You can pass in a closure, or a function + .validator(is_png)) + .get_matches(); + + // Here we can call .unwrap() because the argument is required. + println!("The .PNG file is: {}", matches.value_of("input").unwrap()); +} + +fn is_png(val: String) -> Result<(), String> { + // val is the argument value passed in by the user + // val has type of String. + if val.ends_with(".png") { + Ok(()) + } else { + // clap automatically adds "error: " to the beginning + // of the message. + Err(String::from("the file format must be png.")) + } + // Of course, you can do more complicated validation as + // well, but for the simplicity, this example only checks + // if the value passed in ends with ".png" or not. +} diff --git a/clap/examples/16_app_settings.rs b/clap/examples/16_app_settings.rs new file mode 100644 index 0000000..ab1d185 --- /dev/null +++ b/clap/examples/16_app_settings.rs @@ -0,0 +1,41 @@ +extern crate clap; + +use clap::{App, AppSettings, SubCommand}; + +fn main() { + // You can use AppSettings to change the application level behavior of clap. .setting() function + // of App struct takes AppSettings enum as argument. There is also .settings() function which + // takes slice of AppSettings enum. You can learn more about AppSettings in the documentation, + // which also has examples on each setting. + // + // This example will only show usage of one AppSettings setting. See documentation for more + // information. + + let matches = App::new("myapp") + .setting(AppSettings::SubcommandsNegateReqs) + // Negates requirement of parent command. + + .arg_from_usage(" 'input file to use'") + // Required positional argument called input. This + // will be only required if subcommand is not present. + + .subcommand(SubCommand::with_name("test") + .about("does some testing")) + // if program is invoked with subcommand, you do not + // need to specify the argument anymore due to + // the AppSettings::SubcommandsNegateReqs setting. + + .get_matches(); + + // Calling unwrap() on "input" would not be advised here, because although it's required, + // if the user uses a subcommand, those requirements are no longer required. Hence, we should + // use some sort of 'if let' construct + if let Some(inp) = matches.value_of("input") { + println!("The input file is: {}", inp); + } + + match matches.subcommand() { + ("test", _) => println!("The 'test' subcommand was used"), + _ => unreachable!() + } +} diff --git a/clap/examples/17_yaml.rs b/clap/examples/17_yaml.rs new file mode 100644 index 0000000..3353d73 --- /dev/null +++ b/clap/examples/17_yaml.rs @@ -0,0 +1,53 @@ +// In order to use YAML to define your CLI you must compile clap with the "yaml" feature because +// it's **not** included by default. +// +// In order to do this, ensure your Cargo.toml looks like one of the following: +// +// [dependencies.clap] +// features = ["yaml"] +// +// __OR__ +// +// [dependencies] +// clap = { features = ["yaml"] } + + +// Using yaml requires calling a clap macro `load_yaml!()` so we must use the '#[macro_use]' +// directive +#[macro_use] +extern crate clap; + +#[cfg(feature = "yaml")] +fn main() { + use clap::App; + + // To load a yaml file containing our CLI definition such as the example '17_yaml.yml' we can + // use the convenience macro which loads the file at compile relative to the current file + // similar to how modules are found. + // + // Then we pass that yaml object to App to build the CLI. + // + // Finally we call get_matches() to start the parsing process. We use the matches just as we + // normally would + let yml = load_yaml!("17_yaml.yml"); + let m = App::from_yaml(yml).get_matches(); + + // Because the example 17_yaml.yml is rather large we'll just look a single arg so you can + // see that it works... + if let Some(mode) = m.value_of("mode") { + match mode { + "vi" => println!("You are using vi"), + "emacs" => println!("You are using emacs..."), + _ => unreachable!() + } + } else { + println!("--mode wasn't used..."); + } +} + +#[cfg(not(feature = "yaml"))] +fn main() { + // As stated above, if clap is not compiled with the YAML feature, it is disabled. + println!("YAML feature is disabled."); + println!("Pass --features yaml to cargo when trying this example."); +} diff --git a/clap/examples/17_yaml.yml b/clap/examples/17_yaml.yml new file mode 100644 index 0000000..b0d58b3 --- /dev/null +++ b/clap/examples/17_yaml.yml @@ -0,0 +1,97 @@ +name: yml_app +version: "1.0" +about: an example using a .yml file to build a CLI +author: Kevin K. + +# AppSettings can be defined as a list and are **not** ascii case sensitive +settings: + - ArgRequiredElseHelp + +# All Args must be defined in the 'args:' list where the name of the arg, is the +# key to a Hash object +args: + # The name of this argument, is 'opt' which will be used to access the value + # later in your Rust code + - opt: + help: example option argument from yaml + short: o + long: option + multiple: true + takes_value: true + - pos: + help: example positional argument from yaml + index: 1 + # A list of possible values can be defined as a list + possible_values: + - fast + - slow + - flag: + help: demo flag argument + short: F + multiple: true + global: true + # Conflicts, mutual overrides, and requirements can all be defined as a + # list, where the key is the name of the other argument + conflicts_with: + - opt + requires: + - pos + - mode: + long: mode + help: shows an option with specific values + # possible_values can also be defined in this list format + possible_values: [ vi, emacs ] + takes_value: true + - mvals: + long: mult-vals + help: demos an option which has two named values + # value names can be described in a list, where the help will be shown + # --mult-vals + value_names: + - one + - two + - minvals: + long: min-vals + multiple: true + help: you must supply at least two values to satisfy me + min_values: 2 + - maxvals: + long: max-vals + multiple: true + help: you can only supply a max of 3 values for me! + max_values: 3 + +# All subcommands must be listed in the 'subcommand:' object, where the key to +# the list is the name of the subcommand, and all settings for that command are +# are part of a Hash object +subcommands: + # The name of this subcommand will be 'subcmd' which can be accessed in your + # Rust code later + - subcmd: + about: demos subcommands from yaml + version: "0.1" + author: Kevin K. + # Subcommand args are exactly like App args + args: + - scopt: + short: B + multiple: true + help: example subcommand option + takes_value: true + - scpos1: + help: example subcommand positional + index: 1 + +# ArgGroups are supported as well, and must be sepcified in the 'groups:' +# object of this file +groups: + # the name of the ArgGoup is specified here + - min-max-vals: + # All args and groups that are a part of this group are set here + args: + - minvals + - maxvals + # setting conflicts is done the same manner as setting 'args:' + # + # to make this group required, you could set 'required: true' but for + # this example we won't do that. diff --git a/clap/examples/18_builder_macro.rs b/clap/examples/18_builder_macro.rs new file mode 100644 index 0000000..6bdce47 --- /dev/null +++ b/clap/examples/18_builder_macro.rs @@ -0,0 +1,84 @@ +#[macro_use] +extern crate clap; + +// Note, there isn't a need for "use clap::{ ... };" Because the clap_app! macro uses +// $crate:: internally + +fn main() { + + // Validation example testing that a file exists + let file_exists = |path| { + if std::fs::metadata(path).is_ok() { + Ok(()) + } else { + Err(String::from("File doesn't exist")) + } + }; + + // External module may contain this subcommand. If this exists in another module, a function is + // required to access it. Recommend `fn clap() -> Clap::SubCommand`. + let external_sub_command = clap_app!( @subcommand foo => + (@arg bar: -b "Bar") + ); + + let matches = clap_app!(MyApp => + (@setting SubcommandRequiredElseHelp) + (version: "1.0") + (author: "Alice") + (about: "Does awesome things") + (@arg config: -c --config #{1, 2} {file_exists} "Sets a custom config file") + (@arg proxyHostname: --("proxy-hostname") +takes_value "Sets the hostname of the proxy to use") + (@arg input: * "Input file") + (@group test => + (@attributes +required) + (@arg output: "Sets an optional output file") + (@arg debug: -d ... "Turn debugging information on") + ) + (subcommand: external_sub_command) + (@subcommand test => + (about: "does testing things") + (version: "2.5") + (@arg list: -l "Lists test values") + (@arg test_req: -r requires[list] "Tests requirement for listing") + (@arg aaaa: --aaaa +takes_value { + |a| if a.contains('a') { + Ok(()) + } else { + Err(String::from("string does not contain at least one a")) + } + } "Test if the argument contains an a") + ) + ).get_matches(); + + // You can check the value provided by positional arguments, or option arguments + if let Some(o) = matches.value_of("output") { + println!("Value for output: {}", o); + } + + if let Some(c) = matches.value_of("config") { + println!("Value for config: {}", c); + } + + // You can see how many times a particular flag or argument occurred + // Note, only flags can have multiple occurrences + match matches.occurrences_of("debug") { + 0 => println!("Debug mode is off"), + 1 => println!("Debug mode is kind of on"), + 2 => println!("Debug mode is on"), + 3 | _ => println!("Don't be crazy"), + } + + // You can check for the existence of subcommands, and if found use their + // matches just as you would the top level app + if let Some(matches) = matches.subcommand_matches("test") { + // "$ myapp test" was run + if matches.is_present("list") { + // "$ myapp test -l" was run + println!("Printing testing lists..."); + } else { + println!("Not printing testing lists..."); + } + } + + // Continued program logic goes here... +} diff --git a/clap/examples/19_auto_authors.rs b/clap/examples/19_auto_authors.rs new file mode 100644 index 0000000..afbb985 --- /dev/null +++ b/clap/examples/19_auto_authors.rs @@ -0,0 +1,15 @@ +#[macro_use] +extern crate clap; + +use clap::App; + +fn main() { + App::new("myapp") + .about("does awesome things") + // use crate_authors! to pull the author(s) names from the Cargo.toml + .author(crate_authors!()) + .get_matches(); + + // running this app with -h will display whatever author(s) are in your + // Cargo.toml +} diff --git a/clap/examples/20_subcommands.rs b/clap/examples/20_subcommands.rs new file mode 100644 index 0000000..f80f46d --- /dev/null +++ b/clap/examples/20_subcommands.rs @@ -0,0 +1,143 @@ +// Working with subcommands is simple. There are a few key points to remember when working with +// subcommands in clap. First, SubCommands are really just Apps. This means they can have their own +// settings, version, authors, args, and even their own subcommands. The next thing to remember is +// that subcommands are set up in a tree like hierarchy. +// +// An ASCII art depiction may help explain this better. Using a fictional version of git as the demo +// subject. Imagine the following are all subcommands of git (note, the author is aware these aren't +// actually all subcommands in the real git interface, but it makes explanation easier) +// +// Top Level App (git) TOP +// | +// ----------------------------------------- +// / | \ \ +// clone push add commit LEVEL 1 +// | / \ / \ | +// url origin remote ref name message LEVEL 2 +// / /\ +// path remote local LEVEL 3 +// +// Given the above fictional subcommand hierarchy, valid runtime uses would be (not an all inclusive +// list): +// +// $ git clone url +// $ git push origin path +// $ git add ref local +// $ git commit message +// +// Notice only one command per "level" may be used. You could not, for example, do: +// +// $ git clone url push origin path +// +// It's also important to know that subcommands each have their own set of matches and may have args +// with the same name as other subcommands in a different part of the tree hierarchy (i.e. the arg +// names aren't in a flat namespace). +// +// In order to use subcommands in clap, you only need to know which subcommand you're at in your +// tree, and which args are defined on that subcommand. +// +// Let's make a quick program to illustrate. We'll be using the same example as above but for +// brevity sake we won't implement all of the subcommands, only a few. + +extern crate clap; + +use clap::{App, Arg, SubCommand, AppSettings}; + +fn main() { + + let matches = App::new("git") + .about("A fictional versioning CLI") + .version("1.0") + .author("Me") + .subcommand(SubCommand::with_name("clone") + .about("clones repos") + .arg(Arg::with_name("repo") + .help("The repo to clone") + .required(true))) + .subcommand(SubCommand::with_name("push") + .about("pushes things") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand(SubCommand::with_name("remote") // Subcommands can have their own subcommands, + // which in turn have their own subcommands + .about("pushes remote things") + .arg(Arg::with_name("repo") + .required(true) + .help("The remote repo to push things to"))) + .subcommand(SubCommand::with_name("local") + .about("pushes local things"))) + .subcommand(SubCommand::with_name("add") + .about("adds things") + .author("Someone Else") // Subcommands can list different authors + .version("v2.0 (I'm versioned differently") // or different version from their parents + .setting(AppSettings::ArgRequiredElseHelp) // They can even have different settings + .arg(Arg::with_name("stuff") + .long("stuff") + .help("Stuff to add") + .takes_value(true) + .multiple(true))) + .get_matches(); + + // At this point, the matches we have point to git. Keep this in mind... + + // You can check if one of git's subcommands was used + if matches.is_present("clone") { + println!("'git clone' was run."); + } + + // You can see which subcommand was used + if let Some(subcommand) = matches.subcommand_name() { + println!("'git {}' was used", subcommand); + + // It's important to note, this *only* check's git's DIRECT children, **NOT** it's + // grandchildren, great grandchildren, etc. + // + // i.e. if the command `git push remove --stuff foo` was run, the above will only print out, + // `git push` was used. We'd need to get push's matches to see further into the tree + } + + // An alternative to checking the name is matching on known names. Again notice that only the + // direct children are matched here. + match matches.subcommand_name() { + Some("clone") => println!("'git clone' was used"), + Some("push") => println!("'git push' was used"), + Some("add") => println!("'git add' was used"), + None => println!("No subcommand was used"), + _ => unreachable!(), // Assuming you've listed all direct children above, this is unreachable + } + + // You could get the independent subcommand matches, although this is less common + if let Some(clone_matches) = matches.subcommand_matches("clone") { + // Now we have a reference to clone's matches + println!("Cloning repo: {}", clone_matches.value_of("repo").unwrap()); + } + + // The most common way to handle subcommands is via a combined approach using + // `ArgMatches::subcommand` which returns a tuple of both the name and matches + match matches.subcommand() { + ("clone", Some(clone_matches)) =>{ + // Now we have a reference to clone's matches + println!("Cloning {}", clone_matches.value_of("repo").unwrap()); + }, + ("push", Some(push_matches)) =>{ + // Now we have a reference to push's matches + match push_matches.subcommand() { + ("remote", Some(remote_matches)) =>{ + // Now we have a reference to remote's matches + println!("Pushing to {}", remote_matches.value_of("repo").unwrap()); + }, + ("local", Some(_)) =>{ + println!("'git push local' was used"); + }, + _ => unreachable!(), + } + }, + ("add", Some(add_matches)) =>{ + // Now we have a reference to add's matches + println!("Adding {}", add_matches.values_of("stuff").unwrap().collect::>().join(", ")); + }, + ("", None) => println!("No subcommand was used"), // If no subcommand was used it'll match the tuple ("", None) + _ => unreachable!(), // If all subcommands are defined above, anything else is unreachable!() + } + + // Continued program logic goes here... +} diff --git a/clap/examples/21_aliases.rs b/clap/examples/21_aliases.rs new file mode 100644 index 0000000..3be0445 --- /dev/null +++ b/clap/examples/21_aliases.rs @@ -0,0 +1,39 @@ +extern crate clap; + +use clap::{App, Arg, SubCommand}; + +fn main() { + + let matches = App::new("MyApp") + .subcommand(SubCommand::with_name("ls") + .aliases(&["list", "dir"]) + .about("Adds files to myapp") + .version("0.1") + .author("Kevin K.") + .arg(Arg::with_name("input") + .help("the file to add") + .index(1) + .required(true)) + ) + .get_matches(); + + // You can check if a subcommand was used like normal + if matches.is_present("add") { + println!("'myapp add' was run."); + } + + // You can get the independent subcommand matches (which function exactly like App matches) + if let Some(matches) = matches.subcommand_matches("add") { + // Safe to use unwrap() because of the required() option + println!("Adding file: {}", matches.value_of("input").unwrap()); + } + + // You can also match on a subcommand's name + match matches.subcommand_name() { + Some("add") => println!("'myapp add' was used"), + None => println!("No subcommand was used"), + _ => println!("Some other subcommand was used"), + } + + // Continued program logic goes here... +} diff --git a/clap/examples/22_stop_parsing_with_--.rs b/clap/examples/22_stop_parsing_with_--.rs new file mode 100644 index 0000000..a5ba5b3 --- /dev/null +++ b/clap/examples/22_stop_parsing_with_--.rs @@ -0,0 +1,25 @@ +extern crate clap; + +use clap::{App, Arg}; + +/// myprog -f -p=bob -- sloppy slop slop +fn main() { + + let matches = App::new("myprog") + .arg(Arg::with_name("eff") + .short("f")) + .arg(Arg::with_name("pea") + .short("p") + .takes_value(true)) + .arg(Arg::with_name("slop") + .multiple(true) + .last(true)) + .get_matches(); + + + println!("-f used: {:?}", matches.is_present("eff")); + println!("-p's value: {:?}", matches.value_of("pea")); + println!("'slops' values: {:?}", matches.values_of("slop").map(|vals| vals.collect::>())); + + // Continued program logic goes here... +} -- cgit v1.2.3