├── clap.png ├── index.html ├── clap-tests ├── .gitignore ├── Cargo.toml ├── Makefile ├── src │ └── main.rs └── run_tests.py ├── src ├── args │ ├── argbuilder │ │ ├── mod.rs │ │ ├── flag.rs │ │ ├── positional.rs │ │ └── option.rs │ ├── matchedarg.rs │ ├── mod.rs │ ├── subcommand.rs │ ├── argmatches.rs │ └── group.rs ├── app │ ├── mod.rs │ ├── suggestions.rs │ ├── settings.rs │ └── errors.rs ├── lib.rs ├── fmt.rs └── usageparser.rs ├── tests ├── yaml.rs ├── unique_args.rs ├── print_help.rs ├── app_settings.rs ├── conflicts.rs ├── flags.rs ├── multiple_occurrences.rs ├── opts.rs ├── app.yml ├── positionals.rs ├── require.rs └── posix_compatible.rs ├── .gitignore ├── .clog.toml ├── benches ├── 01_default.rs ├── 02_simple.rs └── 03_complex.rs ├── .travis.yml ├── Makefile ├── CONTRIBUTORS.md ├── examples ├── 10_default_values.rs ├── 09_auto_version.rs ├── 02_apps.rs ├── 11_only_specific_values.rs ├── 17_yaml.rs ├── 13b_enum_values_manual.rs ├── 15_custom_validator.rs ├── 16_app_settings.rs ├── 12_typed_values.rs ├── 13a_enum_values_automatic.rs ├── 04_using_matches.rs ├── 08_subcommands.rs ├── 18_builder_macro.rs ├── 05_flag_args.rs ├── 17_yaml.yml ├── 06_positional_args.rs ├── 01c_quick_example.rs ├── 01a_quick_example.rs ├── 07_option_args.rs ├── 14_groups.rs ├── 01b_quick_example.rs └── 03_args.rs ├── LICENSE-MIT ├── Cargo.toml └── CONTRIBUTING.md /clap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nelsonjchen/clap-rs/master/clap.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /clap-tests/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | /target/ 12 | -------------------------------------------------------------------------------- /clap-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "claptests" 4 | version = "0.0.1" 5 | authors = ["Kevin K. "] 6 | 7 | [dependencies.clap] 8 | path = ".." 9 | -------------------------------------------------------------------------------- /src/args/argbuilder/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::flag::FlagBuilder; 2 | pub use self::option::OptBuilder; 3 | pub use self::positional::PosBuilder; 4 | 5 | mod flag; 6 | mod positional; 7 | mod option; 8 | -------------------------------------------------------------------------------- /src/app/mod.rs: -------------------------------------------------------------------------------- 1 | mod settings; 2 | mod app; 3 | mod suggestions; 4 | mod errors; 5 | 6 | pub use self::settings::AppSettings; 7 | pub use self::app::App; 8 | pub use self::errors::{ClapError, ClapErrorType}; 9 | -------------------------------------------------------------------------------- /tests/yaml.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate clap; 3 | 4 | use clap::App; 5 | 6 | #[test] 7 | #[cfg(feature="yaml")] 8 | fn create_app_from_yaml() { 9 | let yml = load_yaml!("app.yml"); 10 | App::from_yaml(yml); 11 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | /target/ 12 | 13 | # Cargo files 14 | Cargo.lock 15 | 16 | # Temp files 17 | .*~ 18 | 19 | # Backup files 20 | *.bak 21 | *.bk 22 | -------------------------------------------------------------------------------- /.clog.toml: -------------------------------------------------------------------------------- 1 | [clog] 2 | repository = "https://github.com/kbknapp/clap-rs" 3 | outfile = "CHANGELOG.md" 4 | from-latest-tag = true 5 | 6 | [sections] 7 | Performance = ["perf"] 8 | Improvements = ["impr", "im", "imp"] 9 | Documentation = ["docs"] 10 | Deprecations = ["depr"] 11 | Examples = ["examples"] -------------------------------------------------------------------------------- /src/args/matchedarg.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | #[doc(hidden)] 4 | pub struct MatchedArg { 5 | // #[doc(hidden)] 6 | // pub name: String, 7 | #[doc(hidden)] 8 | pub occurrences: u8, 9 | #[doc(hidden)] 10 | // Consider VecMap once stablized 11 | pub values: Option>, 12 | } 13 | -------------------------------------------------------------------------------- /clap-tests/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build test clean 2 | 3 | build: 4 | @# It may be that this project was never built, and no Cargo.lock exists. 5 | @# Thus it may be ignored 6 | cargo update 2>/dev/null || : 7 | cargo build --release 8 | 9 | test: 10 | $(MAKE) build || ($(MAKE) clean && false) 11 | ./run_tests.py 12 | 13 | clean: 14 | cargo clean 15 | -------------------------------------------------------------------------------- /benches/01_default.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate clap; 4 | extern crate test; 5 | 6 | use clap::App; 7 | 8 | use test::Bencher; 9 | 10 | #[bench] 11 | fn build_app(b: &mut Bencher) { 12 | b.iter(|| App::new("claptests")); 13 | } 14 | 15 | #[bench] 16 | fn parse_clean(b: &mut Bencher) { 17 | b.iter(|| App::new("claptests").get_matches_from(vec![""])); 18 | } 19 | -------------------------------------------------------------------------------- /src/args/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::arg::Arg; 2 | pub use self::argmatches::ArgMatches; 3 | pub use self::subcommand::SubCommand; 4 | pub use self::argbuilder::{FlagBuilder, OptBuilder, PosBuilder}; 5 | pub use self::matchedarg::MatchedArg; 6 | pub use self::group::ArgGroup; 7 | 8 | mod arg; 9 | mod argmatches; 10 | mod subcommand; 11 | mod argbuilder; 12 | mod matchedarg; 13 | mod group; 14 | -------------------------------------------------------------------------------- /tests/unique_args.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | 6 | #[test] 7 | #[should_panic] 8 | fn unique_arg_names() { 9 | App::new("some").args(vec![ 10 | Arg::with_name("arg").short("a"), 11 | Arg::with_name("arg").short("b") 12 | ]); 13 | } 14 | 15 | #[test] 16 | #[should_panic] 17 | fn unique_arg_shorts() { 18 | App::new("some").args(vec![ 19 | Arg::with_name("arg1").short("a"), 20 | Arg::with_name("arg2").short("a") 21 | ]); 22 | } 23 | 24 | #[test] 25 | #[should_panic] 26 | fn unique_arg_longs() { 27 | App::new("some").args(vec![ 28 | Arg::with_name("arg1").long("long"), 29 | Arg::with_name("arg2").long("long") 30 | ]); 31 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: required 3 | rust: 4 | - nightly 5 | - beta 6 | - stable 7 | before_script: 8 | - | 9 | pip install 'travis-cargo<0.2' --user && 10 | export PATH=$HOME/.local/bin:$PATH 11 | script: 12 | - | 13 | cargo build --features yaml && 14 | cargo test --features yaml && 15 | make -C clap-tests test && 16 | travis-cargo --only stable doc 17 | after_success: 18 | - | 19 | travis-cargo --only stable doc-upload && 20 | travis-cargo --only stable coveralls -- --features yaml 21 | env: 22 | global: 23 | secure: JLBlgHY6OEmhJ8woewNJHmuBokTNUv7/WvLkJGV8xk0t6bXBwSU0jNloXwlH7FiQTc4TccX0PumPDD4MrMgxIAVFPmmmlQOCmdpYP4tqZJ8xo189E5zk8lKF5OyaVYCs5SMmFC3cxCsKjfwGIexNu3ck5Uhwe9jI0tqgkgM3URA= 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) 2 | THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd) 3 | 4 | test: 5 | cargo test 6 | 7 | build: 8 | cargo build 9 | 10 | update-readme: 11 | cd "$(THIS_DIR)" 12 | cp src/lib.rs code.bak 13 | cat README.md | sed -e 's/^/\/\/! /g' > readme.bak 14 | sed -i '/\/\/!/d' src/lib.rs 15 | sed -i '/\/\/ DOCS/r readme.bak' src/lib.rs 16 | cat src/lib.rs | sed -e 's/`rust/`ignore/g' > src/lib.rs.tmp 17 | cat src/lib.rs.tmp | sed -e 's/`toml/`ignore/g' > src/lib.rs 18 | cat src/lib.rs | sed -e 's/\`sh/`ignore/g' > src/lib.rs.tmp 19 | make clean || (make clean && false) 20 | 21 | clean: 22 | cd "$(THIS_DIR)" 23 | mv code.bak src/lib.rs || true 24 | rm src/lib.rs.t* || true 25 | rm *.bak || true 26 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | The following is a list of contributors to the [clap](https://github.com/kbknapp/clap-rs) project, in alphabetical order: 2 | 3 | * [Alexander Kuvaev](https://github.com/Vinatorul) <> 4 | * [Ivan Dmitrievsky](https://github.com/idmit) <> 5 | * [J/A](https://github.com/archer884) 6 | * [Jacob Helwig](https://github.com/jhelwig) <> 7 | * [James McGlashan](https://github.com/james-darkfox) 8 | * [Kevin K.](https://github.com/kbknapp) <> 9 | * [Markus Unterwaditzer](https://github.com/untitaker) <> 10 | * [Sebastian Thiel](https://github.com/Byron) <> 11 | * [Severen Redwood](https://github.com/SShrike) <> 12 | * [SungRim Huh](https://github.com/sru) <> 13 | * [Tshepang Lekhonkhobe](https://github.com/tshepang) <> 14 | * [Vincent Prouillet](https://github.com/Keats) 15 | -------------------------------------------------------------------------------- /examples/10_default_values.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | fn main() { 6 | // You can get a "default value" like feature by using Option's .unwrap_or() method 7 | // 8 | // Let's assume you have -c argument to allow users to specify a configuration file 9 | // but you also want to support a default file, if none is specified. 10 | let matches = App::new("myapp").about("does awesome things") 11 | .arg(Arg::with_name("CONFIG") 12 | .help("The config file to use (default is \"config.json\")") 13 | .short("c") 14 | .takes_value(true)) 15 | .get_matches(); 16 | 17 | let config_file = matches.value_of("CONFIG").unwrap_or("config.json"); 18 | 19 | // If the user passed in a -c we'll see that value, if not we'll see 'config.json' 20 | println!("The config file is: {}", config_file); 21 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_type= "lib"] 2 | #![cfg_attr(feature = "nightly", feature(plugin))] 3 | //#![cfg_attr(feature = "lints", plugin(clippy))] 4 | //#![cfg_attr(feature = "lints", allow(option_unwrap_used))] 5 | //#![cfg_attr(feature = "lints", allow(explicit_iter_loop))] 6 | //#![cfg_attr(feature = "lints", deny(warnings))] 7 | // Fix until clippy on crates.io is updated to include needless_lifetimes lint 8 | //#![cfg_attr(feature = "lints", allow(unknown_lints))] 9 | 10 | // DOCS 11 | 12 | #[cfg(feature = "suggestions")] 13 | extern crate strsim; 14 | #[cfg(feature = "color")] 15 | extern crate ansi_term; 16 | #[cfg(feature = "yaml")] 17 | extern crate yaml_rust; 18 | 19 | #[cfg(feature = "yaml")] 20 | pub use yaml_rust::YamlLoader; 21 | pub use args::{Arg, SubCommand, ArgMatches, ArgGroup}; 22 | pub use app::{App, AppSettings, ClapError, ClapErrorType}; 23 | pub use fmt::Format; 24 | 25 | #[macro_use] 26 | mod macros; 27 | mod app; 28 | mod args; 29 | mod usageparser; 30 | mod fmt; 31 | 32 | #[cfg(test)] 33 | mod tests; 34 | -------------------------------------------------------------------------------- /tests/print_help.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::App; 4 | 5 | #[test] 6 | fn print_app_help() { 7 | let mut app = App::new("test") 8 | .author("Kevin K.") 9 | .about("tests stuff") 10 | .version("1.3") 11 | .args_from_usage("-f, --flag 'some flag' 12 | --option [opt] 'some option'"); 13 | // We call a get_matches method to cause --help and --version to be built 14 | let _ = app.get_matches_from_safe_borrow(vec![""]); 15 | 16 | // Now we check the output of print_help() 17 | let mut help = vec![]; 18 | app.write_help(&mut help).ok().expect("failed to print help"); 19 | assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\ 20 | Kevin K. 21 | tests stuff 22 | 23 | USAGE: 24 | \ttest [FLAGS] [OPTIONS] 25 | 26 | FLAGS: 27 | -f, --flag some flag 28 | -h, --help Prints help information 29 | -V, --version Prints version information 30 | 31 | OPTIONS: 32 | --option some option\n")); 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kevin B. Knapp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "clap" 4 | version = "1.4.0" 5 | authors = ["Kevin K. "] 6 | exclude = ["examples/*", "clap-tests/*", "tests/*", "benches/*", "clap.png"] 7 | description = "A simple to use, efficient, and full featured Command Line Argument Parser" 8 | repository = "https://github.com/kbknapp/clap-rs.git" 9 | documentation = "http://kbknapp.github.io/clap-rs" 10 | readme = "README.md" 11 | license = "MIT" 12 | keywords = ["argument", "command", "arg", "parser", "parse"] 13 | 14 | [dependencies.strsim] 15 | version = "~0.4.0" 16 | optional = true 17 | 18 | [dependencies.ansi_term] 19 | version = "~0.6.3" 20 | optional = true 21 | 22 | [dependencies.yaml-rust] 23 | version = "~0.2.1" 24 | optional = true 25 | 26 | #[dependencies.clippy] 27 | #version = "~0.0.12" 28 | #optional = true 29 | 30 | [features] 31 | default=["suggestions", "color"] 32 | suggestions=["strsim"] 33 | color = ["ansi_term"] 34 | yaml = ["yaml-rust"] 35 | #lints = ["clippy", "nightly"] 36 | 37 | # for building with nightly and unstable features 38 | nightly = [] 39 | 40 | # for building with travis-cargo 41 | #unstable = ["lints", "nightly"] 42 | unstable = ["nightly"] 43 | 44 | # for building with debug messages 45 | debug = [] 46 | 47 | -------------------------------------------------------------------------------- /examples/09_auto_version.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate clap; 3 | 4 | use clap::App; 5 | 6 | fn main() { 7 | // You can have clap pull the application version directly from your Cargo.toml starting with 8 | // clap v0.4.14 on crates.io (or master#a81f915 on github). Using Rust's env! macro like this: 9 | // 10 | // let version = format!("{}.{}.{}{}", 11 | // env!("CARGO_PKG_VERSION_MAJOR"), 12 | // env!("CARGO_PKG_VERSION_MINOR"), 13 | // env!("CARGO_PKG_VERSION_PATCH"), 14 | // option_env!("CARGO_PKG_VERSION_PRE").unwrap_or("")); 15 | // 16 | // Starting from v0.6.6 on crates.io you can also use the crate_version!() macro instead of 17 | // manually using the env!() macros. Under the hood, the macro uses this exact method to get 18 | // the version. 19 | // 20 | // Thanks to https://github.com/jhelwig for pointing this out 21 | App::new("myapp") 22 | .about("does awesome things") 23 | // use crate_version! to pull the version number 24 | .version(&crate_version!()[..]) 25 | .get_matches(); 26 | 27 | // running the this app with the -V or --version will display whatever version is in your 28 | // Cargo.toml, the default being: myapp 0.0.1 29 | } 30 | -------------------------------------------------------------------------------- /benches/02_simple.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate clap; 4 | extern crate test; 5 | 6 | use clap::App; 7 | 8 | use test::Bencher; 9 | 10 | macro_rules! create_app { 11 | () => ({ 12 | App::new("claptests") 13 | .version("0.1") 14 | .about("tests clap library") 15 | .author("Kevin K. ") 16 | .args_from_usage("-f --flag 'tests flags' 17 | -o --option=[opt] 'tests options' 18 | [positional] 'tests positional'") 19 | }) 20 | } 21 | 22 | #[bench] 23 | fn build_app(b: &mut Bencher) { 24 | 25 | b.iter(|| create_app!()); 26 | } 27 | 28 | #[bench] 29 | fn parse_clean(b: &mut Bencher) { 30 | b.iter(|| create_app!().get_matches_from(vec![""])); 31 | } 32 | 33 | #[bench] 34 | fn parse_flag(b: &mut Bencher) { 35 | b.iter(|| create_app!().get_matches_from(vec!["", "-f"])); 36 | } 37 | 38 | #[bench] 39 | fn parse_option(b: &mut Bencher) { 40 | b.iter(|| create_app!().get_matches_from(vec!["", "-o", "option1"])); 41 | } 42 | 43 | #[bench] 44 | fn parse_positional(b: &mut Bencher) { 45 | b.iter(|| create_app!().get_matches_from(vec!["", "arg1"])); 46 | } 47 | 48 | #[bench] 49 | fn parse_complex(b: &mut Bencher) { 50 | b.iter(|| create_app!().get_matches_from(vec!["", "-o", "option1", "-f", "arg1"])); 51 | } 52 | -------------------------------------------------------------------------------- /examples/02_apps.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App}; 4 | 5 | fn main() { 6 | // Apps describe the top level application 7 | // 8 | // You create an App and set various options on that App using the "builder pattern" 9 | // 10 | // The options (version(), author(), about()) aren't mandatory, but recommended. There is 11 | // another option, usage(), which is an exception to the rule. This should only be used when 12 | // the default usage string automatically generated by clap doesn't suffice. 13 | // 14 | // You also set all the valid arguments your App should accept via the arg(), args(), arg_from_usage() 15 | // and args_from_usage() (as well as subcommands via the subcommand() and subcommands() methods) which 16 | // will be covered later. 17 | // 18 | // Once all options have been set, call .get_matches() in order to start the parsing and find all valid 19 | // command line arguments that supplied by the user at runtime. The name given to new() will be displayed 20 | // when the version or help flags are used. 21 | App::new("MyApp") 22 | .version("1.0") 23 | .author("Kevin K. ") 24 | .about("Does awesome things") 25 | .get_matches(); 26 | 27 | // This example doesn't do much, but it *does* give automatic -h, --help, -V, and --version functionality ;) 28 | 29 | // Continued program logic goes here... 30 | } 31 | -------------------------------------------------------------------------------- /examples/11_only_specific_values.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | fn main() { 6 | // If you have arguments of specific values you want to test for, you can use the 7 | // .possible_values() method of Arg 8 | // 9 | // This allows you specify the valid values for that argument. If the user does not use one of 10 | // those specific values, they will receive a graceful exit with error message informing them 11 | // of the mistake, and what the possible valid values are 12 | // 13 | // For this example, assume you want one positional argument of either "fast" or "slow" 14 | // i.e. the only possible ways to run the program are "myprog fast" or "myprog slow" 15 | let mode_vals = ["fast", "slow"]; 16 | let matches = App::new("myapp").about("does awesome things") 17 | .arg(Arg::with_name("MODE") 18 | .help("What mode to run the program in") 19 | .index(1) 20 | .possible_values(&mode_vals) 21 | .required(true)) 22 | .get_matches(); 23 | 24 | // Note, it's safe to call unwrap() because the arg is required 25 | match matches.value_of("MODE").unwrap() { 26 | "fast" => { 27 | // Do fast things... 28 | }, 29 | "slow" => { 30 | // Do slow things... 31 | }, 32 | _ => unreachable!() 33 | } 34 | } -------------------------------------------------------------------------------- /examples/17_yaml.rs: -------------------------------------------------------------------------------- 1 | // In order to use YAML to define your CLI you must compile clap with the "yaml" feature becasue 2 | // it's **not** included by default. 3 | // 4 | // In order to do this, ensure your Cargo.toml looks like one of the following: 5 | // 6 | // [dependencies.clap] 7 | // features = ["yaml"] 8 | // 9 | // __OR__ 10 | // 11 | // [dependencies] 12 | // clap = { features = ["yaml"] } 13 | 14 | 15 | // Using yaml requires calling a clap macro `load_yaml!()` so we must use the '#[macro_use]' 16 | // directive 17 | #[macro_use] 18 | extern crate clap; 19 | 20 | use clap::App; 21 | 22 | fn main() { 23 | // To load a yaml file containing our CLI definition such as the example '17_yaml.yml' we can 24 | // use the convenience macro which loads the file at compile relative to the current file 25 | // similiar to how modules are found. 26 | // 27 | // Then we pass that yaml object to App to build the CLI. 28 | // 29 | // Finally we call get_matches() to start the parsing process. We use the matches just as we 30 | // normally would 31 | let yml = load_yaml!("17_yaml.yml"); 32 | let m = App::from_yaml(yml).get_matches(); 33 | 34 | // Because the example 17_yaml.yml is rather large we'll just look a single arg so you can 35 | // see that it works... 36 | if let Some(mode) = m.value_of("mode") { 37 | match mode { 38 | "fast" => println!("We're really going now!"), 39 | "slow" => println!("Awwww, too slow :("), 40 | _ => unreachable!() 41 | } 42 | } else { 43 | println!("--mode wasn't used..."); 44 | } 45 | } -------------------------------------------------------------------------------- /examples/13b_enum_values_manual.rs: -------------------------------------------------------------------------------- 1 | // If you require more complex configuration than simple_enum! provides, you can implement the 2 | // trait manually, as in the following example. 3 | // 4 | // In the following example we will create an enum with 4 values, assign a positional argument 5 | // that accepts only one of those values, and use clap to parse the argument. 6 | // 7 | // Start with bringing the trait into scope. 8 | use std::str::FromStr; 9 | 10 | // Add clap like normal 11 | #[macro_use] 12 | extern crate clap; 13 | 14 | use clap::{App, Arg}; 15 | 16 | // Define your enum 17 | enum Vals { 18 | Foo, 19 | Bar, 20 | Baz, 21 | Qux 22 | } 23 | 24 | // Implement the trait 25 | impl FromStr for Vals { 26 | type Err = &'static str; 27 | 28 | fn from_str(s: &str) -> Result { 29 | match s { 30 | "Foo" => Ok(Vals::Foo), 31 | "Bar" => Ok(Vals::Bar), 32 | "Baz" => Ok(Vals::Baz), 33 | "Qux" => Ok(Vals::Qux), 34 | _ => Err("no match") 35 | } 36 | } 37 | } 38 | 39 | fn main() { 40 | // Create the application like normal 41 | let enum_vals = ["Foo", "Bar", "Baz", "Qux"]; 42 | let m = App::new("myapp") 43 | // Use a single positional argument that is required 44 | .arg(Arg::from_usage(" 'The type to use'") 45 | // Define the list of possible values 46 | .possible_values(&enum_vals)) 47 | .get_matches(); 48 | 49 | let t = value_t_or_exit!(m.value_of("type"), Vals); 50 | 51 | // Now we can use our enum like normal. 52 | match t { 53 | Vals::Foo => println!("Found a Foo"), 54 | Vals::Bar => println!("Found a Bar"), 55 | Vals::Baz => println!("Found a Baz"), 56 | Vals::Qux => println!("Found a Qux") 57 | } 58 | } -------------------------------------------------------------------------------- /examples/15_custom_validator.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | fn main() { 6 | // You can define a function (or a closure) to use as a validator to argument values. The 7 | // function must accept a String and return Result<(), String> where Err(String) is the message 8 | // displayed to the user. 9 | 10 | let matches = App::new("myapp") 11 | // Application logic goes here... 12 | .arg(Arg::with_name("input") 13 | .help("the input file to use") 14 | .index(1) 15 | .required(true) 16 | 17 | .validator(|val| { 18 | // val is the argument value passed in by the user 19 | // val has type of String. 20 | 21 | if val.ends_with(".png") { 22 | Ok(()) 23 | } else { 24 | // clap automatically adds "error: " to the beginning 25 | // of the message. 26 | Err(String::from("the file format must be png.")) 27 | } 28 | 29 | // Of course, you can do more complicated validation as 30 | // well, but for the simplicity, this example only checks 31 | // if the value passed in ends with ".png" or not. 32 | })) 33 | .get_matches(); 34 | 35 | // Here we can call .unwrap() because the argument is required. 36 | println!("The .PNG file is: {}", matches.value_of("input").unwrap()); 37 | } 38 | -------------------------------------------------------------------------------- /tests/app_settings.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg, SubCommand, AppSettings, ClapErrorType}; 4 | 5 | #[test] 6 | fn sub_command_negate_requred() { 7 | App::new("sub_command_negate") 8 | .setting(AppSettings::SubcommandsNegateReqs) 9 | .arg(Arg::with_name("test") 10 | .required(true) 11 | .index(1)) 12 | .subcommand(SubCommand::with_name("sub1")) 13 | .get_matches_from(vec!["", "sub1"]); 14 | } 15 | 16 | #[test] 17 | fn sub_command_negate_requred_2() { 18 | let result = App::new("sub_command_negate") 19 | .setting(AppSettings::SubcommandsNegateReqs) 20 | .arg(Arg::with_name("test") 21 | .required(true) 22 | .index(1)) 23 | .subcommand(SubCommand::with_name("sub1")) 24 | .get_matches_from_safe(vec![""]); 25 | assert!(result.is_err()); 26 | let err = result.err().unwrap(); 27 | assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument); 28 | } 29 | 30 | #[test] 31 | fn app_settings_fromstr() { 32 | assert_eq!("subcommandsnegatereqs".parse::().ok().unwrap(), AppSettings::SubcommandsNegateReqs); 33 | assert_eq!("subcommandsrequired".parse::().ok().unwrap(), AppSettings::SubcommandRequired); 34 | assert_eq!("argrequiredelsehelp".parse::().ok().unwrap(), AppSettings::ArgRequiredElseHelp); 35 | assert_eq!("globalversion".parse::().ok().unwrap(), AppSettings::GlobalVersion); 36 | assert_eq!("versionlesssubcommands".parse::().ok().unwrap(), AppSettings::VersionlessSubcommands); 37 | assert_eq!("unifiedhelpmessage".parse::().ok().unwrap(), AppSettings::UnifiedHelpMessage); 38 | assert_eq!("waitonerror".parse::().ok().unwrap(), AppSettings::WaitOnError); 39 | assert_eq!("subcommandrequiredelsehelp".parse::().ok().unwrap(), AppSettings::SubcommandRequiredElseHelp); 40 | assert!("hahahaha".parse::().is_err()); 41 | } -------------------------------------------------------------------------------- /src/app/suggestions.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "suggestions")] 2 | use strsim; 3 | 4 | /// Produces a string from a given list of possible values which is similar to 5 | /// the passed in value `v` with a certain confidence. 6 | /// Thus in a list of possible values like ["foo", "bar"], the value "fop" will yield 7 | /// `Some("foo")`, whereas "blark" would yield `None`. 8 | #[cfg(feature = "suggestions")] 9 | #[cfg_attr(feature = "lints", allow(needless_lifetimes))] 10 | pub fn did_you_mean<'a, T, I>(v: &str, 11 | possible_values: I) 12 | -> Option<&'a str> 13 | where T: AsRef + 'a, 14 | I: IntoIterator 15 | { 16 | 17 | let mut candidate: Option<(f64, &str)> = None; 18 | for pv in possible_values.into_iter() { 19 | let confidence = strsim::jaro_winkler(v, pv.as_ref()); 20 | if confidence > 0.8 && 21 | (candidate.is_none() || (candidate.as_ref().unwrap().0 < confidence)) { 22 | candidate = Some((confidence, pv.as_ref())); 23 | } 24 | } 25 | match candidate { 26 | None => None, 27 | Some((_, candidate)) => Some(candidate), 28 | } 29 | } 30 | 31 | #[cfg(not(feature = "suggestions"))] 32 | pub fn did_you_mean<'a, T, I>(_: &str, 33 | _: I) 34 | -> Option<&'a str> 35 | where T: AsRef + 'a, 36 | I: IntoIterator 37 | { 38 | None 39 | } 40 | 41 | /// A helper to determine message formatting 42 | pub enum DidYouMeanMessageStyle { 43 | /// Suggested value is a long flag 44 | LongFlag, 45 | /// Suggested value is one of various possible values 46 | EnumValue, 47 | } 48 | 49 | #[cfg(test)] 50 | mod test { 51 | use super::*; 52 | 53 | #[test] 54 | fn did_you_mean_possible_values() { 55 | let p_vals = ["test", "possible", "values"]; 56 | assert_eq!(did_you_mean("tst", p_vals.iter()), Some("test")); 57 | assert!(did_you_mean("hahaahahah", p_vals.iter()).is_none()); 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/16_app_settings.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, AppSettings, SubCommand}; 4 | 5 | fn main() { 6 | // You can use AppSettings to change the application level behavior of clap. .setting() function 7 | // of App struct takes AppSettings enum as argument. There is also .settings() function which 8 | // takes slice of AppSettings enum. You can learn more about AppSettings in the documentation, 9 | // which also has examples on each setting. 10 | // 11 | // This example will only show usage of one AppSettings setting. See documentation for more 12 | // information. 13 | 14 | let matches = App::new("myapp") 15 | .setting(AppSettings::SubcommandsNegateReqs) 16 | // Negates requirement of parent command. 17 | 18 | .arg_from_usage(" 'input file to use'") 19 | // Required positional argument called input. This 20 | // will be only required if subcommand is not present. 21 | 22 | .subcommand(SubCommand::with_name("test") 23 | .about("does some testing")) 24 | // if program is invoked with subcommand, you do not 25 | // need to specify the argument anymore due to 26 | // the AppSettings::SubcommandsNegateReqs setting. 27 | 28 | .get_matches(); 29 | 30 | // Calling unwrap() on "input" would not be advised here, because although it's required, 31 | // if the user uses a subcommand, those requirements are no longer required. Hence, we should 32 | // use some sort of 'if let' construct 33 | if let Some(inp) = matches.value_of("input") { 34 | println!("The input file is: {}", inp); 35 | } 36 | 37 | match matches.subcommand() { 38 | ("test", _) => println!("The 'test' subcommand was used"), 39 | _ => unreachable!() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/fmt.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[cfg(all(feature = "color", not(target_os = "windows")))] 4 | use ansi_term::Colour::{Red, Green, Yellow}; 5 | #[cfg(all(feature = "color", not(target_os = "windows")))] 6 | use ansi_term::ANSIString; 7 | 8 | 9 | pub enum Format { 10 | Error(T), 11 | Warning(T), 12 | Good(T), 13 | } 14 | 15 | #[cfg(all(feature = "color", not(target_os = "windows")))] 16 | impl> Format { 17 | fn format(&self) -> ANSIString { 18 | match *self { 19 | Format::Error(ref e) => Red.bold().paint(e.as_ref()), 20 | Format::Warning(ref e) => Yellow.paint(e.as_ref()), 21 | Format::Good(ref e) => Green.paint(e.as_ref()), 22 | } 23 | } 24 | 25 | } 26 | 27 | #[cfg(all(feature = "color", not(target_os = "windows")))] 28 | impl> fmt::Display for Format { 29 | fn fmt(&self, 30 | f: &mut fmt::Formatter) -> fmt::Result { 31 | write!(f, "{}", &self.format()) 32 | } 33 | } 34 | 35 | #[cfg(any(not(feature = "color"), target_os = "windows"))] 36 | impl Format { 37 | fn format(&self) -> &T { 38 | match *self { 39 | Format::Error(ref e) => e, 40 | Format::Warning(ref e) => e, 41 | Format::Good(ref e) => e, 42 | } 43 | } 44 | } 45 | 46 | #[cfg(any(not(feature = "color"), target_os = "windows"))] 47 | impl fmt::Display for Format { 48 | fn fmt(&self, 49 | f: &mut fmt::Formatter) -> fmt::Result { 50 | write!(f, "{}", &self.format()) 51 | } 52 | } 53 | 54 | #[cfg(test)] 55 | mod test { 56 | use super::Format; 57 | use ansi_term::Colour::{Red, Green, Yellow}; 58 | 59 | #[test] 60 | fn colored_output() { 61 | let err = Format::Error("error"); 62 | assert_eq!(&*format!("{}", err), &*format!("{}", Red.bold().paint("error"))); 63 | let good = Format::Good("good"); 64 | assert_eq!(&*format!("{}", good), &*format!("{}", Green.paint("good"))); 65 | let warn = Format::Warning("warn"); 66 | assert_eq!(&*format!("{}", warn), &*format!("{}", Yellow.paint("warn"))); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/args/subcommand.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "yaml")] 2 | use yaml_rust::Yaml; 3 | 4 | use App; 5 | use ArgMatches; 6 | 7 | /// The abstract representation of a command line subcommand used by the consumer of the library. 8 | /// 9 | /// 10 | /// This struct is used by the library consumer and describes all the valid options of the 11 | /// subcommand for their program. SubCommands are treated like "sub apps" and contain all the same 12 | /// possibilities (such as their own arguments and subcommands). 13 | /// 14 | /// # Example 15 | /// 16 | /// ```no_run 17 | /// # use clap::{App, Arg, SubCommand}; 18 | /// # let matches = App::new("myprog") 19 | /// # .subcommand( 20 | /// SubCommand::with_name("conifg") 21 | /// .about("Used for configuration") 22 | /// .arg(Arg::with_name("config_file") 23 | /// .help("The configuration file to use") 24 | /// .index(1)) 25 | /// # ).get_matches(); 26 | pub struct SubCommand<'n, 'a> { 27 | #[doc(hidden)] 28 | pub name: &'n str, 29 | #[doc(hidden)] 30 | pub matches: ArgMatches<'n, 'a>, 31 | } 32 | 33 | impl<'n, 'a> SubCommand<'n, 'a> { 34 | /// Creates a new instance of a subcommand requiring a name. Will be displayed 35 | /// to the user when they print version or help and usage information. 36 | /// 37 | /// # Example 38 | /// 39 | /// ```no_run 40 | /// # use clap::{App, Arg, SubCommand}; 41 | /// # let prog = App::new("myprog").subcommand( 42 | /// SubCommand::with_name("config") 43 | /// # ).get_matches(); 44 | /// ``` 45 | pub fn with_name<'au, 'v, 'ab, 'u, 'h, 'ar>(name: &'ar str) -> App<'au, 'v, 'ab, 'u, 'h, 'ar> { 46 | App::new(name) 47 | } 48 | 49 | /// Creates a new instance of a subcommand from a YAML (.yml) document 50 | /// 51 | /// # Example 52 | /// 53 | /// ```ignore 54 | /// # use clap::{App, Arg, SubCommand}; 55 | /// let sc_yaml = load_yaml!("test_subcommand.yml"); 56 | /// let sc = SubCommand::from_yaml(sc_yaml); 57 | /// ``` 58 | #[cfg(feature = "yaml")] 59 | pub fn from_yaml<'y>(yaml: &'y Yaml) -> App<'y, 'y, 'y, 'y, 'y, 'y> { 60 | App::from_yaml(yaml) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/conflicts.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg, ClapErrorType, ArgGroup}; 4 | 5 | #[test] 6 | fn flag_conflict() { 7 | let result = App::new("flag_conflict") 8 | .arg(Arg::from_usage("-f, --flag 'some flag'") 9 | .conflicts_with("other")) 10 | .arg(Arg::from_usage("-o, --other 'some flag'")) 11 | .get_matches_from_safe(vec!["", "-f", "-o"]); 12 | assert!(result.is_err()); 13 | let err = result.err().unwrap(); 14 | assert_eq!(err.error_type, ClapErrorType::ArgumentConflict); 15 | } 16 | 17 | #[test] 18 | fn flag_conflict_2() { 19 | let result = App::new("flag_conflict") 20 | .arg(Arg::from_usage("-f, --flag 'some flag'") 21 | .conflicts_with("other")) 22 | .arg(Arg::from_usage("-o, --other 'some flag'")) 23 | .get_matches_from_safe(vec!["", "-o", "-f"]); 24 | assert!(result.is_err()); 25 | let err = result.err().unwrap(); 26 | assert_eq!(err.error_type, ClapErrorType::ArgumentConflict); 27 | } 28 | 29 | #[test] 30 | fn group_conflict() { 31 | let result = App::new("group_conflict") 32 | .arg(Arg::from_usage("-f, --flag 'some flag'") 33 | .conflicts_with("gr")) 34 | .arg_group(ArgGroup::with_name("gr") 35 | .required(true) 36 | .add("some") 37 | .add("other")) 38 | .arg(Arg::from_usage("--some 'some arg'")) 39 | .arg(Arg::from_usage("--other 'other arg'")) 40 | .get_matches_from_safe(vec!["", "--other", "-f"]); 41 | assert!(result.is_err()); 42 | let err = result.err().unwrap(); 43 | assert_eq!(err.error_type, ClapErrorType::ArgumentConflict); 44 | } 45 | 46 | #[test] 47 | fn group_conflict_2() { 48 | let result = App::new("group_conflict") 49 | .arg(Arg::from_usage("-f, --flag 'some flag'") 50 | .conflicts_with("gr")) 51 | .arg_group(ArgGroup::with_name("gr") 52 | .required(true) 53 | .add("some") 54 | .add("other")) 55 | .arg(Arg::from_usage("--some 'some arg'")) 56 | .arg(Arg::from_usage("--other 'other arg'")) 57 | .get_matches_from_safe(vec!["", "-f", "--some"]); 58 | assert!(result.is_err()); 59 | let err = result.err().unwrap(); 60 | assert_eq!(err.error_type, ClapErrorType::ArgumentConflict); 61 | } -------------------------------------------------------------------------------- /tests/flags.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | #[test] 6 | fn flag_using_short() { 7 | let m = App::new("flag") 8 | .args(vec![ 9 | Arg::from_usage("-f, --flag 'some flag'"), 10 | Arg::from_usage("-c, --color 'some other flag'") 11 | ]) 12 | .get_matches_from(vec!["", "-f", "-c"]); 13 | assert!(m.is_present("flag")); 14 | assert!(m.is_present("color")); 15 | } 16 | 17 | #[test] 18 | fn flag_using_long() { 19 | let m = App::new("flag") 20 | .args(vec![ 21 | Arg::from_usage("--flag 'some flag'"), 22 | Arg::from_usage("--color 'some other flag'") 23 | ]) 24 | .get_matches_from(vec!["", "--flag", "--color"]); 25 | assert!(m.is_present("flag")); 26 | assert!(m.is_present("color")); 27 | } 28 | 29 | #[test] 30 | fn flag_using_mixed() { 31 | let m = App::new("flag") 32 | .args(vec![ 33 | Arg::from_usage("-f, --flag 'some flag'"), 34 | Arg::from_usage("-c, --color 'some other flag'") 35 | ]) 36 | .get_matches_from(vec!["", "-f", "--color"]); 37 | assert!(m.is_present("flag")); 38 | assert!(m.is_present("color")); 39 | 40 | let m = App::new("flag") 41 | .args(vec![ 42 | Arg::from_usage("-f, --flag 'some flag'"), 43 | Arg::from_usage("-c, --color 'some other flag'") 44 | ]) 45 | .get_matches_from(vec!["", "--flag", "-c"]); 46 | assert!(m.is_present("flag")); 47 | assert!(m.is_present("color")); 48 | } 49 | 50 | #[test] 51 | fn multiple_flags_in_single() { 52 | let m = App::new("multe_flags") 53 | .args(vec![ 54 | Arg::from_usage("-f, --flag 'some flag'"), 55 | Arg::from_usage("-c, --color 'some other flag'"), 56 | Arg::from_usage("-d, --debug 'another other flag'") 57 | ]) 58 | .get_matches_from(vec!["", "-fcd"]); 59 | assert!(m.is_present("flag")); 60 | assert!(m.is_present("color")); 61 | assert!(m.is_present("debug")); 62 | } 63 | 64 | #[test] 65 | #[should_panic] 66 | fn short_flag_misspel() { 67 | App::new("short_flag") 68 | .arg(Arg::from_usage("-f1, --flag 'some flag'")); 69 | } 70 | 71 | #[test] 72 | #[should_panic] 73 | fn short_flag_name_missing() { 74 | App::new("short_flag") 75 | .arg(Arg::from_usage("-f 'some flag'")); 76 | } -------------------------------------------------------------------------------- /tests/multiple_occurrences.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | 6 | #[test] 7 | fn multiple_occurrences_of_flags_long() { 8 | let m = App::new("multiple_occurrences") 9 | .arg(Arg::from_usage("--multflag 'allowed multiple flag'") 10 | .multiple(true)) 11 | .arg(Arg::from_usage("--flag 'disallowed multiple flag'")) 12 | .get_matches_from(vec![ 13 | "", 14 | "--multflag", 15 | "--flag", 16 | "--multflag" 17 | ]); 18 | assert!(m.is_present("multflag")); 19 | assert_eq!(m.occurrences_of("multflag"), 2); 20 | assert!(m.is_present("flag")); 21 | assert_eq!(m.occurrences_of("flag"), 1) 22 | } 23 | 24 | #[test] 25 | fn multiple_occurrences_of_flags_short() { 26 | let m = App::new("multiple_occurrences") 27 | .arg(Arg::from_usage("-m --multflag 'allowed multiple flag'") 28 | .multiple(true)) 29 | .arg(Arg::from_usage("-f --flag 'disallowed multiple flag'")) 30 | .get_matches_from(vec![ 31 | "", 32 | "-m", 33 | "-f", 34 | "-m" 35 | ]); 36 | assert!(m.is_present("multflag")); 37 | assert_eq!(m.occurrences_of("multflag"), 2); 38 | assert!(m.is_present("flag")); 39 | assert_eq!(m.occurrences_of("flag"), 1); 40 | } 41 | 42 | #[test] 43 | fn multiple_occurrences_of_flags_mixed() { 44 | let m = App::new("multiple_occurrences") 45 | .arg(Arg::from_usage("-m, --multflag1 'allowed multiple flag'") 46 | .multiple(true)) 47 | .arg(Arg::from_usage("-n, --multflag2 'another allowed multiple flag'") 48 | .multiple(true)) 49 | .arg(Arg::from_usage("-f, --flag 'disallowed multiple flag'")) 50 | .get_matches_from(vec![ 51 | "", 52 | "-m", 53 | "-f", 54 | "-n", 55 | "--multflag1", 56 | "-m", 57 | "--multflag2" 58 | ]); 59 | assert!(m.is_present("multflag1")); 60 | assert_eq!(m.occurrences_of("multflag1"), 3); 61 | assert!(m.is_present("multflag2")); 62 | assert_eq!(m.occurrences_of("multflag2"), 2); 63 | assert!(m.is_present("flag")); 64 | assert_eq!(m.occurrences_of("flag"), 1); 65 | } -------------------------------------------------------------------------------- /src/args/argbuilder/flag.rs: -------------------------------------------------------------------------------- 1 | // use std::collections::HashSet; 2 | use std::fmt::{Display, Formatter, Result}; 3 | 4 | pub struct FlagBuilder<'n> { 5 | pub name: &'n str, 6 | /// The long version of the flag (i.e. word) 7 | /// without the preceding `--` 8 | pub long: Option<&'n str>, 9 | /// The string of text that will displayed to 10 | /// the user when the application's `help` 11 | /// text is displayed 12 | pub help: Option<&'n str>, 13 | /// Determines if multiple instances of the same 14 | /// flag are allowed 15 | /// I.e. `-v -v -v` or `-vvv` 16 | pub multiple: bool, 17 | /// A list of names for other arguments that 18 | /// *may not* be used with this flag 19 | pub blacklist: Option>, 20 | /// A list of names of other arguments that 21 | /// are *required* to be used when this 22 | /// flag is used 23 | pub requires: Option>, 24 | /// The short version (i.e. single character) 25 | /// of the argument, no preceding `-` 26 | pub short: Option, 27 | pub global: bool, 28 | /// A list of names for other arguments that *mutually override* this flag 29 | pub overrides: Option>, 30 | pub hidden: bool 31 | } 32 | 33 | impl<'n> Display for FlagBuilder<'n> { 34 | fn fmt(&self, 35 | f: &mut Formatter) 36 | -> Result { 37 | if let Some(l) = self.long { 38 | write!(f, "--{}", l) 39 | } else { 40 | write!(f, "-{}", self.short.unwrap()) 41 | } 42 | } 43 | } 44 | #[cfg(test)] 45 | mod test { 46 | use super::FlagBuilder; 47 | 48 | #[test] 49 | fn flagbuilder_display() { 50 | let f = FlagBuilder { 51 | name: "flg", 52 | short: None, 53 | long: Some("flag"), 54 | help: None, 55 | multiple: true, 56 | blacklist: None, 57 | requires: None, 58 | global: false, 59 | overrides: None, 60 | hidden: false, 61 | }; 62 | 63 | assert_eq!(&*format!("{}", f), "--flag"); 64 | 65 | let f2 = FlagBuilder { 66 | name: "flg", 67 | short: Some('f'), 68 | long: None, 69 | help: None, 70 | multiple: false, 71 | blacklist: None, 72 | requires: None, 73 | global: false, 74 | overrides: None, 75 | hidden: false, 76 | }; 77 | 78 | assert_eq!(&*format!("{}", f2), "-f"); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /examples/12_typed_values.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate clap; 3 | 4 | use clap::App; 5 | 6 | fn main() { 7 | // You can use some convenience macros provided by clap to get typed values, so long as the 8 | // type you specify implements std::str::FromStr 9 | // 10 | // This works for both single, and multiple values (multiple values returns a Vec) 11 | // 12 | // There are also two ways in which to get types, those where failures cause the program to exit 13 | // with an error and usage string, and those which return a Result or Result,String> 14 | // respectively. Both methods support single and multiple values. 15 | // 16 | // The macro which returns a Result allows you decide what to do upon a failure, exit, provide a 17 | // default value, etc. You have control. But it also means you have to write the code or boiler plate 18 | // to handle those instances. 19 | // 20 | // That is why the second method exists, so you can simply get a T or Vec back, or be sure the 21 | // program will exit gracefully. The catch is, the second method should *only* be used on required 22 | // arguments, because if the argument isn't found, it exits. Just FYI ;) 23 | // 24 | // The following example shows both methods. 25 | // 26 | // **NOTE:** to use the macros, you must include #[macro_use] just above the 'extern crate clap;' 27 | // declaration in your crate root. 28 | let matches = App::new("myapp") 29 | // Create two arguments, a required positional which accepts multiple values 30 | // and an optional '-l value' 31 | .args_from_usage( 32 | "... 'A sequence of whole positive numbers, i.e. 20 25 30' 33 | -l [len] 'A length to use, defaults to 10 when omitted'") 34 | .get_matches(); 35 | 36 | // Here we get a value of type u32 from our optional -l argument. 37 | // If the value provided to len failes to parse, we default to 10 38 | // 39 | // Using other methods such as unwrap_or_else(|e| println!("{}",e)) 40 | // are possible too. 41 | let len = value_t!(matches.value_of("len"), u32).unwrap_or(10); 42 | 43 | println!("len ({}) + 2 = {}", len, len + 2); 44 | 45 | // This code loops through all the values provided to "seq" and adds 2 46 | // If seq fails to parse, the program exits, you don't have an option 47 | for v in value_t_or_exit!(matches.values_of("seq"), u32) { 48 | println!("Sequence part {} + 2: {}", v, v + 2); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/13a_enum_values_automatic.rs: -------------------------------------------------------------------------------- 1 | // You can use clap's value_t! macro with a custom enum by implementing the std::str::FromStr 2 | // trait which is very straight forward. There are three ways to do this, for simple enums 3 | // meaning those that don't require 'pub' or any '#[derive()]' directives you can use clas's 4 | // simple_enum! macro. For those that require 'pub' or any '#[derive()]'s you can use clap's 5 | // arg_enum! macro. The third way is to implement std::str::FromStr manually. 6 | // 7 | // In most circumstances using either simple_enum! or arg_enum! is fine. 8 | // 9 | // In the following example we will create two enums using macros, assign a positional argument 10 | // that accepts only one of those values, and use clap to parse the argument. 11 | 12 | // Add clap like normal 13 | #[macro_use] 14 | extern crate clap; 15 | 16 | use clap::{App, Arg}; 17 | 18 | // Define your enum, the simple_num! macro takes a enum name followed by => and each value 19 | // separated by a ',' 20 | simple_enum!{ Foo => Bar, Baz, Qux } 21 | 22 | // Using arg_enum! is more like traditional enum declarations 23 | // 24 | // **NOTE:** Only bare variants are supported 25 | arg_enum!{ 26 | #[derive(Debug)] 27 | pub enum Oof { 28 | Rab, 29 | Zab, 30 | Xuq 31 | } 32 | } 33 | 34 | fn main() { 35 | // Create the application like normal 36 | let enum_vals = ["fast", "slow"]; 37 | let m = App::new("myapp") 38 | // Use a single positional argument that is required 39 | .arg(Arg::from_usage(" 'The Foo to use'") 40 | // You can define a list of possible values if you want the values to be 41 | // displayed in the help information. Whether you use possible_values() or 42 | // not, the valid values will ALWAYS be displayed on a failed parse. 43 | .possible_values(&enum_vals)) 44 | // For the second positional, lets not use possible_values() just to show the difference 45 | .arg_from_usage(" 'The Oof to use'") 46 | .get_matches(); 47 | 48 | let t = value_t_or_exit!(m.value_of("type"), Foo); 49 | let t2 = value_t_or_exit!(m.value_of("type2"), Oof); 50 | 51 | 52 | // Now we can use our enum like normal. 53 | match t { 54 | Foo::Bar => println!("Found a Bar"), 55 | Foo::Baz => println!("Found a Baz"), 56 | Foo::Qux => println!("Found a Qux") 57 | } 58 | 59 | // Since our Oof derives Debug, we can do this: 60 | println!("Oof: {:?}", t2); 61 | } -------------------------------------------------------------------------------- /tests/opts.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | #[test] 6 | fn opts_using_short() { 7 | let m = App::new("opts") 8 | .args(vec![ 9 | Arg::from_usage("-f [flag] 'some flag'"), 10 | Arg::from_usage("-c [color] 'some other flag'") 11 | ]) 12 | .get_matches_from(vec!["", "-f", "some", "-c", "other"]); 13 | assert!(m.is_present("flag")); 14 | assert_eq!(m.value_of("flag").unwrap(), "some"); 15 | assert!(m.is_present("color")); 16 | assert_eq!(m.value_of("color").unwrap(), "other"); 17 | } 18 | 19 | #[test] 20 | fn opts_using_long_space() { 21 | let m = App::new("opts") 22 | .args(vec![ 23 | Arg::from_usage("--flag [flag] 'some flag'"), 24 | Arg::from_usage("--color [color] 'some other flag'") 25 | ]) 26 | .get_matches_from(vec!["", "--flag", "some", "--color", "other"]); 27 | assert!(m.is_present("flag")); 28 | assert_eq!(m.value_of("flag").unwrap(), "some"); 29 | assert!(m.is_present("color")); 30 | assert_eq!(m.value_of("color").unwrap(), "other"); 31 | } 32 | 33 | #[test] 34 | fn opts_using_long_equals() { 35 | let m = App::new("opts") 36 | .args(vec![ 37 | Arg::from_usage("--flag [flag] 'some flag'"), 38 | Arg::from_usage("--color [color] 'some other flag'") 39 | ]) 40 | .get_matches_from(vec!["", "--flag=some", "--color=other"]); 41 | assert!(m.is_present("flag")); 42 | assert_eq!(m.value_of("flag").unwrap(), "some"); 43 | assert!(m.is_present("color")); 44 | assert_eq!(m.value_of("color").unwrap(), "other"); 45 | } 46 | 47 | #[test] 48 | fn opts_using_mixed() { 49 | let m = App::new("opts") 50 | .args(vec![ 51 | Arg::from_usage("-f, --flag [flag] 'some flag'"), 52 | Arg::from_usage("-c, --color [color] 'some other flag'") 53 | ]) 54 | .get_matches_from(vec!["", "-f", "some", "--color", "other"]); 55 | assert!(m.is_present("flag")); 56 | assert_eq!(m.value_of("flag").unwrap(), "some"); 57 | assert!(m.is_present("color")); 58 | assert_eq!(m.value_of("color").unwrap(), "other"); 59 | 60 | let m = App::new("opts") 61 | .args(vec![ 62 | Arg::from_usage("-f, --flag [flag] 'some flag'"), 63 | Arg::from_usage("-c, --color [color] 'some other flag'") 64 | ]) 65 | .get_matches_from(vec!["", "--flag=some", "-c", "other"]); 66 | assert!(m.is_present("flag")); 67 | assert_eq!(m.value_of("flag").unwrap(), "some"); 68 | assert!(m.is_present("color")); 69 | assert_eq!(m.value_of("color").unwrap(), "other"); 70 | } -------------------------------------------------------------------------------- /tests/app.yml: -------------------------------------------------------------------------------- 1 | name: claptests 2 | version: 1.0 3 | about: tests clap library 4 | author: Kevin K. 5 | settings: 6 | - ArgRequiredElseHelp 7 | args: 8 | - opt: 9 | short: o 10 | long: option 11 | multiple: true 12 | help: tests options 13 | - positional: 14 | help: tests positionals 15 | index: 1 16 | - positional2: 17 | help: tests positionals with exclusions 18 | index: 2 19 | - flag: 20 | short: f 21 | long: flag 22 | multiple: true 23 | help: tests flags 24 | global: true 25 | - flag2: 26 | short: F 27 | help: tests flags with exclusions 28 | conflicts_with: 29 | - flag 30 | requires: 31 | - option2 32 | - option2: 33 | long: long-option-2 34 | help: tests long options with exclusions 35 | conflicts_with: 36 | - option 37 | requires: 38 | - positional2 39 | - option3: 40 | short: O 41 | long: Option 42 | help: tests options with specific value sets 43 | takes_value: true 44 | possible_values: 45 | - fast 46 | - slow 47 | - positional3: 48 | index: 3 49 | help: tests positionals with specific values 50 | possible_values: [ vi, emacs ] 51 | - multvals: 52 | long: multvals 53 | help: Tests mutliple values, not mult occs 54 | value_names: 55 | - one 56 | - two 57 | - multvalsmo: 58 | long: multvalsmo 59 | multiple: true 60 | help: Tests mutliple values, not mult occs 61 | value_names: [one, two] 62 | - minvals2: 63 | long: minvals2 64 | multiple: true 65 | help: Tests 2 min vals 66 | min_values: 2 67 | - maxvals3: 68 | long: maxvals3 69 | multiple: true 70 | help: Tests 3 max vals 71 | max_values: 3 72 | arg_groups: 73 | - test: 74 | args: 75 | - maxvals3 76 | - minmals2 77 | conflicts_with: 78 | - option3 79 | requires: 80 | - multvals 81 | subcommands: 82 | - subcmd: 83 | about: tests subcommands 84 | version: 0.1 85 | author: Kevin K. 86 | args: 87 | - scoption: 88 | short: o 89 | long: option 90 | multiple: true 91 | help: tests options 92 | takes_value: true 93 | - scpositional: 94 | help: tests positionals 95 | index: 1 96 | -------------------------------------------------------------------------------- /examples/04_using_matches.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | fn main() { 6 | 7 | // Once all App settings (including all arguments) have been set, you call get_matches() which 8 | // parses the string provided by the user, and returns all the valid matches to the ones you 9 | // specified. 10 | // 11 | // You can then query the matches struct to get information about how the user ran the program 12 | // at startup. 13 | // 14 | // For this example, let's assume you created an App which accepts three arguments (plus two 15 | // generated by clap), a flag to display debugging information triggered with "-d" or 16 | // "--debug" as well as an option argument which specifies a custom configuration file to use 17 | // triggered with "-c file" or "--config file" or "--config=file" and finally a positional 18 | // argument which is the input file we want to work with, this will be the only required 19 | // argument. 20 | let matches = App::new("MyApp") 21 | .about("Parses an input file to do awesome things") 22 | .version("1.0") 23 | .author("Kevin K. ") 24 | .arg(Arg::with_name("debug") 25 | .help("turn on debugging information") 26 | .short("d") 27 | .long("debug")) 28 | .arg(Arg::with_name("config") 29 | .help("sets the config file to use") 30 | .short("c") 31 | .long("config")) 32 | .arg(Arg::with_name("input") 33 | .help("the input file to use") 34 | .index(1) 35 | .required(true)) 36 | .get_matches(); 37 | 38 | // We can find out whether or not debugging was turned on 39 | if matches.is_present("debug") { 40 | println!("Debugging is turned on"); 41 | } 42 | 43 | // If we wanted to some custom initialization based off some configuration file provided 44 | // by the user, we could get the file (A string of the file) 45 | if let Some(ref file) = matches.value_of("config") { 46 | println!("Using config file: {}", file); 47 | } 48 | 49 | // Because "input" is required we can safely call unwrap() because had the user NOT 50 | // specified a value, clap would have explained the error the user, and exited. 51 | println!("Doing real work with file: {}", matches.value_of("input").unwrap() ); 52 | 53 | // Continued program logic goes here... 54 | } 55 | -------------------------------------------------------------------------------- /tests/positionals.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg, ClapErrorType}; 4 | 5 | #[test] 6 | fn positional() { 7 | let m = App::new("positional") 8 | .args(vec![ 9 | Arg::from_usage("-f, --flag 'some flag'"), 10 | Arg::with_name("positional") 11 | .index(1) 12 | ]) 13 | .get_matches_from(vec!["", "-f", "test"]); 14 | assert!(m.is_present("positional")); 15 | assert!(m.is_present("flag")); 16 | assert_eq!(m.value_of("positional").unwrap(), "test"); 17 | 18 | let m = App::new("positional") 19 | .args(vec![ 20 | Arg::from_usage("-f, --flag 'some flag'"), 21 | Arg::with_name("positional") 22 | .index(1) 23 | ]) 24 | .get_matches_from(vec!["", "test", "--flag"]); 25 | assert!(m.is_present("positional")); 26 | assert!(m.is_present("flag")); 27 | assert_eq!(m.value_of("positional").unwrap(), "test"); 28 | } 29 | 30 | #[test] 31 | fn positional_multiple() { 32 | let m = App::new("positional_multiple") 33 | .args(vec![ 34 | Arg::from_usage("-f, --flag 'some flag'"), 35 | Arg::with_name("positional") 36 | .index(1) 37 | .multiple(true) 38 | ]) 39 | .get_matches_from(vec!["", "-f", "test1", "test2", "test3"]); 40 | assert!(m.is_present("positional")); 41 | assert!(m.is_present("flag")); 42 | assert_eq!(m.values_of("positional").unwrap(), vec!["test1", "test2", "test3"]); 43 | 44 | let m = App::new("positional_multiple") 45 | .args(vec![ 46 | Arg::from_usage("-f, --flag 'some flag'"), 47 | Arg::with_name("positional") 48 | .index(1) 49 | .multiple(true) 50 | ]) 51 | .get_matches_from(vec!["", "test1", "test2", "test3", "--flag"]); 52 | assert!(m.is_present("positional")); 53 | assert!(m.is_present("flag")); 54 | assert_eq!(m.values_of("positional").unwrap(), vec!["test1", "test2", "test3"]); 55 | } 56 | 57 | #[test] 58 | fn positional_multiple_2() { 59 | let result = App::new("positional_multiple") 60 | .args(vec![ 61 | Arg::from_usage("-f, --flag 'some flag'"), 62 | Arg::with_name("positional") 63 | .index(1) 64 | ]) 65 | .get_matches_from_safe(vec!["", "-f", "test1", "test2", "test3"]); 66 | assert!(result.is_err()); 67 | let err = result.err().unwrap(); 68 | assert_eq!(err.error_type, ClapErrorType::UnexpectedArgument); 69 | } 70 | 71 | #[test] 72 | fn positional_possible_values() { 73 | let m = App::new("positional_possible_values") 74 | .args(vec![ 75 | Arg::from_usage("-f, --flag 'some flag'"), 76 | Arg::with_name("positional") 77 | .index(1) 78 | .possible_value("test123") 79 | ]) 80 | .get_matches_from(vec!["", "-f", "test123"]); 81 | assert!(m.is_present("positional")); 82 | assert!(m.is_present("flag")); 83 | assert_eq!(m.values_of("positional").unwrap(), vec!["test123"]); 84 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | Contributions are always welcome! Please use the following guidelines when contributing to `clap` 4 | 5 | 1. Fork `clap` 6 | 2. Clone your fork (`git clone https://github.com/$YOUR_USERNAME/clap-rs && cd clap-rs`) 7 | 3. Create new branch (`git checkout -b new-branch`) 8 | 4. Make your changes, and commit (`git commit -am "your message"`) 9 | * I use a [conventional](https://github.com/ajoslin/conventional-changelog/blob/a5505865ff3dd710cf757f50530e73ef0ca641da/conventions/angular.md) changelog format so I can update my changelog using [clog](https://github.com/thoughtram/clog) 10 | * In addition to the conventions defined above, I also use `imp`, `wip`, `examples`. 11 | * Format your commit subject line using the following format: `TYPE(COMPONENT): MESSAGE` where `TYPE` is one of the following: 12 | - `feat` - A new feature 13 | - `imp` - An improvement to an existing feature 14 | - `perf` - A performance improvement 15 | - `docs` - Changes to documentation only 16 | - `tests` - Changes to the testing framework or tests only 17 | - `fix` - A bug fix 18 | - `refactor` - Code functionality doesn't change, but underlying structure may 19 | - `style` - Stylistic changes only, no functionality changes 20 | - `wip` - A work in progress commit (Should typically be `git rebase`'ed away) 21 | - `chore` - Catch all or things that have to do with the build system, etc 22 | - `examples` - Changes to existing example, or a new example 23 | * The `COMPONENT` is optional, and may be a single file, directory, or logical component. Can be omitted if commit applies globally 24 | 5. Run the tests (`cargo test --features yaml && make -C clap-tests test`) 25 | 6. `git rebase` into concise commits and remove `--fixup`s (`git rebase -i HEAD~NUM` where `NUM` is number of commits back) 26 | 7. Push your changes back to your fork (`git push origin $your-branch`) 27 | 8. Create a pull request! (You can also create the pull request first, and we'll merge when ready. This a good way to discuss proposed changes.) 28 | 29 | Another really great way to help is if you find an interesting, or helpful way in which to use `clap`. You can either add it to the `examples/` directory, or file an issue and tell me. I'm all about giving credit where credit is due :) 30 | 31 | ## Goals 32 | 33 | There are a few goals of `clap` that I'd like to maintain throughout contributions. 34 | 35 | * Remain backwards compatible when possible 36 | - If backwards compatibility *must* be broken, use deprecation warnings if at all possible before removing legacy code 37 | - This does not apply for security concerns 38 | * Parse arguments quickly 39 | - Parsing of arguments shouldn't slow down usage of the main program 40 | - This is also true of generating help and usage information (although *slightly* less stringent, as the program is about to exit) 41 | * Try to be cognizant of memory usage 42 | - Once parsing is complete, the memory footprint of `clap` should be low since the main program is the star of the show 43 | * `panic!` on *developer* error, exit gracefully on *end-user* error 44 | -------------------------------------------------------------------------------- /examples/08_subcommands.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg, SubCommand}; 4 | 5 | fn main() { 6 | 7 | // SubCommands function exactly like sub-Apps, because that's exactly what they are. Each 8 | // instance of a SubCommand can have it's own version, author(s), Args, and even it's own 9 | // subcommands. 10 | // 11 | // # Help and Version 12 | // Just like Apps, each subcommand will get it's own "help" and "version" flags automatically 13 | // generated. Also, like Apps, you can override "-V" or "-h" safely and still get "--help" and 14 | // "--version" auto generated. 15 | // 16 | // NOTE: If you specify a subcommand for your App, clap will also autogenerate a "help" 17 | // subcommand along with "-h" and "--help" (applies to sub-subcommands as well). 18 | // 19 | // Just like arg() and args(), subcommands can be specified one at a time via subcommand() or 20 | // multiple ones at once with a Vec provided to subcommands(). 21 | let matches = App::new("MyApp") 22 | // Normal App and Arg configuration goes here... 23 | 24 | // In the following example assume we wanted an application which 25 | // supported an "add" subcommand, this "add" subcommand also took 26 | // one positional argument of a file to add: 27 | .subcommand(SubCommand::with_name("add") // The name we call argument with 28 | .about("Adds files to myapp") // The message displayed in "myapp -h" 29 | // or "myapp help" 30 | .version("0.1") // Subcommands can have independent version 31 | .author("Kevin K.") // And authors 32 | .arg(Arg::with_name("input") // And their own arguments 33 | .help("the file to add") 34 | .index(1) 35 | .required(true))) 36 | .get_matches(); 37 | 38 | // You can check if a subcommand was used like normal 39 | if matches.is_present("add") { 40 | println!("'myapp add' was run."); 41 | } 42 | 43 | // You can get the independent subcommand matches (which function exactly like App matches) 44 | if let Some(ref matches) = matches.subcommand_matches("add") { 45 | // Safe to use unwrap() because of the required() option 46 | println!("Adding file: {}", matches.value_of("input").unwrap()); 47 | } 48 | 49 | // You can also match on a subcommand's name 50 | match matches.subcommand_name() { 51 | Some("add") => println!("'myapp add' was used"), 52 | None => println!("No subcommand was used"), 53 | _ => println!("Some other subcommand was used"), 54 | } 55 | 56 | // Continued program logic goes here... 57 | } 58 | -------------------------------------------------------------------------------- /examples/18_builder_macro.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate clap; 2 | 3 | // No use imports from clap. #[macro_use] gives us `clap_app!` which internally uses `$crate::` 4 | 5 | fn main() { 6 | 7 | // Validation example testing that a file exists 8 | let file_exists = |path| { 9 | if std::fs::metadata(path).is_ok() { 10 | Ok(()) 11 | } else { 12 | Err(String::from("File doesn't exist")) 13 | } 14 | }; 15 | 16 | // External module may contain this subcommand. If this exists in another module, a function is 17 | // required to access it. Recommend `fn clap() -> Clap::SubCommand`. 18 | let external_sub_command = clap_app!( @subcommand foo => 19 | (@arg bar: -b "Bar") 20 | ); 21 | 22 | let matches = clap_app!(MyApp => 23 | (@setting SubcommandRequiredElseHelp) 24 | (version: "1.0") 25 | (author: "Alice") 26 | (about: "Does awesome things") 27 | (@arg config: -c --config #{1, 2} {file_exists} "Sets a custom config file") 28 | (@arg input: * "Input file") 29 | (@group test => 30 | (@attributes +required) 31 | (@arg output: "Sets an optional output file") 32 | (@arg debug: -d ... "Turn debugging information on") 33 | ) 34 | (subcommand: external_sub_command) 35 | (@subcommand test => 36 | (about: "does testing things") 37 | (version: "2.5") 38 | (@arg list: -l "Lists test values") 39 | (@arg test_req: -r requires[list] "Tests requirement for listing") 40 | (@arg aaaa: --aaaa +takes_value { 41 | |a| if a.contains("a") { 42 | Ok(()) 43 | } else { 44 | Err(String::from("string does not contain at least one a")) 45 | } 46 | } "Test if the argument contains an a") 47 | ) 48 | ).get_matches(); 49 | 50 | // You can check the value provided by positional arguments, or option arguments 51 | if let Some(o) = matches.value_of("output") { 52 | println!("Value for output: {}", o); 53 | } 54 | 55 | if let Some(c) = matches.value_of("config") { 56 | println!("Value for config: {}", c); 57 | } 58 | 59 | // You can see how many times a particular flag or argument occurred 60 | // Note, only flags can have multiple occurrences 61 | match matches.occurrences_of("debug") { 62 | 0 => println!("Debug mode is off"), 63 | 1 => println!("Debug mode is kind of on"), 64 | 2 => println!("Debug mode is on"), 65 | 3 | _ => println!("Don't be crazy"), 66 | } 67 | 68 | // You can check for the existence of subcommands, and if found use their 69 | // matches just as you would the top level app 70 | if let Some(ref matches) = matches.subcommand_matches("test") { 71 | // "$ myapp test" was run 72 | if matches.is_present("list") { 73 | // "$ myapp test -l" was run 74 | println!("Printing testing lists..."); 75 | } else { 76 | println!("Not printing testing lists..."); 77 | } 78 | } 79 | 80 | 81 | // Continued program logic goes here... 82 | } 83 | -------------------------------------------------------------------------------- /examples/05_flag_args.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | fn main() { 6 | 7 | // Of the three argument types, flags are the most simple. Flags are simple switches which can 8 | // be either "on" or "off" 9 | // 10 | // clap also supports multiple occurrences of flags, the common example is "verbosity" where a 11 | // user could want a little information with "-v" or tons of information with "-v -v" or "-vv" 12 | let matches = App::new("MyApp") 13 | // Regular App configuration goes here... 14 | 15 | // We'll add a flag that represents an awesome meter... 16 | // 17 | // I'll explain each possible setting that "flags" accept. Keep in mind 18 | // that you DO NOT need to set each of these for every flag, only the ones 19 | // you want for your individual case. 20 | .arg(Arg::with_name("awesome") 21 | .help("turns up the awesome") // Displayed when showing help info 22 | .short("a") // Trigger this arg with "-a" 23 | .long("awesome") // Trigger this arg with "--awesome" 24 | .multiple(true) // This flag should allow multiple 25 | // occurrences such as "-aaa" or "-a -a" 26 | .requires("config") // Says, "If the user uses -a, they MUST 27 | // also use this other 'config' arg too" 28 | // Can also specifiy a list using 29 | // requires_all(Vec<&str>) 30 | .conflicts_with("output") // Opposite of requires(), says "if the 31 | // user uses -a, they CANNOT use 'output'" 32 | // also has a mutually_excludes_all(Vec<&str>) 33 | ) 34 | // NOTE: In order to compile this example, comment out requres() and 35 | // mutually_excludes() because we have not defined an "output" or "config" 36 | // argument. 37 | .get_matches(); 38 | 39 | // We can find out whether or not awesome was used 40 | if matches.is_present("awesome") { 41 | println!("Awesomeness is turned on"); 42 | } 43 | 44 | // If we set the mutliple() option of a flag we can check how many times the user specified 45 | // 46 | // Note: if we did not specify the multiple() option, and the user used "awesome" we would get 47 | // a 1 (no matter how many times they actually used it), or a 0 if they didn't use it at all 48 | match matches.occurrences_of("awesome") { 49 | 0 => println!("Nothing is awesome"), 50 | 1 => println!("Some things are awesome"), 51 | 2 => println!("Lots of things are awesome"), 52 | 3 | _ => println!("EVERYTHING is awesome!"), 53 | } 54 | 55 | // Continued program logic goes here... 56 | } 57 | -------------------------------------------------------------------------------- /src/args/argbuilder/positional.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter, Result}; 2 | use std::result::Result as StdResult; 3 | use std::rc::Rc; 4 | 5 | pub struct PosBuilder<'n> { 6 | pub name: &'n str, 7 | /// The string of text that will displayed to the user when the application's 8 | /// `help` text is displayed 9 | pub help: Option<&'n str>, 10 | /// If this is a required by default when using the command line program 11 | /// i.e. a configuration file that's required for the program to function 12 | /// **NOTE:** required by default means, it is required *until* mutually 13 | /// exclusive arguments are evaluated. 14 | pub required: bool, 15 | /// Allow multiple occurrences of an option argument such as "-c some -c other" 16 | pub multiple: bool, 17 | /// A list of names of other arguments that are *required* to be used when 18 | /// this flag is used 19 | pub requires: Option>, 20 | /// A list of names for other arguments that *may not* be used with this flag 21 | pub blacklist: Option>, 22 | /// A list of possible values for this argument 23 | pub possible_vals: Option>, 24 | /// The index of the argument 25 | pub index: u8, 26 | pub num_vals: Option, 27 | pub max_vals: Option, 28 | pub min_vals: Option, 29 | pub empty_vals: bool, 30 | pub global: bool, 31 | pub validator: Option StdResult<(), String>>>, 32 | /// A list of names for other arguments that *mutually override* this flag 33 | pub overrides: Option>, 34 | pub hidden: bool 35 | } 36 | 37 | impl<'n> Display for PosBuilder<'n> { 38 | fn fmt(&self, 39 | f: &mut Formatter) 40 | -> Result { 41 | if self.required { 42 | try!(write!(f, "<{}>", self.name)); 43 | } else { 44 | try!(write!(f, "[{}]", self.name)); 45 | } 46 | if self.multiple { 47 | try!(write!(f, "...")); 48 | } 49 | 50 | Ok(()) 51 | } 52 | } 53 | #[cfg(test)] 54 | mod test { 55 | use super::PosBuilder; 56 | 57 | #[test] 58 | fn posbuilder_display() { 59 | let p = PosBuilder { 60 | name: "pos", 61 | help: None, 62 | multiple: true, 63 | blacklist: None, 64 | required: false, 65 | possible_vals: None, 66 | requires: None, 67 | num_vals: None, 68 | min_vals: None, 69 | max_vals: None, 70 | index: 1, 71 | empty_vals: true, 72 | global: false, 73 | validator: None, 74 | overrides: None, 75 | hidden: false, 76 | }; 77 | 78 | assert_eq!(&*format!("{}", p), "[pos]..."); 79 | 80 | let p2 = PosBuilder { 81 | name: "pos", 82 | help: None, 83 | multiple: false, 84 | blacklist: None, 85 | required: true, 86 | possible_vals: None, 87 | requires: None, 88 | num_vals: None, 89 | min_vals: None, 90 | max_vals: None, 91 | index: 1, 92 | empty_vals: true, 93 | global: false, 94 | validator: None, 95 | overrides: None, 96 | hidden: false, 97 | }; 98 | 99 | assert_eq!(&*format!("{}", p2), ""); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /examples/17_yaml.yml: -------------------------------------------------------------------------------- 1 | name: yml_app 2 | version: 1.0 3 | about: an example using a .yml file to build a CLI 4 | author: Kevin K. 5 | 6 | # AppSettings can be defined as a list and are **not** ascii case sensitive 7 | settings: 8 | - ArgRequiredElseHelp 9 | 10 | # All Args must be defined in the 'args:' list where the name of the arg, is the 11 | # key to a Hash object 12 | args: 13 | # The name of this argument, is 'opt' which will be used to access the value 14 | # later in your Rust code 15 | - opt: 16 | help: example option argument from yaml 17 | short: o 18 | long: option 19 | multiple: true 20 | takes_value: true 21 | - pos: 22 | help: example positional argument from yaml 23 | index: 1 24 | # A list of possible values can be defined as a list 25 | possible_values: 26 | - fast 27 | - slow 28 | - flag: 29 | help: demo flag argument 30 | short: F 31 | multiple: true 32 | global: true 33 | # Conflicts, mutual overrides, and requirements can all be defined as a 34 | # list, where the key is the name of the other argument 35 | conflicts_with: 36 | - opt 37 | requires: 38 | - pos 39 | - mode: 40 | long: mode 41 | help: shows an option with specific values 42 | # possible_values can also be defined in this list format 43 | possible_values: [ vi, emacs ] 44 | takes_value: true 45 | - mvals: 46 | long: mult-vals 47 | help: demos an option which has two named values 48 | # value names can be described in a list, where the help will be shown 49 | # --mult-vals 50 | value_names: 51 | - one 52 | - two 53 | - minvals: 54 | long: min-vals 55 | multiple: true 56 | help: you must supply at least two values to satisfy me 57 | min_values: 2 58 | - maxvals: 59 | long: max-vals 60 | multiple: true 61 | help: you can only supply a max of 3 values for me! 62 | max_values: 3 63 | 64 | # All subcommands must be listed in the 'subcommand:' object, where the key to 65 | # the list is the name of the subcommand, and all settings for that command are 66 | # are part of a Hash object 67 | subcommands: 68 | # The nae of this subcommand will be 'subcmd' which can be accessed in your 69 | # Rust code later 70 | - subcmd: 71 | about: demos subcommands from yaml 72 | version: 0.1 73 | author: Kevin K. 74 | # Subcommand args are exactly like App args 75 | args: 76 | - scopt: 77 | short: B 78 | multiple: true 79 | help: example subcommand option 80 | takes_value: true 81 | - scpos1: 82 | help: example subcommand positional 83 | index: 1 84 | 85 | # ArgGroups are supported as well, and must be sepcified in the 'arg_groups:' 86 | # object of this file 87 | arg_groups: 88 | # the name of the ArgGoup is specified here 89 | - min-max-vals: 90 | # All args and groups that are a part of this group are set here 91 | args: 92 | - minvals 93 | - maxvals 94 | # setting conflicts is done the same manner as setting 'args:' 95 | # 96 | # to make this group required, you could set 'required: true' but for 97 | # this example we won't do that. 98 | -------------------------------------------------------------------------------- /examples/06_positional_args.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | fn main() { 6 | 7 | // Positional arguments are those values after the program name which are not preceded by any 8 | // identifier (such as "myapp some_file"). Positionals support many of the same options as 9 | // flags, as well as a few additional ones. 10 | let matches = App::new("MyApp") 11 | // Regular App configuration goes here... 12 | 13 | // We'll add two positional arguments, a input file, and a config file. 14 | // 15 | // I'll explain each possible setting that "positionals" accept. Keep in 16 | // mind that you DO NOT need to set each of these for every flag, only the 17 | // ones that apply to your individual case. 18 | .arg(Arg::with_name("input") 19 | .help("the input file to use") // Displayed when showing help info 20 | .index(1) // Set the order in which the user must 21 | // specify this argument (Starts at 1) 22 | .requires("config") // Says, "If the user uses "input", they MUST 23 | // also use this other 'config' arg too" 24 | // Can also specifiy a list using 25 | // requires_all(Vec<&str>) 26 | .conflicts_with("output") // Opposite of requires(), says "if the 27 | // user uses -a, they CANNOT use 'output'" 28 | // also has a mutually_excludes_all(Vec<&str>) 29 | .required(true) // By default this argument MUST be present 30 | // NOTE: mutual exclusions take precedence over 31 | // required arguments 32 | ) 33 | .arg(Arg::with_name("config") 34 | .help("the config file to use") 35 | .index(2)) // Note, we do not need to specify required(true) 36 | // if we don't want to, because "input" already 37 | // requires "config" 38 | // Note, we also do not need to specify requires("input") 39 | // because requires lists are automatically two-way 40 | 41 | // NOTE: In order to compile this example, comment out mutually_excludes() 42 | // because we have not defined an "output" argument. 43 | .get_matches(); 44 | 45 | // We can find out whether or not "input" or "config" were used 46 | if matches.is_present("input") { 47 | println!("An input file was specified"); 48 | } 49 | 50 | // We can also get the values for those arguments 51 | if let Some(ref in_file) = matches.value_of("input") { 52 | // It's safe to call unwrap() because of the required options we set above 53 | println!("Doing work with {} and {}", in_file, matches.value_of("config").unwrap()); 54 | } 55 | // Continued program logic goes here... 56 | } 57 | -------------------------------------------------------------------------------- /examples/01c_quick_example.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate clap; 3 | 4 | fn main() { 5 | // This example shows how to create an application with several arguments using macro builder. 6 | // It combines the simplicity of the from_usage methods and the performance of the Builder Pattern. 7 | // 8 | // The example below is functionally identical to the one in 01a_quick_example.rs and 01b_quick_example.rs 9 | // 10 | // Create an application with 5 possible arguments (2 auto generated) and 2 subcommands (1 auto generated) 11 | // - A config file 12 | // + Uses "-c filename" or "--config filename" 13 | // - An output file 14 | // + A positional argument (i.e. "$ myapp output_filename") 15 | // - A debug flag 16 | // + Uses "-d" or "--debug" 17 | // + Allows multiple occurrences of such as "-dd" (for vary levels of debugging, as an example) 18 | // - A help flag (automatically generated by clap) 19 | // + Uses "-h" or "--help" (Only autogenerated if you do NOT specify your own "-h" or "--help") 20 | // - A version flag (automatically generated by clap) 21 | // + Uses "-V" or "--version" (Only autogenerated if you do NOT specify your own "-V" or "--version") 22 | // - A subcommand "test" (subcommands behave like their own apps, with their own arguments 23 | // + Used by "$ myapp test" with the following arguments 24 | // > A list flag 25 | // = Uses "-l" (usage is "$ myapp test -l" 26 | // > A help flag (automatically generated by clap 27 | // = Uses "-h" or "--help" (full usage "$ myapp test -h" or "$ myapp test --help") 28 | // > A version flag (automatically generated by clap 29 | // = Uses "-V" or "--version" (full usage "$ myapp test -V" or "$ myapp test --version") 30 | // - A subcommand "help" (automatically generated by clap because we specified a subcommand of our own) 31 | // + Used by "$ myapp help" (same functionality as "-h" or "--help") 32 | let matches = clap_app!(myapp => 33 | (version: "1.0") 34 | (author: "Kevin K. ") 35 | (about: "Does awesome things") 36 | (@arg CONFIG: -c --config +takes_value "Sets a custom config file") 37 | (@arg INPUT: +required "Sets the input file to use") 38 | (@arg debug: -d ... "Sets the level of debugging information") 39 | (@subcommand test => 40 | (about: "controls testing features") 41 | (version: "1.3") 42 | (author: "Someone E. ") 43 | (@arg verbose: -v --verbose "Print test information verbosely") 44 | ) 45 | ).get_matches(); 46 | 47 | // Calling .unwrap() is safe here because "INPUT" is required (if "INPUT" wasn't 48 | // required we could have used an 'if let' to conditionally get the value) 49 | println!("Using input file: {}", matches.value_of("INPUT").unwrap()); 50 | 51 | // Gets a value for config if supplied by user, or defaults to "default.conf" 52 | let config = matches.value_of("CONFIG").unwrap_or("default.conf"); 53 | println!("Value for config: {}", config); 54 | 55 | // Vary the output based on how many times the user used the "debug" flag 56 | // (i.e. 'myapp -d -d -d' or 'myapp -ddd' vs 'myapp -d' 57 | match matches.occurrences_of("debug") { 58 | 0 => println!("Debug mode is off"), 59 | 1 => println!("Debug mode is kind of on"), 60 | 2 => println!("Debug mode is on"), 61 | 3 | _ => println!("Don't be crazy"), 62 | } 63 | 64 | // You can information about subcommands by requesting their matches by name 65 | // (as below), requesting just the name used, or both at the same time 66 | if let Some(matches) = matches.subcommand_matches("test") { 67 | if matches.is_present("verbose") { 68 | println!("Printing verbosely..."); 69 | } else { 70 | println!("Printing normally..."); 71 | } 72 | } 73 | 74 | // more porgram logic goes here... 75 | } 76 | -------------------------------------------------------------------------------- /examples/01a_quick_example.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, SubCommand}; 4 | 5 | fn main() { 6 | 7 | // This example shows how to create an application with several arguments using usage strings, which can be 8 | // far less verbose that shown in 01b_QuickExample.rs, but is more readable. The downside is you cannot set 9 | // the more advanced configuration options using this method (well...actually you can, you'll see ;) ) 10 | // 11 | // The example below is functionally identical to the 01b_quick_example.rs and 01c_quick_example.rs 12 | // 13 | // Create an application with 5 possible arguments (2 auto generated) and 2 subcommands (1 auto generated) 14 | // - A config file 15 | // + Uses "-c filename" or "--config filename" 16 | // - An output file 17 | // + A positional argument (i.e. "$ myapp output_filename") 18 | // - A debug flag 19 | // + Uses "-d" or "--debug" 20 | // + Allows multiple occurrences of such as "-dd" (for vary levels of debugging, as an example) 21 | // - A help flag (automatically generated by clap) 22 | // + Uses "-h" or "--help" (Only autogenerated if you do NOT specify your own "-h" or "--help") 23 | // - A version flag (automatically generated by clap) 24 | // + Uses "-V" or "--version" (Only autogenerated if you do NOT specify your own "-V" or "--version") 25 | // - A subcommand "test" (subcommands behave like their own apps, with their own arguments 26 | // + Used by "$ myapp test" with the following arguments 27 | // > A list flag 28 | // = Uses "-l" (usage is "$ myapp test -l" 29 | // > A help flag (automatically generated by clap 30 | // = Uses "-h" or "--help" (full usage "$ myapp test -h" or "$ myapp test --help") 31 | // > A version flag (automatically generated by clap 32 | // = Uses "-V" or "--version" (full usage "$ myapp test -V" or "$ myapp test --version") 33 | // - A subcommand "help" (automatically generated by clap because we specified a subcommand of our own) 34 | // + Used by "$ myapp help" (same functionality as "-h" or "--help") 35 | let matches = App::new("MyApp") 36 | .version("1.0") 37 | .author("Kevin K. ") 38 | .about("Does awesome things") 39 | .args_from_usage("-c --config=[conf] 'Sets a custom config file' 40 | [output] 'Sets an optional output file' 41 | [debug]... -d 'Turn debugging information on'") 42 | .subcommand(SubCommand::with_name("test") 43 | .about("does testing things") 44 | .arg_from_usage("[list] -l 'lists test values'")) 45 | .get_matches(); 46 | 47 | // You can check the value provided by positional arguments, or option arguments 48 | if let Some(o) = matches.value_of("output") { 49 | println!("Value for output: {}", o); 50 | } 51 | 52 | if let Some(c) = matches.value_of("config") { 53 | println!("Value for config: {}", c); 54 | } 55 | 56 | // You can see how many times a particular flag or argument occurred 57 | // Note, only flags can have multiple occurrences 58 | match matches.occurrences_of("debug") { 59 | 0 => println!("Debug mode is off"), 60 | 1 => println!("Debug mode is kind of on"), 61 | 2 => println!("Debug mode is on"), 62 | 3 | _ => println!("Don't be crazy"), 63 | } 64 | 65 | // You can check for the existence of subcommands, and if found use their 66 | // matches just as you would the top level app 67 | if let Some(ref matches) = matches.subcommand_matches("test") { 68 | // "$ myapp test" was run 69 | if matches.is_present("list") { 70 | // "$ myapp test -l" was run 71 | println!("Printing testing lists..."); 72 | } else { 73 | println!("Not printing testing lists..."); 74 | } 75 | } 76 | 77 | 78 | // Continued program logic goes here... 79 | } 80 | -------------------------------------------------------------------------------- /examples/07_option_args.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | fn main() { 6 | 7 | // Option arguments are those that take an additional value, such as "-c value". In clap they 8 | // support three types of specification, those with short() as "-o some", or those with long() 9 | // as "--option value" or "--option=value" 10 | // 11 | // Options also support a multiple setting, which is discussed in the example below. 12 | let matches = App::new("MyApp") 13 | // Regular App configuration goes here... 14 | 15 | // Assume we an application that accepts an input file via the "-i file" 16 | // or the "--input file" (as wel as "--input=file"). 17 | // Below every setting supported by option arguments is discussed. 18 | // NOTE: You DO NOT need to specify each setting, only those which apply 19 | // to your particular case. 20 | .arg(Arg::with_name("input") 21 | .help("the input file to use") // Displayed when showing help info 22 | .takes_value(true) // MUST be set to true in order to be an "option" argument 23 | .short("i") // This argument is triggered with "-i" 24 | .long("input") // This argument is triggered with "--input" 25 | .multiple(true) // Set to true if you wish to allow multiple occurrences 26 | // such as "-i file -i other_file -i third_file" 27 | .required(true) // By default this argument MUST be present 28 | // NOTE: mutual exclusions take precedence over 29 | // required arguments 30 | .requires("config") // Says, "If the user uses "input", they MUST 31 | // also use this other 'config' arg too" 32 | // Can also specifiy a list using 33 | // requires_all(Vec<&str>) 34 | .conflicts_with("output") // Opposite of requires(), says "if the 35 | // user uses -a, they CANNOT use 'output'" 36 | // also has a conflicts_with_all(Vec<&str>) 37 | ) 38 | // NOTE: In order to compile this example, comment out conflicts_with() 39 | // and requires() because we have not defined an "output" or "config" 40 | // argument. 41 | .get_matches(); 42 | 43 | // We can find out whether or not "input" was used 44 | if matches.is_present("input") { 45 | println!("An input file was specified"); 46 | } 47 | 48 | // We can also get the value for "input" 49 | // 50 | // NOTE: If we specified multiple(), this will only return the _FIRST_ 51 | // occurrence 52 | if let Some(ref in_file) = matches.value_of("input") { 53 | println!("An input file: {}", in_file); 54 | } 55 | 56 | // If we specified the multiple() setting we can get all the values 57 | if let Some(ref in_v) = matches.values_of("input") { 58 | for in_file in in_v.iter() { 59 | println!("An input file: {}", in_file); 60 | } 61 | } 62 | 63 | // We can see how many times the option was used with the occurrences_of() method 64 | // 65 | // NOTE: Just like with flags, if we did not specify the multiple() setting this will only 66 | // return 1 no matter how many times the argument was used (unless it wasn't used at all, in 67 | // in which case 0 is returned) 68 | println!("The \"input\" argument was used {} times", matches.occurrences_of("input")); 69 | 70 | // Continued program logic goes here... 71 | } 72 | -------------------------------------------------------------------------------- /examples/14_groups.rs: -------------------------------------------------------------------------------- 1 | /// `ArgGroup`s are a family of related arguments and way for you to say, "Any of these arguments". 2 | /// By placing arguments in a logical group, you can make easier requirement and exclusion rules 3 | /// intead of having to list each individually, or when you want a rule to apply "any but not all" 4 | /// arguments. 5 | /// 6 | /// For instance, you can make an entire ArgGroup required, this means that one (and *only* one) 7 | /// argument. from that group must be present. Using more than one argument from an ArgGroup causes 8 | /// a failure (graceful exit). 9 | /// 10 | /// You can also do things such as name an ArgGroup as a confliction or requirement, meaning any 11 | /// of the arguments that belong to that group will cause a failure if present, or must present 12 | /// respectively. 13 | /// 14 | /// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be 15 | /// present out of a given set. Imagine that you had multiple arguments, and you want one of them to 16 | /// be required, but making all of them required isn't feasible because perhaps they conflict with 17 | /// each other. For example, lets say that you were building an application where one could set a 18 | /// given version number by supplying a string with an option argument, i.e. `--set-ver v1.2.3`, you 19 | /// also wanted to support automatically using a previous version number and simply incrementing one 20 | /// of the three numbers. So you create three flags `--major`, `--minor`, and `--patch`. All of 21 | /// these arguments shouldn't be used at one time but you want to specify that *at least one* of 22 | /// them is used. For this, you can create a group. 23 | 24 | extern crate clap; 25 | 26 | use clap::{App, Arg, ArgGroup}; 27 | 28 | fn main() { 29 | // Create application like normal 30 | let matches = App::new("myapp") 31 | // Add the version arguments 32 | .args_from_usage("--set-ver [ver] 'set version manually' 33 | --major 'auto inc major' 34 | --minor 'auto inc minor' 35 | --patch 'auto inc patch'") 36 | // Create a group, make it required, and add the above arguments 37 | .arg_group(ArgGroup::with_name("vers") 38 | .required(true) 39 | .add_all(&["vers", "major", "minor", "patch"])) 40 | // Arguments can also be added to a group individually, these two arguments 41 | // are part of the "input" group which is not required 42 | .arg(Arg::from_usage("[INPUT_FILE] 'some regular input'").group("input")) 43 | .arg(Arg::from_usage("--spec-in [SPEC_IN] 'some special input argument'").group("input")) 44 | // Now let's assume we have a -c [config] argument which requires one of 45 | // (but **not** both) the "input" arguments 46 | .arg(Arg::with_name("config").short("c").takes_value(true).requires("input")) 47 | .get_matches(); 48 | 49 | // Let's assume the old version 1.2.3 50 | let mut major = 1; 51 | let mut minor = 2; 52 | let mut patch = 3; 53 | 54 | // See if --set-ver was used to set the version manually 55 | let version = if let Some(ver) = matches.value_of("ver") { 56 | format!("{}", ver) 57 | } else { 58 | // Increment the one requested (in a real program, we'd reset the lower numbers) 59 | let (maj, min, pat) = (matches.is_present("major"), 60 | matches.is_present("minor"), 61 | matches.is_present("patch")); 62 | match (maj, min, pat) { 63 | (true, _, _) => major += 1, 64 | (_, true, _) => minor += 1, 65 | (_, _, true) => patch += 1, 66 | _ => unreachable!(), 67 | }; 68 | format!("{}.{}.{}", major, minor, patch) 69 | }; 70 | 71 | println!("Version: {}", version); 72 | 73 | // Check for usage of -c 74 | if matches.is_present("config") { 75 | let input = matches.value_of("INPUT_FILE").unwrap_or(matches.value_of("SPEC_IN").unwrap()); 76 | println!("Doing work using input {} and config {}", 77 | input, 78 | matches.value_of("config").unwrap()); 79 | } 80 | 81 | 82 | } -------------------------------------------------------------------------------- /src/args/argbuilder/option.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use std::collections::BTreeSet; 3 | use std::fmt::{Display, Formatter, Result}; 4 | use std::result::Result as StdResult; 5 | 6 | pub struct OptBuilder<'n> { 7 | pub name: &'n str, 8 | /// The short version (i.e. single character) of the argument, no preceding `-` 9 | pub short: Option, 10 | /// The long version of the flag (i.e. word) without the preceding `--` 11 | pub long: Option<&'n str>, 12 | /// The string of text that will displayed to the user when the application's 13 | /// `help` text is displayed 14 | pub help: Option<&'n str>, 15 | /// Allow multiple occurrences of an option argument such as "-c some -c other" 16 | pub multiple: bool, 17 | /// A list of names for other arguments that *may not* be used with this flag 18 | pub blacklist: Option>, 19 | /// If this is a required by default when using the command line program 20 | /// i.e. a configuration file that's required for the program to function 21 | /// **NOTE:** required by default means, it is required *until* mutually 22 | /// exclusive arguments are evaluated. 23 | pub required: bool, 24 | /// A list of possible values for this argument 25 | pub possible_vals: Option>, 26 | /// A list of names of other arguments that are *required* to be used when 27 | /// this flag is used 28 | pub requires: Option>, 29 | pub num_vals: Option, 30 | pub min_vals: Option, 31 | pub max_vals: Option, 32 | pub val_names: Option>, 33 | pub empty_vals: bool, 34 | pub global: bool, 35 | pub validator: Option StdResult<(), String>>>, 36 | /// A list of names for other arguments that *mutually override* this flag 37 | pub overrides: Option>, 38 | pub hidden: bool 39 | } 40 | 41 | impl<'n> Display for OptBuilder<'n> { 42 | fn fmt(&self, 43 | f: &mut Formatter) 44 | -> Result { 45 | // Write the name such --long or -l 46 | if let Some(l) = self.long { 47 | try!(write!(f, "--{}", l)); 48 | } else { 49 | try!(write!(f, "-{}", self.short.unwrap())); 50 | } 51 | 52 | // Write the values such as 53 | if let Some(ref vec) = self.val_names { 54 | for n in vec.iter() { 55 | try!(write!(f, " <{}>", n)); 56 | } 57 | } else { 58 | let num = self.num_vals.unwrap_or(1); 59 | for _ in (0..num) { 60 | try!(write!(f, " <{}>", self.name)); 61 | } 62 | if self.multiple && num == 1 { 63 | try!(write!(f, "...")); 64 | } 65 | } 66 | 67 | Ok(()) 68 | } 69 | } 70 | 71 | #[cfg(test)] 72 | mod test { 73 | use super::OptBuilder; 74 | use std::collections::BTreeSet; 75 | 76 | #[test] 77 | fn optbuilder_display() { 78 | let o = OptBuilder { 79 | name: "opt", 80 | short: None, 81 | long: Some("option"), 82 | help: None, 83 | multiple: true, 84 | blacklist: None, 85 | required: false, 86 | possible_vals: None, 87 | requires: None, 88 | num_vals: None, 89 | min_vals: None, 90 | max_vals: None, 91 | val_names: None, 92 | empty_vals: true, 93 | global: false, 94 | validator: None, 95 | overrides: None, 96 | hidden: false, 97 | }; 98 | 99 | assert_eq!(&*format!("{}", o), "--option ..."); 100 | 101 | let mut v_names = BTreeSet::new(); 102 | v_names.insert("file"); 103 | v_names.insert("name"); 104 | 105 | let o2 = OptBuilder { 106 | name: "opt", 107 | short: Some('o'), 108 | long: None, 109 | help: None, 110 | multiple: false, 111 | blacklist: None, 112 | required: false, 113 | possible_vals: None, 114 | requires: None, 115 | num_vals: None, 116 | min_vals: None, 117 | max_vals: None, 118 | val_names: Some(v_names), 119 | empty_vals: true, 120 | global: false, 121 | validator: None, 122 | overrides: None, 123 | hidden: false, 124 | }; 125 | 126 | assert_eq!(&*format!("{}", o2), "-o "); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /examples/01b_quick_example.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg, SubCommand}; 4 | 5 | fn main() { 6 | 7 | // This method shows the traditional, and slightly more configurable way to set up arguments. This method is 8 | // more verbose, but allows setting more configuration options, and even supports easier dynamic generation. 9 | // 10 | // The example below is functionally identical to the 01a_quick_example.rs and 01c_quick_example.rs 11 | // 12 | // *NOTE:* You can actually achieve the best of both worlds by using Arg::from_usage() (instead of Arg::with_name()) 13 | // and *then* setting any additional properties. 14 | // 15 | // Create an application with 5 possible arguments (2 auto generated) and 2 subcommands (1 auto generated) 16 | // - A config file 17 | // + Uses "-c filename" or "--config filename" 18 | // - An output file 19 | // + A positional argument (i.e. "$ myapp output_filename") 20 | // - A debug flag 21 | // + Uses "-d" or "--debug" 22 | // + Allows multiple occurrences of such as "-dd" (for vary levels of debugging, as an example) 23 | // - A help flag (automatically generated by clap) 24 | // + Uses "-h" or "--help" (Only autogenerated if you do NOT specify your own "-h" or "--help") 25 | // - A version flag (automatically generated by clap) 26 | // + Uses "-V" or "--version" (Only autogenerated if you do NOT specify your own "-V" or "--version") 27 | // - A subcommand "test" (subcommands behave like their own apps, with their own arguments 28 | // + Used by "$ myapp test" with the following arguments 29 | // > A list flag 30 | // = Uses "-l" (usage is "$ myapp test -l" 31 | // > A help flag (automatically generated by clap 32 | // = Uses "-h" or "--help" (full usage "$ myapp test -h" or "$ myapp test --help") 33 | // > A version flag (automatically generated by clap 34 | // = Uses "-V" or "--version" (full usage "$ myapp test -V" or "$ myapp test --version") 35 | // - A subcommand "help" (automatically generated by clap because we specified a subcommand of our own) 36 | // + Used by "$ myapp help" (same functionality as "-h" or "--help") 37 | let matches = App::new("MyApp") 38 | .version("1.0") 39 | .author("Kevin K. ") 40 | .about("Does awesome things") 41 | .arg(Arg::with_name("config") 42 | .short("c") 43 | .long("config") 44 | .help("Sets a custom config file") 45 | .takes_value(true)) 46 | .arg(Arg::with_name("output") 47 | .help("Sets an optional output file") 48 | .index(1)) 49 | .arg(Arg::with_name("debug") 50 | .short("d") 51 | .multiple(true) 52 | .help("Turn debugging information on")) 53 | .subcommand(SubCommand::with_name("test") 54 | .about("does testing things") 55 | .arg(Arg::with_name("list") 56 | .short("l") 57 | .help("lists test values"))) 58 | .get_matches(); 59 | 60 | // You can check the value provided by positional arguments, or option arguments 61 | if let Some(o) = matches.value_of("output") { 62 | println!("Value for output: {}", o); 63 | } 64 | 65 | if let Some(c) = matches.value_of("config") { 66 | println!("Value for config: {}", c); 67 | } 68 | 69 | // You can see how many times a particular flag or argument occurred 70 | // Note, only flags can have multiple occurrences 71 | match matches.occurrences_of("debug") { 72 | 0 => println!("Debug mode is off"), 73 | 1 => println!("Debug mode is kind of on"), 74 | 2 => println!("Debug mode is on"), 75 | 3 | _ => println!("Don't be crazy"), 76 | } 77 | 78 | // You can check for the existence of subcommands, and if found use their 79 | // matches just as you would the top level app 80 | if let Some(ref matches) = matches.subcommand_matches("test") { 81 | // "$ myapp test" was run 82 | if matches.is_present("list") { 83 | // "$ myapp test -l" was run 84 | println!("Printing testing lists..."); 85 | } else { 86 | println!("Not printing testing lists..."); 87 | } 88 | } 89 | 90 | 91 | // Continued program logic goes here... 92 | } 93 | -------------------------------------------------------------------------------- /examples/03_args.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg}; 4 | 5 | fn main() { 6 | // Args describe a possible valid argument which may be supplied by the user at runtime. There 7 | // are three different types of arguments (flags, options, and positional) as well as a fourth 8 | // special type of argument, called SubCommands (which will be discussed separately). 9 | // 10 | // Args are described in the same manner as Apps using the "builder pattern" with multiple 11 | // methods describing various settings for the individual arguments. Or by supplying a "usage" 12 | // string. Both methods have their pros and cons. 13 | // 14 | // Arguments can be added to applications in two manners, one at a time with the arg(), and 15 | // arg_from_usage() method, or multiple arguments at once via a Vec inside the args() method, 16 | // or a single &str describing multiple Args (one per line) supplied to args_from_usage(). 17 | // 18 | // There are various options which can be set for a given argument, some apply to any of the 19 | // three types of arguments, some only apply one or two of the types. *NOTE* if you set 20 | // incompatible options on a single argument, clap will panic! at runtime. This is by design, 21 | // so that you know right away an error was made by the developer, not the end user. 22 | // 23 | // # Help and Version 24 | // clap automatically generates a help and version flag for you, unless you specificy your 25 | // own. By default help uses "-h" and "--help", and version uses "-V" and "--version". You can 26 | // safely overide "-V" and "-h" to your own arguments, and "--help" and "--version" will stil 27 | // be automatically generated for you. 28 | let matches = App::new("MyApp") 29 | // All application settings go here... 30 | 31 | // A simple "Flag" argument example (i.e. "-d") using the builder pattern 32 | .arg(Arg::with_name("debug") 33 | .help("turn on debugging information") 34 | .short("d")) 35 | 36 | // Two arguments, one "Option" argument (i.e. one that takes a value) such 37 | // as "-c some", and one positional argument (i.e. "myapp some_file") 38 | .args( vec![ 39 | Arg::with_name("config") 40 | .help("sets the config file to use") 41 | .takes_value(true) 42 | .short("c") 43 | .long("config"), 44 | Arg::with_name("input") 45 | .help("the input file to use") 46 | .index(1) 47 | .required(true) 48 | ]) 49 | 50 | // *Note* the following two examples are convienience methods, if you wish 51 | // to still get the full configurability of Arg::with_name() and the readability 52 | // of arg_from_usage(), you can instantiate a new Arg with Arg::from_usage() and 53 | // still be able to set all the additional properties, just like Arg::with_name() 54 | // 55 | // 56 | // One "Flag" using a usage string 57 | .arg_from_usage("--license 'display the license file'") 58 | 59 | // Two args, one "Positional", and one "Option" using a usage string 60 | .args_from_usage("[output] 'Supply an output file to use' 61 | -i --int=[interface] 'Set an interface to use'") 62 | .get_matches(); 63 | 64 | // Here are some examples of using the arguments defined above. Keep in mind that this is only 65 | // an example, and may be somewhat contrived 66 | // 67 | // First we check if debugging should be on or not 68 | println!("Debugging mode is: {}", if matches.is_present("debug") { "ON" } else { "OFF" }); 69 | 70 | // Next we print the config file we're using, if any was defined with either -c or 71 | // --config 72 | if let Some(config) = matches.value_of("config") { 73 | println!("A config file was passed in: {}", config); 74 | } 75 | 76 | // Let's print the file the user passed in. We can use .unwrap() here becase the arg is 77 | // required, and parsing would have failed if the user forgot it 78 | println!("Using input file: {}", matches.value_of("input").unwrap()); 79 | 80 | // We could continue checking for and using arguments in this manner, such as "license", 81 | // "output", and "interface". Keep in mind that "output" and "interface" are optional, so you 82 | // shouldn't call .unwrap(), instead prefer using an 'if let' expression as we did with 83 | // "config" 84 | } 85 | -------------------------------------------------------------------------------- /src/usageparser.rs: -------------------------------------------------------------------------------- 1 | use std::str::Chars; 2 | 3 | pub enum UsageToken<'u> { 4 | Name(&'u str, Option), 5 | Short(char), 6 | Long(&'u str), 7 | Help(&'u str), 8 | Multiple, 9 | } 10 | 11 | pub struct UsageParser<'u> { 12 | usage: &'u str, 13 | chars: Chars<'u>, 14 | s: usize, 15 | e: usize, 16 | } 17 | 18 | impl<'u> UsageParser<'u> { 19 | pub fn with_usage(u: &'u str) -> UsageParser<'u> { 20 | UsageParser { 21 | usage: u, 22 | chars: u.chars(), 23 | s: 0, 24 | e: 0, 25 | } 26 | } 27 | 28 | 29 | } 30 | 31 | impl<'u> Iterator for UsageParser<'u> { 32 | type Item = UsageToken<'u>; 33 | 34 | fn next(&mut self) -> Option> { 35 | loop { 36 | match self.chars.next() { 37 | Some(c) if c == '[' || c == '<' => { 38 | // self.s = self.e + 1; 39 | if self.e != 0 { 40 | self.e += 1; 41 | } 42 | self.s = self.e + 1; 43 | let closing = match c { 44 | '[' => ']', 45 | '<' => '>', 46 | _ => unreachable!(), 47 | }; 48 | while let Some(c) = self.chars.next() { 49 | self.e += 1; 50 | if c == closing { 51 | break 52 | } 53 | } 54 | if self.e > self.usage.len() { 55 | return None 56 | } 57 | 58 | let name = &self.usage[self.s..self.e]; 59 | 60 | return Some(UsageToken::Name(name, if c == '<' { Some(true) } else { None })); 61 | } 62 | Some('\'') => { 63 | self.s = self.e + 2; 64 | self.e = self.usage.len() - 1; 65 | 66 | while let Some(_) = self.chars.next() { 67 | continue 68 | } 69 | 70 | return Some(UsageToken::Help(&self.usage[self.s..self.e])); 71 | } 72 | Some('-') => { 73 | self.e += 1; 74 | match self.chars.next() { 75 | Some('-') => { 76 | if self.e != 1 { 77 | self.e += 1; 78 | } 79 | 80 | self.s = self.e + 1; 81 | 82 | while let Some(c) = self.chars.next() { 83 | self.e += 1; 84 | if c == ' ' || c == '=' || c == '.' { 85 | break 86 | } 87 | } 88 | if self.e > self.usage.len() { 89 | return None 90 | } 91 | 92 | if self.e == self.usage.len() - 1 { 93 | return Some(UsageToken::Long(&self.usage[self.s..])) 94 | } 95 | return Some(UsageToken::Long(&self.usage[self.s..self.e])) 96 | } 97 | Some(c) => { 98 | // When short is first don't increment e 99 | if self.e != 1 { 100 | self.e += 1; 101 | } 102 | // Short 103 | if !c.is_alphanumeric() { 104 | return None 105 | } 106 | return Some(UsageToken::Short(c)) 107 | } 108 | _ => { 109 | return None 110 | } 111 | } 112 | } 113 | Some('.') => { 114 | self.e += 1; 115 | let mut mult = false; 116 | for _ in 0..2 { 117 | self.e += 1; 118 | match self.chars.next() { 119 | // longs consume one '.' so they match '.. ' whereas shorts can 120 | // match '...' 121 | Some('.') | Some(' ') => { 122 | mult = true; 123 | } 124 | _ => { 125 | // if there is no help or following space all we can match is '..' 126 | if self.e == self.usage.len() - 1 { 127 | mult = true; 128 | } 129 | break; 130 | } 131 | } 132 | } 133 | if mult { 134 | return Some(UsageToken::Multiple) 135 | } 136 | } 137 | Some(' ') | Some('=') | Some(']') | Some('>') | Some('\t') | Some(',') => { 138 | self.e += 1; 139 | continue 140 | } 141 | None => { 142 | return None 143 | } 144 | Some(c) => panic!("Usage parser error, unexpected \ 145 | \"{}\" at \"{}\", check from_usage call", c, self.usage), 146 | } 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /tests/require.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg, ClapErrorType, ArgGroup}; 4 | 5 | #[test] 6 | fn flag_required() { 7 | let result = App::new("flag_required") 8 | .arg(Arg::from_usage("-f, --flag 'some flag'") 9 | .requires("color")) 10 | .arg(Arg::from_usage("-c, --color 'third flag'")) 11 | .get_matches_from_safe(vec!["", "-f"]); 12 | assert!(result.is_err()); 13 | let err = result.err().unwrap(); 14 | assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument); 15 | } 16 | 17 | #[test] 18 | fn flag_required_2() { 19 | let m = App::new("flag_required") 20 | .arg(Arg::from_usage("-f, --flag 'some flag'") 21 | .requires("color")) 22 | .arg(Arg::from_usage("-c, --color 'third flag'")) 23 | .get_matches_from(vec!["", "-f", "-c"]); 24 | assert!(m.is_present("color")); 25 | assert!(m.is_present("flag")); 26 | } 27 | 28 | #[test] 29 | fn option_required() { 30 | let result = App::new("option_required") 31 | .arg(Arg::from_usage("-f [flag] 'some flag'") 32 | .requires("color")) 33 | .arg(Arg::from_usage("-c [color] 'third flag'")) 34 | .get_matches_from_safe(vec!["", "-f", "val"]); 35 | assert!(result.is_err()); 36 | let err = result.err().unwrap(); 37 | assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument); 38 | } 39 | 40 | #[test] 41 | fn option_required_2() { 42 | let m = App::new("option_required") 43 | .arg(Arg::from_usage("-f [flag] 'some flag'") 44 | .requires("color")) 45 | .arg(Arg::from_usage("-c [color] 'third flag'")) 46 | .get_matches_from(vec!["", "-f", "val", "-c", "other_val"]); 47 | assert!(m.is_present("color")); 48 | assert_eq!(m.value_of("color").unwrap(), "other_val"); 49 | assert!(m.is_present("flag")); 50 | assert_eq!(m.value_of("flag").unwrap(), "val"); 51 | } 52 | 53 | #[test] 54 | fn positional_required() { 55 | let result = App::new("positional_required") 56 | .arg(Arg::with_name("flag") 57 | .index(1) 58 | .required(true)) 59 | .get_matches_from_safe(vec![""]); 60 | assert!(result.is_err()); 61 | let err = result.err().unwrap(); 62 | assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument); 63 | } 64 | 65 | #[test] 66 | fn positional_required_2() { 67 | let m = App::new("positional_required") 68 | .arg(Arg::with_name("flag") 69 | .index(1) 70 | .required(true)) 71 | .get_matches_from(vec!["", "someval"]); 72 | assert!(m.is_present("flag")); 73 | assert_eq!(m.value_of("flag").unwrap(), "someval"); 74 | } 75 | 76 | #[test] 77 | fn group_required() { 78 | let result = App::new("group_required") 79 | .arg(Arg::from_usage("-f, --flag 'some flag'")) 80 | .arg_group(ArgGroup::with_name("gr") 81 | .required(true) 82 | .add("some") 83 | .add("other")) 84 | .arg(Arg::from_usage("--some 'some arg'")) 85 | .arg(Arg::from_usage("--other 'other arg'")) 86 | .get_matches_from_safe(vec!["", "-f"]); 87 | assert!(result.is_err()); 88 | let err = result.err().unwrap(); 89 | assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument); 90 | } 91 | 92 | #[test] 93 | fn group_required_2() { 94 | let m = App::new("group_required") 95 | .arg(Arg::from_usage("-f, --flag 'some flag'")) 96 | .arg_group(ArgGroup::with_name("gr") 97 | .required(true) 98 | .add("some") 99 | .add("other")) 100 | .arg(Arg::from_usage("--some 'some arg'")) 101 | .arg(Arg::from_usage("--other 'other arg'")) 102 | .get_matches_from(vec!["", "-f", "--some"]); 103 | assert!(m.is_present("some")); 104 | assert!(!m.is_present("other")); 105 | assert!(m.is_present("flag")); 106 | } 107 | 108 | #[test] 109 | fn group_required_3() { 110 | let m = App::new("group_required") 111 | .arg(Arg::from_usage("-f, --flag 'some flag'")) 112 | .arg_group(ArgGroup::with_name("gr") 113 | .required(true) 114 | .add("some") 115 | .add("other")) 116 | .arg(Arg::from_usage("--some 'some arg'")) 117 | .arg(Arg::from_usage("--other 'other arg'")) 118 | .get_matches_from(vec!["", "-f", "--other"]); 119 | assert!(!m.is_present("some")); 120 | assert!(m.is_present("other")); 121 | assert!(m.is_present("flag")); 122 | } 123 | 124 | #[test] 125 | fn arg_require_group() { 126 | let result = App::new("arg_require_group") 127 | .arg(Arg::from_usage("-f, --flag 'some flag'") 128 | .requires("gr")) 129 | .arg_group(ArgGroup::with_name("gr") 130 | .add("some") 131 | .add("other")) 132 | .arg(Arg::from_usage("--some 'some arg'")) 133 | .arg(Arg::from_usage("--other 'other arg'")) 134 | .get_matches_from_safe(vec!["", "-f"]); 135 | assert!(result.is_err()); 136 | let err = result.err().unwrap(); 137 | assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument); 138 | } 139 | 140 | #[test] 141 | fn arg_require_group_2() { 142 | let m = App::new("arg_require_group") 143 | .arg(Arg::from_usage("-f, --flag 'some flag'") 144 | .requires("gr")) 145 | .arg_group(ArgGroup::with_name("gr") 146 | .add("some") 147 | .add("other")) 148 | .arg(Arg::from_usage("--some 'some arg'")) 149 | .arg(Arg::from_usage("--other 'other arg'")) 150 | .get_matches_from(vec!["", "-f", "--some"]); 151 | assert!(m.is_present("some")); 152 | assert!(!m.is_present("other")); 153 | assert!(m.is_present("flag")); 154 | } 155 | 156 | #[test] 157 | fn arg_require_group_3() { 158 | let m = App::new("arg_require_group") 159 | .arg(Arg::from_usage("-f, --flag 'some flag'") 160 | .requires("gr")) 161 | .arg_group(ArgGroup::with_name("gr") 162 | .add("some") 163 | .add("other")) 164 | .arg(Arg::from_usage("--some 'some arg'")) 165 | .arg(Arg::from_usage("--other 'other arg'")) 166 | .get_matches_from(vec!["", "-f", "--other"]); 167 | assert!(!m.is_present("some")); 168 | assert!(m.is_present("other")); 169 | assert!(m.is_present("flag")); 170 | } -------------------------------------------------------------------------------- /clap-tests/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate clap; 3 | 4 | use clap::{App, Arg, SubCommand}; 5 | 6 | 7 | fn main() { 8 | let m_val_names = ["one", "two"]; 9 | let args = "-o --option=[opt]... 'tests options' 10 | [positional] 'tests positionals'"; 11 | let opt3_vals = ["fast", "slow"]; 12 | let pos3_vals = ["vi", "emacs"]; 13 | let matches = App::new("claptests") 14 | // Test version from Cargo.toml 15 | .version(&crate_version!()[..]) 16 | .about("tests clap library") 17 | .author("Kevin K. ") 18 | .args_from_usage(args) 19 | .arg(Arg::from_usage("-f --flag... 'tests flags'") 20 | .global(true)) 21 | .args(vec![ 22 | Arg::from_usage("[flag2] -F 'tests flags with exclusions'").conflicts_with("flag").requires("option2"), 23 | Arg::from_usage("--long-option-2 [option2] 'tests long options with exclusions'").conflicts_with("option").requires("positional2"), 24 | Arg::from_usage("[positional2] 'tests positionals with exclusions'"), 25 | Arg::from_usage("-O --Option [option3] 'tests options with specific value sets'").possible_values(&opt3_vals), 26 | Arg::from_usage("[positional3]... 'tests positionals with specific values'").possible_values(&pos3_vals), 27 | Arg::from_usage("--multvals [multvals] 'Tests mutliple values, not mult occs'").value_names(&m_val_names), 28 | Arg::from_usage("--multvalsmo [multvalsmo]... 'Tests mutliple values, not mult occs'").value_names(&m_val_names), 29 | Arg::from_usage("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2), 30 | Arg::from_usage("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3) 31 | ]) 32 | .subcommand(SubCommand::with_name("subcmd") 33 | .about("tests subcommands") 34 | .version("0.1") 35 | .author("Kevin K. ") 36 | .arg_from_usage("-o --option [scoption]... 'tests options'") 37 | .arg_from_usage("[scpositional] 'tests positionals'")) 38 | .get_matches(); 39 | 40 | if matches.is_present("flag") { 41 | println!("flag present {} times", matches.occurrences_of("flag")); 42 | } else { 43 | println!("flag NOT present"); 44 | } 45 | 46 | if matches.is_present("opt") { 47 | if let Some(v) = matches.value_of("opt") { 48 | println!("option present {} times with value: {}",matches.occurrences_of("opt"), v); 49 | } 50 | if let Some(ref ov) = matches.values_of("opt") { 51 | for o in ov { 52 | println!("An option: {}", o); 53 | } 54 | } 55 | } else { 56 | println!("option NOT present"); 57 | } 58 | 59 | if let Some(p) = matches.value_of("positional") { 60 | println!("positional present with value: {}", p); 61 | } else { 62 | println!("positional NOT present"); 63 | } 64 | 65 | if matches.is_present("flag2") { 66 | println!("flag2 present"); 67 | println!("option2 present with value of: {}", matches.value_of("option2").unwrap()); 68 | println!("positional2 present with value of: {}", matches.value_of("positional2").unwrap()); 69 | } else { 70 | println!("flag2 NOT present"); 71 | println!("option2 maybe present with value of: {}", matches.value_of("option2").unwrap_or("Nothing")); 72 | println!("positional2 maybe present with value of: {}", matches.value_of("positional2").unwrap_or("Nothing")); 73 | } 74 | 75 | match matches.value_of("option3").unwrap_or("") { 76 | "fast" => println!("option3 present quickly"), 77 | "slow" => println!("option3 present slowly"), 78 | _ => println!("option3 NOT present") 79 | } 80 | 81 | match matches.value_of("positional3").unwrap_or("") { 82 | "vi" => println!("positional3 present in vi mode"), 83 | "emacs" => println!("positional3 present in emacs mode"), 84 | _ => println!("positional3 NOT present") 85 | } 86 | 87 | if matches.is_present("opt") { 88 | if let Some(v) = matches.value_of("opt") { 89 | println!("option present {} times with value: {}",matches.occurrences_of("opt"), v); 90 | } 91 | if let Some(ref ov) = matches.values_of("opt") { 92 | for o in ov { 93 | println!("An option: {}", o); 94 | } 95 | } 96 | } else { 97 | println!("option NOT present"); 98 | } 99 | 100 | if let Some(p) = matches.value_of("positional") { 101 | println!("positional present with value: {}", p); 102 | } else { 103 | println!("positional NOT present"); 104 | } 105 | if matches.is_present("subcmd") { 106 | println!("subcmd present"); 107 | if let Some(matches) = matches.subcommand_matches("subcmd") { 108 | if matches.is_present("flag") { 109 | println!("flag present {} times", matches.occurrences_of("flag")); 110 | } else { 111 | println!("flag NOT present"); 112 | } 113 | 114 | if matches.is_present("scoption") { 115 | if let Some(v) = matches.value_of("scoption") { 116 | println!("scoption present with value: {}", v); 117 | } 118 | if let Some(ref ov) = matches.values_of("scoption") { 119 | for o in ov { 120 | println!("An scoption: {}", o); 121 | } 122 | } 123 | } else { 124 | println!("scoption NOT present"); 125 | } 126 | 127 | if let Some(p) = matches.value_of("scpositional") { 128 | println!("scpositional present with value: {}", p); 129 | } 130 | } 131 | } else { 132 | println!("subcmd NOT present"); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/app/settings.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | use std::ascii::AsciiExt; 3 | 4 | /// Application level settings, which affect how `App` operates 5 | #[derive(PartialEq, Debug)] 6 | pub enum AppSettings { 7 | /// Allows subcommands to override all requirements of the parent (this command). For example 8 | /// if you had a subcommand or even top level application which had a required arguments that 9 | /// are only required as long as there is no subcommand present. 10 | /// 11 | /// **NOTE:** This defaults to false (using subcommand does *not* negate requirements) 12 | /// 13 | /// # Example 14 | /// 15 | /// ```no_run 16 | /// # use clap::{App, AppSettings}; 17 | /// App::new("myprog") 18 | /// .setting(AppSettings::SubcommandsNegateReqs) 19 | /// # ; 20 | /// ``` 21 | SubcommandsNegateReqs, 22 | /// Allows specifying that if no subcommand is present at runtime, error and exit gracefully 23 | /// 24 | /// **NOTE:** This defaults to false (subcommands do *not* need to be present) 25 | /// 26 | /// # Example 27 | /// 28 | /// ```no_run 29 | /// # use clap::{App, AppSettings}; 30 | /// App::new("myprog") 31 | /// .setting(AppSettings::SubcommandRequired) 32 | /// # ; 33 | /// ``` 34 | SubcommandRequired, 35 | /// Specifies that the help text sould be displayed (and then exit gracefully), if no 36 | /// arguments are present at runtime (i.e. an empty run such as, `$ myprog`. 37 | /// 38 | /// **NOTE:** Subcommands count as arguments 39 | /// 40 | /// # Example 41 | /// 42 | /// ```no_run 43 | /// # use clap::{App, AppSettings}; 44 | /// App::new("myprog") 45 | /// .setting(AppSettings::ArgRequiredElseHelp) 46 | /// # ; 47 | /// ``` 48 | ArgRequiredElseHelp, 49 | /// Uses version of the current command for all subcommands. (Defaults to false; subcommands 50 | /// have independant version strings) 51 | /// 52 | /// **NOTE:** The version for the current command and this setting must be set **prior** to 53 | /// adding any subcommands 54 | /// 55 | /// # Example 56 | /// 57 | /// ```no_run 58 | /// # use clap::{App, Arg, SubCommand, AppSettings}; 59 | /// App::new("myprog") 60 | /// .version("v1.1") 61 | /// .setting(AppSettings::GlobalVersion) 62 | /// .subcommand(SubCommand::with_name("test")) 63 | /// .get_matches(); 64 | /// // running `myprog test --version` will display 65 | /// // "myprog-test v1.1" 66 | /// ``` 67 | GlobalVersion, 68 | /// Disables `-V` and `--version` for all subcommands (Defaults to false; subcommands have 69 | /// version flags) 70 | /// 71 | /// **NOTE:** This setting must be set **prior** adding any subcommands 72 | /// 73 | /// **NOTE:** Do not set this value to false, it will have undesired results! 74 | /// 75 | /// # Example 76 | /// 77 | /// ```no_run 78 | /// # use clap::{App, Arg, SubCommand, AppSettings}; 79 | /// App::new("myprog") 80 | /// .version("v1.1") 81 | /// .setting(AppSettings::VersionlessSubcommands) 82 | /// .subcommand(SubCommand::with_name("test")) 83 | /// .get_matches(); 84 | /// // running `myprog test --version` will display unknown argument error 85 | /// ``` 86 | VersionlessSubcommands, 87 | /// By default the auto-generated help message groups flags, options, and positional arguments 88 | /// separately. This setting disable that and groups flags and options together presenting a 89 | /// more unified help message (a la getopts or docopt style). 90 | /// 91 | /// **NOTE:** This setting is cosmetic only and does not affect any functionality. 92 | /// 93 | /// # Example 94 | /// 95 | /// ```no_run 96 | /// # use clap::{App, Arg, SubCommand, AppSettings}; 97 | /// App::new("myprog") 98 | /// .setting(AppSettings::UnifiedHelpMessage) 99 | /// .get_matches(); 100 | /// // running `myprog --help` will display a unified "docopt" or "getopts" style help message 101 | /// ``` 102 | UnifiedHelpMessage, 103 | /// Will display a message "Press [ENTER]/[RETURN] to continue..." and wait user before 104 | /// exiting 105 | /// 106 | /// This is most useful when writing an application which is run from a GUI shortcut, or on 107 | /// Windows where a user tries to open the binary by double-clicking instead of using the 108 | /// command line (i.e. set `.arg_required_else_help(true)` and `.wait_on_error(true)` to 109 | /// display the help in such a case). 110 | /// 111 | /// **NOTE:** This setting is **not** recursive with subcommands, meaning if you wish this 112 | /// behavior for all subcommands, you must set this on each command (needing this is extremely 113 | /// rare) 114 | /// 115 | /// # Example 116 | /// 117 | /// ```no_run 118 | /// # use clap::{App, Arg, AppSettings}; 119 | /// App::new("myprog") 120 | /// .setting(AppSettings::WaitOnError) 121 | /// # ; 122 | /// ``` 123 | WaitOnError, 124 | /// Specifies that the help text sould be displayed (and then exit gracefully), if no 125 | /// subcommands are present at runtime (i.e. an empty run such as, `$ myprog`. 126 | /// 127 | /// **NOTE:** This should *not* be used with `.subcommand_required()` as they do the same 128 | /// thing, except one prints the help text, and one prints an error. 129 | /// 130 | /// **NOTE:** If the user specifies arguments at runtime, but no subcommand the help text will 131 | /// still be displayed and exit. If this is *not* the desired result, consider using 132 | /// `.arg_required_else_help()` 133 | /// 134 | /// # Example 135 | /// 136 | /// ```no_run 137 | /// # use clap::{App, Arg, AppSettings}; 138 | /// App::new("myprog") 139 | /// .setting(AppSettings::SubcommandRequiredElseHelp) 140 | /// # ; 141 | /// ``` 142 | SubcommandRequiredElseHelp, 143 | } 144 | 145 | impl FromStr for AppSettings { 146 | type Err = String; 147 | fn from_str(s: &str) -> Result::Err> { 148 | match &*s.to_ascii_lowercase() { 149 | "subcommandsnegatereqs" => Ok(AppSettings::SubcommandsNegateReqs), 150 | "subcommandsrequired" => Ok(AppSettings::SubcommandRequired), 151 | "argrequiredelsehelp" => Ok(AppSettings::ArgRequiredElseHelp), 152 | "globalversion" => Ok(AppSettings::GlobalVersion), 153 | "versionlesssubcommands" => Ok(AppSettings::VersionlessSubcommands), 154 | "unifiedhelpmessage" => Ok(AppSettings::UnifiedHelpMessage), 155 | "waitonerror" => Ok(AppSettings::WaitOnError), 156 | "subcommandrequiredelsehelp" => Ok(AppSettings::SubcommandRequiredElseHelp), 157 | _ => Err("unknown AppSetting, cannot convert from str".to_owned()) 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/app/errors.rs: -------------------------------------------------------------------------------- 1 | use std::process; 2 | use std::error::Error; 3 | use std::fmt; 4 | 5 | /// Command line argument parser error types 6 | #[derive(PartialEq, Debug)] 7 | pub enum ClapErrorType { 8 | /// Error occurs when some possible values were set, but clap found unexpected value 9 | /// 10 | /// 11 | /// # Example 12 | /// 13 | /// ```no_run 14 | /// # use clap::{App, Arg}; 15 | /// let result = App::new("myprog") 16 | /// .arg(Arg::with_name("debug").index(1) 17 | /// .possible_value("fast") 18 | /// .possible_value("slow")) 19 | /// .get_matches_from_safe(vec!["", "other"]); 20 | /// ``` 21 | InvalidValue, 22 | /// Error occurs when clap found unexpected flag or option 23 | /// 24 | /// 25 | /// # Example 26 | /// 27 | /// ```no_run 28 | /// # use clap::{App, Arg}; 29 | /// let result = App::new("myprog") 30 | /// .arg(Arg::from_usage("-f, --flag 'some flag'")) 31 | /// .get_matches_from_safe(vec!["", "--other"]); 32 | /// ``` 33 | InvalidArgument, 34 | /// Error occurs when clap found unexpected subcommand 35 | /// 36 | /// 37 | /// # Example 38 | /// 39 | /// ```no_run 40 | /// # use clap::{App, Arg, SubCommand}; 41 | /// let result = App::new("myprog") 42 | /// .subcommand(SubCommand::with_name("conifg") 43 | /// .about("Used for configuration") 44 | /// .arg(Arg::with_name("config_file") 45 | /// .help("The configuration file to use") 46 | /// .index(1))) 47 | /// .get_matches_from_safe(vec!["", "other"]); 48 | /// ``` 49 | InvalidSubcommand, 50 | /// Error occurs when option does not allow empty values but some was found 51 | /// 52 | /// 53 | /// # Example 54 | /// 55 | /// ```no_run 56 | /// # use clap::{App, Arg}; 57 | /// let result = App::new("myprog") 58 | /// .arg(Arg::with_name("debug") 59 | /// .empty_values(false)) 60 | /// .arg(Arg::with_name("color")) 61 | /// .get_matches_from_safe(vec!["", "--debug", "--color"]); 62 | /// ``` 63 | EmptyValue, 64 | /// Parser inner error 65 | OptionError, 66 | /// Parser inner error 67 | ArgumentError, 68 | /// Parser inner error 69 | ValueError, 70 | /// Error occurs when argument got more values then were expected 71 | /// 72 | /// 73 | /// # Example 74 | /// 75 | /// ```no_run 76 | /// # use clap::{App, Arg}; 77 | /// let result = App::new("myprog") 78 | /// .arg(Arg::with_name("debug").index(1) 79 | /// .max_values(2)) 80 | /// .get_matches_from_safe(vec!["", "too", "much", "values"]); 81 | /// ``` 82 | TooMuchValues, 83 | /// Error occurs when argument got less values then were expected 84 | /// 85 | /// 86 | /// # Example 87 | /// 88 | /// ```no_run 89 | /// # use clap::{App, Arg}; 90 | /// let result = App::new("myprog") 91 | /// .arg(Arg::with_name("debug").index(1) 92 | /// .min_values(3)) 93 | /// .get_matches_from_safe(vec!["", "too", "few"]); 94 | /// ``` 95 | TooFewValues, 96 | /// Error occurs when clap find two ore more conflicting arguments 97 | /// 98 | /// 99 | /// # Example 100 | /// 101 | /// ```no_run 102 | /// # use clap::{App, Arg}; 103 | /// let result = App::new("myprog") 104 | /// .arg(Arg::with_name("debug") 105 | /// .conflicts_with("color")) 106 | /// .get_matches_from_safe(vec!["", "--debug", "--color"]); 107 | /// ``` 108 | ArgumentConflict, 109 | /// Error occurs when one or more required arguments missing 110 | /// 111 | /// 112 | /// # Example 113 | /// 114 | /// ```no_run 115 | /// # use clap::{App, Arg}; 116 | /// let result = App::new("myprog") 117 | /// .arg(Arg::with_name("debug") 118 | /// .required(true)) 119 | /// .get_matches_from_safe(vec![""]); 120 | /// ``` 121 | MissingRequiredArgument, 122 | /// Error occurs when required subcommand missing 123 | /// 124 | /// 125 | /// # Example 126 | /// 127 | /// ```no_run 128 | /// # use clap::{App, Arg, AppSettings, SubCommand}; 129 | /// let result = App::new("myprog") 130 | /// .setting(AppSettings::SubcommandRequired) 131 | /// .subcommand(SubCommand::with_name("conifg") 132 | /// .about("Used for configuration") 133 | /// .arg(Arg::with_name("config_file") 134 | /// .help("The configuration file to use") 135 | /// .index(1))) 136 | /// .get_matches_from_safe(vec![""]); 137 | /// ``` 138 | MissingSubcommand, 139 | /// Occurs when no argument or subcommand has been supplied and 140 | /// `AppSettings::ArgRequiredElseHelp` was used 141 | /// 142 | /// 143 | /// # Example 144 | /// 145 | /// ```no_run 146 | /// # use clap::{App, Arg, AppSettings, SubCommand}; 147 | /// let result = App::new("myprog") 148 | /// .setting(AppSettings::ArgRequiredElseHelp) 149 | /// .subcommand(SubCommand::with_name("conifg") 150 | /// .about("Used for configuration") 151 | /// .arg(Arg::with_name("config_file") 152 | /// .help("The configuration file to use") 153 | /// .index(1))) 154 | /// .get_matches_from_safe(vec![""]); 155 | /// ``` 156 | MissingArgumentOrSubcommand, 157 | /// Error occurs when clap find argument while is was not expecting any 158 | /// 159 | /// 160 | /// # Example 161 | /// 162 | /// ```no_run 163 | /// # use clap::{App}; 164 | /// let result = App::new("myprog") 165 | /// .get_matches_from_safe(vec!["", "--arg"]); 166 | /// ``` 167 | UnexpectedArgument, 168 | /// Error occurs when argument was used multiple times and was not set as multiple. 169 | /// 170 | /// 171 | /// # Example 172 | /// 173 | /// ```no_run 174 | /// # use clap::{App, Arg}; 175 | /// let result = App::new("myprog") 176 | /// .arg(Arg::with_name("debug") 177 | /// .multiple(false)) 178 | /// .get_matches_from_safe(vec!["", "--debug", "--debug"]); 179 | /// ``` 180 | UnexpectedMultipleUsage, 181 | } 182 | 183 | /// Command line argument parser error 184 | #[derive(Debug)] 185 | pub struct ClapError { 186 | /// Formated error message 187 | pub error: String, 188 | /// Command line argument parser error type 189 | pub error_type: ClapErrorType, 190 | } 191 | 192 | impl ClapError { 193 | /// Prints the error to `stderr` and exits with a status of `1` 194 | pub fn exit(&self) -> ! { 195 | wlnerr!("{}", self.error); 196 | process::exit(1); 197 | } 198 | } 199 | 200 | impl Error for ClapError { 201 | fn description(&self) -> &str { 202 | &*self.error 203 | } 204 | } 205 | 206 | impl fmt::Display for ClapError { 207 | fn fmt(&self, 208 | f: &mut fmt::Formatter) 209 | -> fmt::Result { 210 | write!(f, "{}", self.error) 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /tests/posix_compatible.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | 3 | use clap::{App, Arg, ClapErrorType}; 4 | 5 | #[test] 6 | fn posix_compatible_flags_long() { 7 | let m = App::new("posix") 8 | .arg(Arg::from_usage("--flag 'some flag'").mutually_overrides_with("color")) 9 | .arg(Arg::from_usage("--color 'some other flag'")) 10 | .get_matches_from(vec!["", "--flag", "--color"]); 11 | assert!(m.is_present("color")); 12 | assert!(!m.is_present("flag")); 13 | 14 | let m = App::new("posix") 15 | .arg(Arg::from_usage("--flag 'some flag'").mutually_overrides_with("color")) 16 | .arg(Arg::from_usage("--color 'some other flag'")) 17 | .get_matches_from(vec!["", "--color", "--flag"]); 18 | assert!(!m.is_present("color")); 19 | assert!(m.is_present("flag")); 20 | } 21 | 22 | #[test] 23 | fn posix_compatible_flags_short() { 24 | let m = App::new("posix") 25 | .arg(Arg::from_usage("-f, --flag 'some flag'").mutually_overrides_with("color")) 26 | .arg(Arg::from_usage("-c, --color 'some other flag'")) 27 | .get_matches_from(vec!["", "-f", "-c"]); 28 | assert!(m.is_present("color")); 29 | assert!(!m.is_present("flag")); 30 | 31 | let m = App::new("posix") 32 | .arg(Arg::from_usage("-f, --flag 'some flag'").mutually_overrides_with("color")) 33 | .arg(Arg::from_usage("-c, --color 'some other flag'")) 34 | .get_matches_from(vec!["", "-c", "-f"]); 35 | assert!(!m.is_present("color")); 36 | assert!(m.is_present("flag")); 37 | } 38 | 39 | #[test] 40 | fn posix_compatible_opts_long() { 41 | let m = App::new("posix") 42 | .arg(Arg::from_usage("--flag [flag] 'some flag'").mutually_overrides_with("color")) 43 | .arg(Arg::from_usage("--color [color] 'some other flag'")) 44 | .get_matches_from(vec!["", "--flag", "some" ,"--color", "other"]); 45 | assert!(m.is_present("color")); 46 | assert_eq!(m.value_of("color").unwrap(), "other"); 47 | assert!(!m.is_present("flag")); 48 | 49 | let m = App::new("posix") 50 | .arg(Arg::from_usage("--flag [flag] 'some flag'").mutually_overrides_with("color")) 51 | .arg(Arg::from_usage("--color [color] 'some other flag'")) 52 | .get_matches_from(vec!["", "--color", "some" ,"--flag", "other"]); 53 | assert!(!m.is_present("color")); 54 | assert!(m.is_present("flag")); 55 | assert_eq!(m.value_of("flag").unwrap(), "other"); 56 | } 57 | 58 | #[test] 59 | fn posix_compatible_opts_long_equals() { 60 | let m = App::new("posix") 61 | .arg(Arg::from_usage("--flag [flag] 'some flag'").mutually_overrides_with("color")) 62 | .arg(Arg::from_usage("--color [color] 'some other flag'")) 63 | .get_matches_from(vec!["", "--flag=some" ,"--color=other"]); 64 | assert!(m.is_present("color")); 65 | assert_eq!(m.value_of("color").unwrap(), "other"); 66 | assert!(!m.is_present("flag")); 67 | 68 | let m = App::new("posix") 69 | .arg(Arg::from_usage("--flag [flag] 'some flag'").mutually_overrides_with("color")) 70 | .arg(Arg::from_usage("--color [color] 'some other flag'")) 71 | .get_matches_from(vec!["", "--color=some" ,"--flag=other"]); 72 | assert!(!m.is_present("color")); 73 | assert!(m.is_present("flag")); 74 | assert_eq!(m.value_of("flag").unwrap(), "other"); 75 | } 76 | 77 | #[test] 78 | fn posix_compatible_opts_short() { 79 | let m = App::new("posix") 80 | .arg(Arg::from_usage("-f [flag] 'some flag'").mutually_overrides_with("color")) 81 | .arg(Arg::from_usage("-c [color] 'some other flag'")) 82 | .get_matches_from(vec!["", "-f", "some", "-c", "other"]); 83 | assert!(m.is_present("color")); 84 | assert_eq!(m.value_of("color").unwrap(), "other"); 85 | assert!(!m.is_present("flag")); 86 | 87 | let m = App::new("posix") 88 | .arg(Arg::from_usage("-f [flag] 'some flag'").mutually_overrides_with("color")) 89 | .arg(Arg::from_usage("-c [color] 'some other flag'")) 90 | .get_matches_from(vec!["", "-c", "some", "-f", "other"]); 91 | assert!(!m.is_present("color")); 92 | assert!(m.is_present("flag")); 93 | assert_eq!(m.value_of("flag").unwrap(), "other"); 94 | } 95 | 96 | #[test] 97 | fn conflict_overriden() { 98 | let m = App::new("conflict_overriden") 99 | .arg(Arg::from_usage("-f, --flag 'some flag'") 100 | .conflicts_with("debug")) 101 | .arg(Arg::from_usage("-d, --debug 'other flag'")) 102 | .arg(Arg::from_usage("-c, --color 'third flag'") 103 | .mutually_overrides_with("flag")) 104 | .get_matches_from(vec!["", "-f", "-c", "-d"]); 105 | assert!(m.is_present("color")); 106 | assert!(!m.is_present("flag")); 107 | assert!(m.is_present("debug")); 108 | } 109 | 110 | #[test] 111 | fn conflict_overriden_2() { 112 | let result = App::new("conflict_overriden") 113 | .arg(Arg::from_usage("-f, --flag 'some flag'") 114 | .conflicts_with("debug")) 115 | .arg(Arg::from_usage("-d, --debug 'other flag'")) 116 | .arg(Arg::from_usage("-c, --color 'third flag'") 117 | .mutually_overrides_with("flag")) 118 | .get_matches_from_safe(vec!["", "-f", "-d", "-c"]); 119 | assert!(result.is_err()); 120 | let err = result.err().unwrap(); 121 | assert_eq!(err.error_type, ClapErrorType::ArgumentConflict); 122 | } 123 | 124 | #[test] 125 | fn conflict_overriden_3() { 126 | let result = App::new("conflict_overriden") 127 | .arg(Arg::from_usage("-f, --flag 'some flag'") 128 | .conflicts_with("debug")) 129 | .arg(Arg::from_usage("-d, --debug 'other flag'")) 130 | .arg(Arg::from_usage("-c, --color 'third flag'") 131 | .mutually_overrides_with("flag")) 132 | .get_matches_from_safe(vec!["", "-d", "-c", "-f"]); 133 | assert!(result.is_err()); 134 | let err = result.err().unwrap(); 135 | assert_eq!(err.error_type, ClapErrorType::ArgumentConflict); 136 | } 137 | 138 | #[test] 139 | fn conflict_overriden_4() { 140 | let m = App::new("conflict_overriden") 141 | .arg(Arg::from_usage("-f, --flag 'some flag'") 142 | .conflicts_with("debug")) 143 | .arg(Arg::from_usage("-d, --debug 'other flag'")) 144 | .arg(Arg::from_usage("-c, --color 'third flag'") 145 | .mutually_overrides_with("flag")) 146 | .get_matches_from(vec!["", "-d", "-f", "-c"]); 147 | assert!(m.is_present("color")); 148 | assert!(!m.is_present("flag")); 149 | assert!(m.is_present("debug")); 150 | } 151 | 152 | #[test] 153 | fn require_overriden() { 154 | let result = App::new("require_overriden") 155 | .arg(Arg::with_name("flag") 156 | .index(1) 157 | .required(true)) 158 | .arg(Arg::from_usage("-c, --color 'other flag'") 159 | .mutually_overrides_with("flag")) 160 | .get_matches_from_safe(vec!["", "flag", "-c"]); 161 | assert!(result.is_err()); 162 | let err = result.err().unwrap(); 163 | assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument); 164 | } 165 | 166 | #[test] 167 | fn require_overriden_2() { 168 | let m = App::new("require_overriden") 169 | .arg(Arg::with_name("flag") 170 | .index(1) 171 | .required(true)) 172 | .arg(Arg::from_usage("-c, --color 'other flag'") 173 | .mutually_overrides_with("flag")) 174 | .get_matches_from(vec!["", "-c", "flag"]); 175 | assert!(!m.is_present("color")); 176 | assert!(m.is_present("flag")); 177 | } 178 | 179 | #[test] 180 | fn require_overriden_3() { 181 | let m = App::new("require_overriden") 182 | .arg(Arg::from_usage("-f, --flag 'some flag'") 183 | .requires("debug")) 184 | .arg(Arg::from_usage("-d, --debug 'other flag'")) 185 | .arg(Arg::from_usage("-c, --color 'third flag'") 186 | .mutually_overrides_with("flag")) 187 | .get_matches_from(vec!["", "-f", "-c"]); 188 | assert!(m.is_present("color")); 189 | assert!(!m.is_present("flag")); 190 | assert!(!m.is_present("debug")); 191 | } 192 | 193 | #[test] 194 | fn require_overriden_4() { 195 | let result = App::new("require_overriden") 196 | .arg(Arg::from_usage("-f, --flag 'some flag'") 197 | .requires("debug")) 198 | .arg(Arg::from_usage("-d, --debug 'other flag'")) 199 | .arg(Arg::from_usage("-c, --color 'third flag'") 200 | .mutually_overrides_with("flag")) 201 | .get_matches_from_safe(vec!["", "-c", "-f"]); 202 | assert!(result.is_err()); 203 | let err = result.err().unwrap(); 204 | assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument); 205 | } -------------------------------------------------------------------------------- /benches/03_complex.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | #[macro_use] 4 | extern crate clap; 5 | extern crate test; 6 | 7 | use clap::{App, Arg, SubCommand}; 8 | 9 | use test::Bencher; 10 | 11 | static M_VAL_NAMES: [&'static str; 2] = ["one", "two"]; 12 | static ARGS: &'static str = "-o --option=[opt]... 'tests options' 13 | [positional] 'tests positionals'"; 14 | static OPT3_VALS: [&'static str; 2] = ["fast", "slow"]; 15 | static POS3_VALS: [&'static str; 2] = ["vi", "emacs"]; 16 | 17 | macro_rules! create_app { 18 | () => ({ 19 | App::new("claptests") 20 | .version("0.1") 21 | .about("tests clap library") 22 | .author("Kevin K. ") 23 | .args_from_usage(ARGS) 24 | .arg(Arg::from_usage("-f --flag... 'tests flags'") 25 | .global(true)) 26 | .args(vec![ 27 | Arg::from_usage("[flag2] -F 'tests flags with exclusions'").conflicts_with("flag").requires("option2"), 28 | Arg::from_usage("--long-option-2 [option2] 'tests long options with exclusions'").conflicts_with("option").requires("positional2"), 29 | Arg::from_usage("[positional2] 'tests positionals with exclusions'"), 30 | Arg::from_usage("-O --Option [option3] 'tests options with specific value sets'").possible_values(&OPT3_VALS), 31 | Arg::from_usage("[positional3]... 'tests positionals with specific values'").possible_values(&POS3_VALS), 32 | Arg::from_usage("--multvals [multvals] 'Tests mutliple values, not mult occs'").value_names(&M_VAL_NAMES), 33 | Arg::from_usage("--multvalsmo [multvalsmo]... 'Tests mutliple values, not mult occs'").value_names(&M_VAL_NAMES), 34 | Arg::from_usage("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2), 35 | Arg::from_usage("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3) 36 | ]) 37 | .subcommand(SubCommand::with_name("subcmd") 38 | .about("tests subcommands") 39 | .version("0.1") 40 | .author("Kevin K. ") 41 | .arg_from_usage("-o --option [scoption]... 'tests options'") 42 | .arg_from_usage("[scpositional] 'tests positionals'")) 43 | }) 44 | } 45 | 46 | #[bench] 47 | fn create_app_from_usage(b: &mut Bencher) { 48 | 49 | b.iter(|| create_app!()); 50 | } 51 | 52 | #[bench] 53 | fn create_app_builder(b: &mut Bencher) { 54 | b.iter(|| { 55 | App::new("claptests") 56 | .version("0.1") 57 | .about("tests clap library") 58 | .author("Kevin K. ") 59 | .arg(Arg::with_name("opt") 60 | .help("tests options") 61 | .short("o") 62 | .long("option") 63 | .takes_value(true) 64 | .multiple(true)) 65 | .arg(Arg::with_name("positional") 66 | .help("tests positionals") 67 | .index(1)) 68 | .arg(Arg::with_name("flag") 69 | .short("f") 70 | .help("tests flags") 71 | .long("flag") 72 | .multiple(true) 73 | .global(true)) 74 | .arg(Arg::with_name("flag2") 75 | .short("F") 76 | .help("tests flags with exclusions") 77 | .conflicts_with("flag") 78 | .requires("option2")) 79 | .arg(Arg::with_name("option2") 80 | .help("tests long options with exclusions") 81 | .conflicts_with("option") 82 | .requires("positional2") 83 | .takes_value(true) 84 | .long("long-option-2")) 85 | .arg(Arg::with_name("positional2") 86 | .index(3) 87 | .help("tests positionals with exclusions")) 88 | .arg(Arg::with_name("option3") 89 | .short("O") 90 | .long("Option") 91 | .takes_value(true) 92 | .help("tests options with specific value sets") 93 | .possible_values(&OPT3_VALS)) 94 | .arg(Arg::with_name("positional3") 95 | .multiple(true) 96 | .help("tests positionals with specific values") 97 | .index(4) 98 | .possible_values(&POS3_VALS)) 99 | .arg(Arg::with_name("multvals") 100 | .long("multvals") 101 | .takes_value(true) 102 | .help("Tests mutliple values, not mult occs") 103 | .value_names(&M_VAL_NAMES)) 104 | .arg(Arg::with_name("multvalsmo") 105 | .long("multvalsmo") 106 | .takes_value(true) 107 | .multiple(true) 108 | .help("Tests mutliple values, not mult occs") 109 | .value_names(&M_VAL_NAMES)) 110 | .arg(Arg::with_name("minvals") 111 | .long("minvals2") 112 | .multiple(true) 113 | .takes_value(true) 114 | .help("Tests 2 min vals") 115 | .min_values(2)) 116 | .arg(Arg::with_name("maxvals") 117 | .long("maxvals3") 118 | .takes_value(true) 119 | .multiple(true) 120 | .help("Tests 3 max vals") 121 | .max_values(3)) 122 | .subcommand(SubCommand::with_name("subcmd") 123 | .about("tests subcommands") 124 | .version("0.1") 125 | .author("Kevin K. ") 126 | .arg(Arg::with_name("scoption") 127 | .short("o") 128 | .long("option") 129 | .multiple(true) 130 | .takes_value(true) 131 | .help("tests options")) 132 | .arg(Arg::with_name("scpositional") 133 | .index(1) 134 | .help("tests positionals"))); 135 | }); 136 | } 137 | 138 | #[bench] 139 | fn create_app_macros(b: &mut Bencher) { 140 | b.iter(|| { 141 | clap_app!(claptests => 142 | (version: "0.1") 143 | (about: "tests clap library") 144 | (author: "Kevin K. ") 145 | (@arg opt: -o --option +takes_value ... "tests options") 146 | (@arg positional: index(1) "tests positionals") 147 | (@arg flag: -f --flag ... +global "tests flags") 148 | (@arg flag2: -F conflicts_with[flag] requires[option2] 149 | "tests flags with exclusions") 150 | (@arg option2: --long_option_2 conflicts_with[option] requires[positional2] 151 | "tests long options with exclusions") 152 | (@arg positional2: index(2) "tests positionals with exclusions") 153 | (@arg option3: -O --Option +takes_value possible_value[fast slow] 154 | "tests options with specific value sets") 155 | (@arg positional3: index(3) ... possible_value[vi emacs] 156 | "tests positionals with specific values") 157 | (@arg multvals: --multvals +takes_value value_name[one two] 158 | "Tests mutliple values, not mult occs") 159 | (@arg multvalsmo: --multvalsmo ... +takes_value value_name[one two] 160 | "Tests mutliple values, not mult occs") 161 | (@arg minvals: --minvals2 min_values(1) ... +takes_value "Tests 2 min vals") 162 | (@arg maxvals: --maxvals3 ... +takes_value max_values(3) "Tests 3 max vals") 163 | (@subcommand subcmd => 164 | (about: "tests subcommands") 165 | (version: "0.1") 166 | (author: "Kevin K. ") 167 | (@arg scoption: -o --option ... +takes_value "tests options") 168 | (@arg scpositional: index(1) "tests positionals")) 169 | ); 170 | }); 171 | } 172 | 173 | #[bench] 174 | fn parse_clean(b: &mut Bencher) { 175 | b.iter(|| create_app!().get_matches_from(vec![""])); 176 | } 177 | 178 | #[bench] 179 | fn parse_flag(b: &mut Bencher) { 180 | b.iter(|| create_app!().get_matches_from(vec!["", "-f"])); 181 | } 182 | 183 | #[bench] 184 | fn parse_option(b: &mut Bencher) { 185 | b.iter(|| create_app!().get_matches_from(vec!["", "-o", "option1"])); 186 | } 187 | 188 | #[bench] 189 | fn parse_positional(b: &mut Bencher) { 190 | b.iter(|| create_app!().get_matches_from(vec!["", "arg1"])); 191 | } 192 | 193 | #[bench] 194 | fn parse_sc_clean(b: &mut Bencher) { 195 | b.iter(|| create_app!().get_matches_from(vec!["", "subcmd"])); 196 | } 197 | 198 | #[bench] 199 | fn parse_sc_flag(b: &mut Bencher) { 200 | b.iter(|| create_app!().get_matches_from(vec!["", "subcmd", "-f"])); 201 | } 202 | 203 | #[bench] 204 | fn parse_sc_option(b: &mut Bencher) { 205 | b.iter(|| create_app!().get_matches_from(vec!["", "subcmd", "-o", "option1"])); 206 | } 207 | 208 | #[bench] 209 | fn parse_sc_positional(b: &mut Bencher) { 210 | b.iter(|| create_app!().get_matches_from(vec!["", "subcmd", "arg1"])); 211 | } 212 | 213 | #[bench] 214 | fn parse_complex1(b: &mut Bencher) { 215 | b.iter(|| create_app!().get_matches_from(vec!["", "-ff", "-o", "option1", "arg1", "-O", "fast", "arg2", "--multvals", "one", "two", "three"])); 216 | } 217 | 218 | #[bench] 219 | fn parse_complex2(b: &mut Bencher) { 220 | b.iter(|| create_app!().get_matches_from(vec!["", "arg1", "-f", "arg2", "--long-option-2", "some", "-O", "slow", "--multvalsmo", "one", "two", "--minvals2", "3", "2", "1"])); 221 | } 222 | 223 | 224 | #[bench] 225 | fn parse_sc_complex(b: &mut Bencher) { 226 | b.iter(|| create_app!().get_matches_from(vec!["", "subcmd", "-f", "-o", "option1", "arg1"])); 227 | } 228 | -------------------------------------------------------------------------------- /src/args/argmatches.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use args::SubCommand; 4 | use args::MatchedArg; 5 | 6 | /// Used to get information about the arguments that where supplied to the program at runtime by 7 | /// the user. To get a new instance of this struct you use `.get_matches()` of the `App` struct. 8 | /// 9 | /// 10 | /// # Example 11 | /// 12 | /// ```no_run 13 | /// # use clap::{App, Arg}; 14 | /// let matches = App::new("MyApp") 15 | /// // adding of arguments and configuration goes here... 16 | /// # .arg(Arg::with_name("config") 17 | /// # .long("config") 18 | /// # .required(true) 19 | /// # .takes_value(true)) 20 | /// # .arg(Arg::with_name("debug") 21 | /// # .short("d") 22 | /// # .multiple(true)) 23 | /// .get_matches(); 24 | /// // if you had an argument named "output" that takes a value 25 | /// if let Some(o) = matches.value_of("output") { 26 | /// println!("Value for output: {}", o); 27 | /// } 28 | /// 29 | /// // If you have a required argument you can call .unwrap() because the program will exit long 30 | /// // before this point if the user didn't specify it at runtime. 31 | /// println!("Config file: {}", matches.value_of("config").unwrap()); 32 | /// 33 | /// // You can check the presence of an argument 34 | /// if matches.is_present("debug") { 35 | /// // Another way to check if an argument was present, or if it occurred multiple times is to 36 | /// // use occurrences_of() which returns 0 if an argument isn't found at runtime, or the 37 | /// // number of times that it occurred, if it was. To allow an argument to appear more than 38 | /// // once, you must use the .multiple(true) method, otherwise it will only return 1 or 0. 39 | /// if matches.occurrences_of("debug") > 2 { 40 | /// println!("Debug mode is REALLY on"); 41 | /// } else { 42 | /// println!("Debug mode kind of on"); 43 | /// } 44 | /// } 45 | /// 46 | /// // You can get the sub-matches of a particular subcommand (in this case "test") 47 | /// // If "test" had it's own "-l" flag you could check for it's presence accordingly 48 | /// if let Some(ref matches) = matches.subcommand_matches("test") { 49 | /// if matches.is_present("list") { 50 | /// println!("Printing testing lists..."); 51 | /// } else { 52 | /// println!("Not printing testing lists..."); 53 | /// } 54 | /// } 55 | pub struct ArgMatches<'n, 'a> { 56 | #[doc(hidden)] 57 | pub args: HashMap<&'a str, MatchedArg>, 58 | #[doc(hidden)] 59 | pub subcommand: Option>>, 60 | #[doc(hidden)] 61 | pub usage: Option, 62 | } 63 | 64 | impl<'n, 'a> ArgMatches<'n, 'a> { 65 | /// Creates a new instance of `ArgMatches`. This ins't called directly, but 66 | /// through the `.get_matches()` method of `App` 67 | /// 68 | /// # Example 69 | /// 70 | /// ```no_run 71 | /// # use clap::{App, Arg}; 72 | /// let matches = App::new("myprog").get_matches(); 73 | /// ``` 74 | #[doc(hidden)] 75 | pub fn new() -> ArgMatches<'n, 'a> { 76 | ArgMatches { 77 | args: HashMap::new(), 78 | subcommand: None, 79 | usage: None 80 | } 81 | } 82 | 83 | /// Gets the value of a specific option or positional argument (i.e. an argument that takes 84 | /// an additional value at runtime). If the option wasn't present at runtime 85 | /// it returns `None`. 86 | /// 87 | /// *NOTE:* If getting a value for an option or positional argument that allows multiples, 88 | /// prefer `values_of()` as `value_of()` will only return the _*first*_ value. 89 | /// 90 | /// # Example 91 | /// 92 | /// ```no_run 93 | /// # use clap::{App, Arg}; 94 | /// # let matches = App::new("myapp") 95 | /// # .arg(Arg::with_name("output") 96 | /// # .takes_value(true)) 97 | /// # .get_matches(); 98 | /// if let Some(o) = matches.value_of("output") { 99 | /// println!("Value for output: {}", o); 100 | /// } 101 | /// ``` 102 | pub fn value_of(&self, 103 | name: &str) 104 | -> Option<&str> { 105 | if let Some(ref arg) = self.args.get(name) { 106 | if let Some(ref vals) = arg.values { 107 | if let Some(ref val) = vals.values().nth(0) { 108 | return Some(&val[..]); 109 | } 110 | } 111 | } 112 | None 113 | } 114 | 115 | /// Gets the values of a specific option or positional argument in a vector (i.e. an argument 116 | /// that takes multiple values at runtime). If the option wasn't present at runtime it 117 | /// returns `None` 118 | /// 119 | /// # Example 120 | /// 121 | /// ```no_run 122 | /// # use clap::{App, Arg}; 123 | /// # let matches = App::new("myapp") 124 | /// # .arg(Arg::with_name("output").takes_value(true)).get_matches(); 125 | /// // If the program had option "-c" that took a value and was run 126 | /// // via "myapp -o some -o other -o file" 127 | /// // values_of() would return a [&str; 3] ("some", "other", "file") 128 | /// if let Some(os) = matches.values_of("output") { 129 | /// for o in os { 130 | /// println!("A value for output: {}", o); 131 | /// } 132 | /// } 133 | /// ``` 134 | pub fn values_of(&'a self, 135 | name: &str) 136 | -> Option> { 137 | if let Some(ref arg) = self.args.get(name) { 138 | if let Some(ref vals) = arg.values { 139 | return Some(vals.values().map(|s| &s[..]).collect::>()); 140 | } 141 | } 142 | None 143 | } 144 | 145 | /// Returns if an argument was present at runtime. 146 | /// 147 | /// 148 | /// # Example 149 | /// 150 | /// ```no_run 151 | /// # use clap::{App, Arg}; 152 | /// # let matches = App::new("myapp") 153 | /// # .arg(Arg::with_name("output").takes_value(true)).get_matches(); 154 | /// if matches.is_present("output") { 155 | /// println!("The output argument was used!"); 156 | /// } 157 | /// ``` 158 | pub fn is_present(&self, 159 | name: &str) 160 | -> bool { 161 | if let Some(ref sc) = self.subcommand { 162 | if sc.name == name { 163 | return true; 164 | } 165 | } 166 | if self.args.contains_key(name) { 167 | return true; 168 | } 169 | false 170 | } 171 | 172 | /// Returns the number of occurrences of an option, flag, or positional argument at runtime. 173 | /// If an argument isn't present it will return `0`. Can be used on arguments which *don't* 174 | /// allow multiple occurrences, but will obviously only return `0` or `1`. 175 | /// 176 | /// 177 | /// # Example 178 | /// 179 | /// ```no_run 180 | /// # use clap::{App, Arg}; 181 | /// # let matches = App::new("myapp") 182 | /// # .arg(Arg::with_name("output").takes_value(true)).get_matches(); 183 | /// if matches.occurrences_of("debug") > 1 { 184 | /// println!("Debug mode is REALLY on"); 185 | /// } else { 186 | /// println!("Debug mode kind of on"); 187 | /// } 188 | /// ``` 189 | pub fn occurrences_of(&self, 190 | name: &str) 191 | -> u8 { 192 | if let Some(ref arg) = self.args.get(name) { 193 | return arg.occurrences; 194 | } 195 | 0 196 | } 197 | 198 | /// Returns the `ArgMatches` for a particular subcommand or None if the subcommand wasn't 199 | /// present at runtime. 200 | /// 201 | /// 202 | /// # Example 203 | /// 204 | /// ```no_run 205 | /// # use clap::{App, Arg, SubCommand}; 206 | /// # let app_matches = App::new("myapp") 207 | /// # .subcommand(SubCommand::with_name("test")).get_matches(); 208 | /// if let Some(matches) = app_matches.subcommand_matches("test") { 209 | /// // Use matches as normal 210 | /// } 211 | /// ``` 212 | pub fn subcommand_matches<'na>(&self, 213 | name: &'na str) 214 | -> Option<&ArgMatches> { 215 | if let Some( ref sc) = self.subcommand { 216 | if sc.name != name { 217 | return None; 218 | } 219 | return Some(&sc.matches); 220 | } 221 | None 222 | } 223 | 224 | /// Returns the name of the subcommand used of the parent `App`, or `None` if one wasn't found 225 | /// 226 | /// *NOTE*: Only a single subcommand may be present per `App` at runtime, does *NOT* check for 227 | /// the name of sub-subcommand's names 228 | /// 229 | /// 230 | /// # Example 231 | /// 232 | /// ```no_run 233 | /// # use clap::{App, Arg, SubCommand}; 234 | /// # let app_matches = App::new("myapp") 235 | /// # .subcommand(SubCommand::with_name("test")).get_matches(); 236 | /// match app_matches.subcommand_name() { 237 | /// Some("test") => {}, // test was used 238 | /// Some("config") => {}, // config was used 239 | /// _ => {}, // Either no subcommand or one not tested for... 240 | /// } 241 | /// ``` 242 | pub fn subcommand_name(&self) -> Option<&str> { 243 | if let Some( ref sc ) = self.subcommand { 244 | return Some(&sc.name[..]); 245 | } 246 | None 247 | } 248 | 249 | /// Returns the name and `ArgMatches` of the subcommand used at runtime or ("", None) if one 250 | /// wasn't found. 251 | /// 252 | /// 253 | /// # Example 254 | /// 255 | /// ```no_run 256 | /// # use clap::{App, Arg, SubCommand}; 257 | /// # let app_matches = App::new("myapp") 258 | /// # .subcommand(SubCommand::with_name("test")).get_matches(); 259 | /// match app_matches.subcommand() { 260 | /// ("test", Some(matches)) => {}, // test was used 261 | /// ("config", Some(matches)) => {}, // config was used 262 | /// _ => {}, // Either no subcommand or one not tested for... 263 | /// } 264 | /// ``` 265 | pub fn subcommand(&self) -> (&str, Option<&ArgMatches>) { 266 | if let Some( ref sc ) = self.subcommand { 267 | return (&sc.name[..], Some(&sc.matches)); 268 | } 269 | ("", None) 270 | } 271 | 272 | /// Returns a string slice of the usage statement for the `App` (or `SubCommand`) 273 | /// 274 | /// 275 | /// # Example 276 | /// 277 | /// ```no_run 278 | /// # use clap::{App, Arg, SubCommand}; 279 | /// # let app_matches = App::new("myapp") 280 | /// # .subcommand(SubCommand::with_name("test")).get_matches(); 281 | /// println!("{}",app_matches.usage()); 282 | /// ``` 283 | pub fn usage(&self) -> &str { 284 | if let Some( ref u ) = self.usage { 285 | return &u[..]; 286 | } 287 | 288 | // Should be un-reachable 289 | "" 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /clap-tests/run_tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import subprocess 4 | import re 5 | 6 | failed = False 7 | 8 | _ansi = re.compile(r'\x1b[^m]*m') 9 | 10 | _help = '''claptests 0.0.1 11 | Kevin K. 12 | tests clap library 13 | 14 | USAGE: 15 | \tclaptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND] 16 | 17 | FLAGS: 18 | -f, --flag tests flags 19 | -F tests flags with exclusions 20 | -h, --help Prints help information 21 | -V, --version Prints version information 22 | 23 | OPTIONS: 24 | --maxvals3 ... Tests 3 max vals 25 | --minvals2 ... Tests 2 min vals 26 | --multvals Tests mutliple values, not mult occs 27 | --multvalsmo Tests mutliple values, not mult occs 28 | -o, --option ... tests options 29 | --long-option-2 tests long options with exclusions 30 | -O, --Option tests options with specific value sets [values: fast slow] 31 | 32 | ARGS: 33 | positional tests positionals 34 | positional2 tests positionals with exclusions 35 | positional3... tests positionals with specific values 36 | 37 | SUBCOMMANDS: 38 | help Prints this message 39 | subcmd tests subcommands''' 40 | 41 | _sc_dym_usage = '''error: The subcommand 'subcm' isn't valid 42 | Did you mean 'subcmd' ? 43 | 44 | If you received this message in error, try re-running with 'claptests -- subcm' 45 | 46 | USAGE: 47 | claptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND] 48 | 49 | For more information try --help''' 50 | 51 | _arg_dym_usage = '''error: The argument '--optio' isn't valid 52 | Did you mean --option ? 53 | 54 | USAGE: 55 | claptests --option ... 56 | 57 | For more information try --help''' 58 | 59 | _pv_dym_usage = '''error: 'slo' isn't a valid value for '--Option ' 60 | [valid values: fast slow] 61 | 62 | Did you mean 'slow' ? 63 | 64 | USAGE: 65 | claptests --Option 66 | 67 | For more information try --help''' 68 | 69 | _excluded = '''error: The argument '--flag' cannot be used with '-F' 70 | 71 | USAGE: 72 | \tclaptests [positional2] -F --long-option-2 73 | 74 | For more information try --help''' 75 | 76 | _excluded_l = '''error: The argument '-f' cannot be used '-F' 77 | 78 | USAGE: 79 | claptests [positional2] -F --long-option-2 80 | 81 | For more information try --help''' 82 | 83 | _required = '''error: The following required arguments were not supplied: 84 | \t'[positional2]' 85 | \t'--long-option-2 ' 86 | 87 | USAGE: 88 | \tclaptests [positional2] -F --long-option-2 89 | 90 | For more information try --help''' 91 | 92 | _fop = '''flag present 1 times 93 | option present 1 times with value: some 94 | An option: some 95 | positional present with value: value 96 | flag2 NOT present 97 | option2 maybe present with value of: Nothing 98 | positional2 maybe present with value of: Nothing 99 | option3 NOT present 100 | positional3 NOT present 101 | option present 1 times with value: some 102 | An option: some 103 | positional present with value: value 104 | subcmd NOT present''' 105 | 106 | _f2op = '''flag present 2 times 107 | option present 1 times with value: some 108 | An option: some 109 | positional present with value: value 110 | flag2 NOT present 111 | option2 maybe present with value of: Nothing 112 | positional2 maybe present with value of: Nothing 113 | option3 NOT present 114 | positional3 NOT present 115 | option present 1 times with value: some 116 | An option: some 117 | positional present with value: value 118 | subcmd NOT present''' 119 | 120 | _o2p = '''flag NOT present 121 | option present 2 times with value: some 122 | An option: some 123 | An option: other 124 | positional present with value: value 125 | flag2 NOT present 126 | option2 maybe present with value of: Nothing 127 | positional2 maybe present with value of: Nothing 128 | option3 NOT present 129 | positional3 NOT present 130 | option present 2 times with value: some 131 | An option: some 132 | An option: other 133 | positional present with value: value 134 | subcmd NOT present''' 135 | 136 | _schelp = '''claptests-subcmd 0.1 137 | Kevin K. 138 | tests subcommands 139 | 140 | USAGE: 141 | \tclaptests subcmd [FLAGS] [OPTIONS] [--] [ARGS] 142 | 143 | FLAGS: 144 | -f, --flag tests flags 145 | -h, --help Prints help information 146 | -V, --version Prints version information 147 | 148 | OPTIONS: 149 | -o, --option ... tests options 150 | 151 | ARGS: 152 | scpositional tests positionals''' 153 | 154 | _scfop = '''flag NOT present 155 | option NOT present 156 | positional NOT present 157 | flag2 NOT present 158 | option2 maybe present with value of: Nothing 159 | positional2 maybe present with value of: Nothing 160 | option3 NOT present 161 | positional3 NOT present 162 | option NOT present 163 | positional NOT present 164 | subcmd present 165 | flag present 1 times 166 | scoption present with value: some 167 | An scoption: some 168 | scpositional present with value: value''' 169 | 170 | _scf2op = '''flag NOT present 171 | option NOT present 172 | positional NOT present 173 | flag2 NOT present 174 | option2 maybe present with value of: Nothing 175 | positional2 maybe present with value of: Nothing 176 | option3 NOT present 177 | positional3 NOT present 178 | option NOT present 179 | positional NOT present 180 | subcmd present 181 | flag present 2 times 182 | scoption present with value: some 183 | An scoption: some 184 | scpositional present with value: value''' 185 | 186 | _min_vals_few = '''error: The argument '--minvals2 ...' requires at least 2 values, but 1 was provided 187 | 188 | USAGE: 189 | \tclaptests --minvals2 ... 190 | 191 | For more information try --help''' 192 | 193 | _exact = '''flag NOT present 194 | option NOT present 195 | positional NOT present 196 | flag2 NOT present 197 | option2 maybe present with value of: Nothing 198 | positional2 maybe present with value of: Nothing 199 | option3 NOT present 200 | positional3 NOT present 201 | option NOT present 202 | positional NOT present 203 | subcmd NOT present''' 204 | 205 | _max_vals_more = '''flag NOT present 206 | option NOT present 207 | positional present with value: too 208 | flag2 NOT present 209 | option2 maybe present with value of: Nothing 210 | positional2 maybe present with value of: Nothing 211 | option3 NOT present 212 | positional3 NOT present 213 | option NOT present 214 | positional present with value: too 215 | subcmd NOT present''' 216 | 217 | _mult_vals_more = '''error: The argument '--multvals' was supplied more than once, but does not support multiple values 218 | 219 | USAGE: 220 | \tclaptests --multvals 221 | 222 | For more information try --help''' 223 | 224 | _mult_vals_few = '''error: The argument '--multvals ' requires a value but none was supplied 225 | 226 | USAGE: 227 | \tclaptests --multvals 228 | 229 | For more information try --help''' 230 | 231 | _mult_vals_2m1 = '''error: The argument '--multvalsmo ' requires 2 values, but 1 was provided 232 | 233 | USAGE: 234 | claptests --multvalsmo 235 | 236 | For more information try --help''' 237 | 238 | _bin = './target/release/claptests' 239 | 240 | cmds = {#'help short: ': ['{} -h'.format(_bin), _help], 241 | #'help long: ': ['{} --help'.format(_bin), _help], 242 | 'help subcmd: ': ['{} help'.format(_bin), _help], 243 | #'excluded first: ': ['{} -f -F'.format(_bin), _excluded], 244 | #'excluded last: ': ['{} -F -f'.format(_bin), _excluded_l], 245 | 'missing required: ': ['{} -F'.format(_bin), _required], 246 | 'max_vals too many: ': ['{} --maxvals3 some other value too'.format(_bin), _max_vals_more], 247 | 'max_vals exact: ': ['{} --maxvals3 some other value'.format(_bin), _exact], 248 | 'max_vals less: ': ['{} --maxvals3 some other'.format(_bin), _exact], 249 | 'min_vals more: ': ['{} --minvals2 some other value too'.format(_bin), _exact], 250 | 'min_vals exact: ': ['{} --minvals2 some value'.format(_bin), _exact], 251 | 'min_vals too few: ': ['{} --minvals2 some'.format(_bin), _min_vals_few], 252 | 'mult_vals too many: ': ['{} --multvals some other --multvals some other'.format(_bin), _mult_vals_more], 253 | 'mult_vals too few: ': ['{} --multvals some'.format(_bin), _mult_vals_few], 254 | 'mult_vals exact: ': ['{} --multvals some other'.format(_bin), _exact], 255 | 'mult_valsmo x2: ': ['{} --multvalsmo some other --multvalsmo some other'.format(_bin), _exact], 256 | 'mult_valsmo x2-1: ': ['{} --multvalsmo some other --multvalsmo some'.format(_bin), _mult_vals_2m1], 257 | 'mult_valsmo x1: ': ['{} --multvalsmo some other'.format(_bin), _exact], 258 | 'F2(ss),O(s),P: ': ['{} value -f -f -o some'.format(_bin), _f2op], 259 | 'arg dym: ': ['{} --optio=foo'.format(_bin), _arg_dym_usage], 260 | #'pv dym: ': ['{} --Option slo'.format(_bin), _pv_dym_usage], 261 | #'pv dym(=): ': ['{} --Option=slo'.format(_bin), _pv_dym_usage], 262 | 'O2(ll)P: ': ['{} value --option some --option other'.format(_bin), _o2p], 263 | 'O2(l=l=)P: ': ['{} value --option=some --option=other'.format(_bin), _o2p], 264 | 'O2(ss)P: ': ['{} value -o some -o other'.format(_bin), _o2p], 265 | 'F2(s2),O(s),P: ': ['{} value -ff -o some'.format(_bin), _f2op], 266 | 'F(s),O(s),P: ': ['{} value -f -o some'.format(_bin), _fop], 267 | 'F(l),O(l),P: ': ['{} value --flag --option some'.format(_bin), _fop], 268 | 'F(l),O(l=),P: ': ['{} value --flag --option=some'.format(_bin), _fop], 269 | 'sc dym: ': ['{} subcm'.format(_bin), _sc_dym_usage], 270 | 'sc help short: ': ['{} subcmd -h'.format(_bin), _schelp], 271 | 'sc help long: ': ['{} subcmd --help'.format(_bin), _schelp], 272 | 'scF(l),O(l),P: ': ['{} subcmd value --flag --option some'.format(_bin), _scfop], 273 | 'scF(l),O(s),P: ': ['{} subcmd value --flag -o some'.format(_bin), _scfop], 274 | 'scF(l),O(l=),P: ': ['{} subcmd value --flag --option=some'.format(_bin), _scfop], 275 | 'scF(s),O(l),P: ': ['{} subcmd value -f --option some'.format(_bin), _scfop], 276 | 'scF(s),O(s),P: ': ['{} subcmd value -f -o some'.format(_bin), _scfop], 277 | 'scF(s),O(l=),P: ': ['{} subcmd value -f --option=some'.format(_bin), _scfop], 278 | 'scF2(s),O(l),P: ': ['{} subcmd value -ff --option some'.format(_bin), _scf2op], 279 | 'scF2(s),O(s),P: ': ['{} subcmd value -ff -o some'.format(_bin), _scf2op], 280 | 'scF2(s),O(l=),P: ': ['{} subcmd value -ff --option=some'.format(_bin), _scf2op], 281 | 'scF2(l2),O(l),P: ': ['{} subcmd value --flag --flag --option some'.format(_bin), _scf2op], 282 | 'scF2(l2),O(s),P: ': ['{} subcmd value --flag --flag -o some'.format(_bin), _scf2op], 283 | 'scF2(l2),O(l=),P: ': ['{} subcmd value --flag --flag --option=some'.format(_bin), _scf2op], 284 | 'scF2(s2),O(l),P: ': ['{} subcmd value -f -f --option some'.format(_bin), _scf2op], 285 | 'scF2(s2),O(s),P: ': ['{} subcmd value -f -f -o some'.format(_bin), _scf2op], 286 | 'scF2(s2),O(l=),P: ': ['{} subcmd value -f -f --option=some'.format(_bin), _scf2op] 287 | } 288 | 289 | def pass_fail(name, check, good): 290 | global failed 291 | sys.stdout.write(name) 292 | if check == good: 293 | print('Pass') 294 | return 295 | failed = True 296 | print('Fail\n\tShould be: \n{}\n\tBut is: \n{}'.format(good, check)) 297 | 298 | 299 | def main(): 300 | for cmd, cmd_v in cmds.items(): 301 | proc = subprocess.Popen(cmd_v[0], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 302 | out, err = proc.communicate() 303 | out = _ansi.sub('', out.strip()) 304 | err = _ansi.sub('', err.strip()) 305 | if out: 306 | pass_fail(cmd, out, cmd_v[1]) 307 | else: 308 | pass_fail(cmd, err, cmd_v[1]) 309 | 310 | if failed: 311 | print('One or more tests failed') 312 | return 1 313 | print('All tests passed!') 314 | 315 | if __name__ == '__main__': 316 | sys.exit(main()) 317 | -------------------------------------------------------------------------------- /src/args/group.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "yaml")] 2 | use std::collections::BTreeMap; 3 | use std::fmt::{Debug, Formatter, Result}; 4 | 5 | #[cfg(feature = "yaml")] 6 | use yaml_rust::Yaml; 7 | 8 | /// `ArgGroup`s are a family of related arguments and way for you to say, "Any of these arguments". 9 | /// By placing arguments in a logical group, you can make easier requirement and exclusion rules 10 | /// intead of having to list each individually, or when you want a rule to apply "any but not all" 11 | /// arguments. 12 | /// 13 | /// For instance, you can make an entire ArgGroup required, this means that one (and *only* one) 14 | /// argument. from that group must be present. Using more than one argument from an ArgGroup causes 15 | /// a failure (graceful exit). 16 | /// 17 | /// You can also do things such as name an ArgGroup as a confliction or requirement, meaning any 18 | /// of the arguments that belong to that group will cause a failure if present, or must present 19 | /// respectively. 20 | /// 21 | /// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be 22 | /// present out of a given set. Imagine that you had multiple arguments, and you want one of them 23 | /// to be required, but making all of them required isn't feasible because perhaps they conflict 24 | /// with each other. For example, lets say that you were building an application where one could 25 | /// set a given version number by supplying a string with an option argument, i.e. 26 | /// `--set-ver v1.2.3`, you also wanted to support automatically using a previous version number 27 | /// and simply incrementing one of the three numbers. So you create three flags `--major`, 28 | /// `--minor`, and `--patch`. All of these arguments shouldn't be used at one time but you want to 29 | /// specify that *at least one* of them is used. For this, you can create a group. 30 | /// 31 | /// # Example 32 | /// 33 | /// ```no_run 34 | /// # use clap::{App, ArgGroup}; 35 | /// let _ = App::new("app") 36 | /// .args_from_usage("--set-ver [ver] 'set the version manually' 37 | /// --major 'auto increase major' 38 | /// --minor 'auto increase minor' 39 | /// --patch 'auto increase patch") 40 | /// .arg_group(ArgGroup::with_name("vers") 41 | /// .add_all(&["ver", "major", "minor","patch"]) 42 | /// .required(true)) 43 | /// # .get_matches(); 44 | pub struct ArgGroup<'n, 'ar> { 45 | #[doc(hidden)] 46 | pub name: &'n str, 47 | #[doc(hidden)] 48 | pub args: Vec<&'ar str>, 49 | #[doc(hidden)] 50 | pub required: bool, 51 | #[doc(hidden)] 52 | pub requires: Option>, 53 | #[doc(hidden)] 54 | pub conflicts: Option>, 55 | } 56 | 57 | impl<'n, 'ar> ArgGroup<'n, 'ar> { 58 | /// Creates a new instace of `ArgGroup` using a unique string name. 59 | /// The name will only be used by the library consumer and not displayed to the use. 60 | /// 61 | /// # Example 62 | /// 63 | /// ```no_run 64 | /// # use clap::{App, ArgGroup}; 65 | /// # let matches = App::new("myprog") 66 | /// # .arg_group( 67 | /// ArgGroup::with_name("conifg") 68 | /// # ).get_matches(); 69 | pub fn with_name(n: &'n str) -> Self { 70 | ArgGroup { 71 | name: n, 72 | required: false, 73 | args: vec![], 74 | requires: None, 75 | conflicts: None 76 | } 77 | } 78 | 79 | /// Creates a new instace of `ArgGroup` from a .yml (YAML) file. 80 | /// 81 | /// # Example 82 | /// 83 | /// ```ignore 84 | /// # use clap::ArgGroup; 85 | /// let yml = load_yaml!("group.yml"); 86 | /// let ag = ArgGroup::from_yaml(yml); 87 | /// ``` 88 | #[cfg(feature = "yaml")] 89 | pub fn from_yaml<'y>(y: &'y BTreeMap) -> ArgGroup<'y, 'y> { 90 | // We WANT this to panic on error...so expect() is good. 91 | let name_yml = y.keys().nth(0).unwrap(); 92 | let name_str = name_yml.as_str().unwrap(); 93 | let mut a = ArgGroup::with_name(name_str); 94 | let group_settings = y.get(name_yml).unwrap().as_hash().unwrap(); 95 | 96 | for (k, v) in group_settings.iter() { 97 | a = match k.as_str().unwrap() { 98 | "required" => a.required(v.as_bool().unwrap()), 99 | "args" => { 100 | for ys in v.as_vec().unwrap() { 101 | if let Some(s) = ys.as_str() { 102 | a = a.add(s); 103 | } 104 | } 105 | a 106 | } 107 | "requires" => { 108 | for ys in v.as_vec().unwrap() { 109 | if let Some(s) = ys.as_str() { 110 | a = a.requires(s); 111 | } 112 | } 113 | a 114 | } 115 | "conflicts_with" => { 116 | for ys in v.as_vec().unwrap() { 117 | if let Some(s) = ys.as_str() { 118 | a = a.conflicts_with(s); 119 | } 120 | } 121 | a 122 | } 123 | s => panic!("Unknown ArgGroup setting '{}' in YAML file for \ 124 | ArgGroup '{}'", s, name_str), 125 | } 126 | } 127 | 128 | a 129 | } 130 | 131 | /// Adds an argument to this group by name 132 | /// 133 | /// 134 | /// # Example 135 | /// 136 | /// ```no_run 137 | /// # use clap::{App, ArgGroup}; 138 | /// # let matches = App::new("myprog") 139 | /// # .arg_group( 140 | /// # ArgGroup::with_name("conifg") 141 | /// .add("config") 142 | /// # ).get_matches(); 143 | pub fn add(mut self, 144 | n: &'ar str) 145 | -> Self { 146 | self.args.push(n); 147 | self 148 | } 149 | 150 | /// Adds multiple arguments to this group by name 151 | /// 152 | /// 153 | /// # Example 154 | /// 155 | /// ```no_run 156 | /// # use clap::{App, ArgGroup}; 157 | /// # let matches = App::new("myprog") 158 | /// # .arg_group( 159 | /// # ArgGroup::with_name("conifg") 160 | /// .add_all(&["config", "input", "output"]) 161 | /// # ).get_matches(); 162 | pub fn add_all(mut self, 163 | ns: &[&'ar str]) 164 | -> Self { 165 | for n in ns { 166 | self = self.add(n); 167 | } 168 | self 169 | } 170 | 171 | /// Sets the requirement of this group. A required group will be displayed in the usage string 172 | /// of the application in the format `[arg|arg2|arg3]`. A required `ArgGroup` simply states 173 | /// that one, and only one argument from this group *must* be present at runtime (unless 174 | /// conflicting with another argument). 175 | /// 176 | /// 177 | /// # Example 178 | /// 179 | /// ```no_run 180 | /// # use clap::{App, ArgGroup}; 181 | /// # let matches = App::new("myprog") 182 | /// # .arg_group( 183 | /// # ArgGroup::with_name("conifg") 184 | /// .required(true) 185 | /// # ).get_matches(); 186 | pub fn required(mut self, 187 | r: bool) 188 | -> Self { 189 | self.required = r; 190 | self 191 | } 192 | 193 | /// Sets the requirement rules of this group. This is not to be confused with a required group. 194 | /// Requirement rules function just like argument requirement rules, you can name other 195 | /// arguments or groups that must be present when one of the arguments from this group is used. 196 | /// 197 | /// **NOTE:** The name provided may be an argument, or group name 198 | /// 199 | /// 200 | /// # Example 201 | /// 202 | /// ```no_run 203 | /// # use clap::{App, ArgGroup}; 204 | /// # let matches = App::new("myprog") 205 | /// # .arg_group( 206 | /// # ArgGroup::with_name("conifg") 207 | /// .requires("config") 208 | /// # ).get_matches(); 209 | pub fn requires(mut self, 210 | n: &'ar str) 211 | -> Self { 212 | if let Some(ref mut reqs) = self.requires { 213 | reqs.push(n); 214 | } else { 215 | self.requires = Some(vec![n]); 216 | } 217 | self 218 | } 219 | 220 | /// Sets the requirement rules of this group. This is not to be confused with a required group. 221 | /// Requirement rules function just like argument requirement rules, you can name other 222 | /// arguments or groups that must be present when one of the arguments from this group is used. 223 | /// 224 | /// **NOTE:** The names provided may be an argument, or group name 225 | /// 226 | /// 227 | /// # Example 228 | /// 229 | /// ```no_run 230 | /// # use clap::{App, ArgGroup}; 231 | /// # let matches = App::new("myprog") 232 | /// # .arg_group( 233 | /// # ArgGroup::with_name("conifg") 234 | /// .requires_all(&["config", "input"]) 235 | /// # ).get_matches(); 236 | pub fn requires_all(mut self, 237 | ns: &[&'ar str]) 238 | -> Self { 239 | for n in ns { 240 | self = self.requires(n); 241 | } 242 | self 243 | } 244 | 245 | /// Sets the exclusion rules of this group. Exclusion rules function just like argument 246 | /// exclusion rules, you can name other arguments or groups that must not be present when one 247 | /// of the arguments from this group are used. 248 | /// 249 | /// **NOTE:** The name provided may be an argument, or group name 250 | /// 251 | /// 252 | /// # Example 253 | /// 254 | /// ```no_run 255 | /// # use clap::{App, ArgGroup}; 256 | /// # let matches = App::new("myprog") 257 | /// # .arg_group( 258 | /// # ArgGroup::with_name("conifg") 259 | /// .conflicts_with("config") 260 | /// # ).get_matches(); 261 | pub fn conflicts_with(mut self, 262 | n: &'ar str) 263 | -> Self { 264 | if let Some(ref mut confs) = self.conflicts { 265 | confs.push(n); 266 | } else { 267 | self.conflicts = Some(vec![n]); 268 | } 269 | self 270 | } 271 | 272 | /// Sets the exclusion rules of this group. Exclusion rules function just like argument 273 | /// exclusion rules, you can name other arguments or groups that must not be present when one 274 | /// of the arguments from this group are used. 275 | /// 276 | /// **NOTE:** The names provided may be an argument, or group name 277 | /// 278 | /// 279 | /// # Example 280 | /// 281 | /// ```no_run 282 | /// # use clap::{App, ArgGroup}; 283 | /// # let matches = App::new("myprog") 284 | /// # .arg_group( 285 | /// # ArgGroup::with_name("conifg") 286 | /// .conflicts_with_all(&["config", "input"]) 287 | /// # ).get_matches(); 288 | pub fn conflicts_with_all(mut self, 289 | ns: &[&'ar str]) 290 | -> Self { 291 | for n in ns { 292 | self = self.conflicts_with(n); 293 | } 294 | self 295 | } 296 | } 297 | 298 | impl<'n, 'ar> Debug for ArgGroup<'n, 'ar> { 299 | fn fmt(&self, 300 | f: &mut Formatter) 301 | -> Result { 302 | write!(f, "{{ 303 | name:{:?}, 304 | args: {:?}, 305 | required: {:?}, 306 | requires: {:?}, 307 | conflicts: {:?}, 308 | }}", self.name, self.args, self.required, self.requires, self.conflicts) 309 | } 310 | } 311 | 312 | #[cfg(test)] 313 | mod test { 314 | use super::ArgGroup; 315 | 316 | #[test] 317 | fn groups() { 318 | let g = ArgGroup::with_name("test") 319 | .add("a1") 320 | .add_all(&["a2", "a3"]) 321 | .add("a4") 322 | .required(true) 323 | .conflicts_with("c1") 324 | .conflicts_with_all(&["c2", "c3"]) 325 | .conflicts_with("c4") 326 | .requires("r1") 327 | .requires_all(&["r2", "r3"]) 328 | .requires("r4"); 329 | 330 | let args = vec!["a1", "a2", "a3", "a4"]; 331 | let reqs = vec!["r1", "r2", "r3", "r4"]; 332 | let confs = vec!["c1", "c2", "c3", "c4"]; 333 | 334 | assert_eq!(g.args, args); 335 | assert_eq!(g.requires.unwrap(), reqs); 336 | assert_eq!(g.conflicts.unwrap(), confs); 337 | 338 | } 339 | } 340 | --------------------------------------------------------------------------------