clap/src/app/mod.rs

2817 lines
108 KiB
Rust
Raw Normal View History

#[allow(dead_code)]
mod settings;
#[macro_use]
mod macros;
pub use self::settings::AppSettings;
2015-11-09 08:48:49 +00:00
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::env;
use std::io::{self, BufRead, BufWriter, Write};
use std::path::Path;
use std::process;
use std::ffi::OsStr;
use std::borrow::Borrow;
use std::fmt::Display;
2015-11-09 08:48:49 +00:00
#[cfg(feature = "yaml")]
use yaml_rust::Yaml;
use vec_map::VecMap;
use args::{Arg, AnyArg, ArgGroup, ArgMatches, ArgMatcher, SubCommand};
2015-11-09 08:48:49 +00:00
use args::{FlagBuilder, OptBuilder, PosBuilder};
use args::settings::{ArgFlags, ArgSettings};
use fmt::Format;
use self::settings::AppFlags;
use suggestions;
use errors::{ClapError, ClapErrorType, ClapResult, error_builder};
const INTERNAL_ERROR_MSG: &'static str = "Fatal internal error. Please consider filing a bug \
report at https://github.com/kbknapp/clap-rs/issues";
/// Used to create a representation of a command line program and all possible command line
/// arguments.
///
/// Application settings are set using the "builder pattern" with `.get_matches()` being the
/// terminal method that starts the runtime-parsing process and returns information about
/// the user supplied arguments (or lack there of).
///
/// There aren't any mandatory "options" that one must set. The "options" may also appear in any
/// order (so long as `.get_matches()` is the last method called).
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let matches = App::new("myprog")
/// .author("Me, me@mail.com")
/// .version("1.0.2")
/// .about("Explains in brief what the program does")
/// .arg(
/// Arg::with_name("in_file").index(1)
/// )
/// .after_help("Longer explaination to appear after the options when \
/// displaying the help information from --help or -h")
/// .get_matches();
///
/// // Your program logic starts here...
/// ```
#[allow(missing_debug_implementations)]
pub struct App<'a, 'v, 'ab, 'u, 'h, 'ar> {
// The name displayed to the user when showing version and help/usage information
name: String,
name_slice: &'ar str,
// A string of author(s) if desired. Displayed when showing help/usage information
author: Option<&'a str>,
// The version displayed to the user
version: Option<&'v str>,
// A brief explanation of the program that gets displayed to the user when shown
// help/usage
// information
about: Option<&'ab str>,
// Additional help information
more_help: Option<&'h str>,
// A list of possible flags
flags: Vec<FlagBuilder<'ar>>,
// A list of possible options
opts: Vec<OptBuilder<'ar>>,
// A list of positional arguments
positionals: VecMap<PosBuilder<'ar>>,
// A list of subcommands
subcommands: Vec<App<'a, 'v, 'ab, 'u, 'h, 'ar>>,
help_short: Option<char>,
version_short: Option<char>,
required: Vec<&'ar str>,
short_list: Vec<char>,
long_list: Vec<&'ar str>,
blacklist: Vec<&'ar str>,
usage_str: Option<&'u str>,
bin_name: Option<String>,
usage: Option<String>,
groups: HashMap<&'ar str, ArgGroup<'ar, 'ar>>,
global_args: Vec<Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar>>,
help_str: Option<&'u str>,
settings: AppFlags,
overrides: Vec<&'ar str>,
}
impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar> {
/// Creates a new instance of an application requiring a name (such as the binary). The name
/// will be displayed to the user when they request to print version or help and usage
/// information. The name should not contain spaces (hyphens '-' are ok).
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let prog = App::new("myprog")
/// # .get_matches();
/// ```
pub fn new(n: &'ar str) -> Self {
App {
name: n.to_owned(),
name_slice: n,
author: None,
about: None,
more_help: None,
version: None,
flags: vec![],
opts: vec![],
positionals: VecMap::new(),
subcommands: vec![],
help_short: None,
version_short: None,
required: vec![],
short_list: vec![],
long_list: vec![],
usage_str: None,
usage: None,
blacklist: vec![],
bin_name: None,
groups: HashMap::new(),
global_args: vec![],
help_str: None,
overrides: vec![],
settings: AppFlags::new(),
}
}
/// Creates a new instace of `App` from a .yml (YAML) file. The YAML file must be properly
/// formatted or this function will panic!(). A full example of supported YAML objects can be
/// found in `examples/17_yaml.rs` and `examples/17_yaml.yml`.
///
/// In order to use this function you must compile with the `features = ["yaml"]` in your
/// settings for `[dependencies.clap]` table of your `Cargo.toml`
///
/// Note, due to how the YAML objects are built there is a convienience macro for loading the
/// YAML file (relative to the current file, like modules work). That YAML object can then be
/// passed to this function.
///
/// # Examples
///
/// ```ignore
/// # use clap::App;
/// let yml = load_yaml!("app.yml");
/// let app = App::from_yaml(yml);
/// ```
#[cfg(feature = "yaml")]
pub fn from_yaml<'y>(mut yaml: &'y Yaml) -> App<'y, 'y, 'y, 'y, 'y, 'y> {
// We WANT this to panic on error...so expect() is good.
let mut is_sc = None;
let mut a = if let Some(name) = yaml["name"].as_str() {
App::new(name)
} else {
let yaml_hash = yaml.as_hash().unwrap();
let sc_key = yaml_hash.keys().nth(0).unwrap();
is_sc = Some(yaml_hash.get(sc_key).unwrap());
App::new(sc_key.as_str().unwrap())
};
yaml = if let Some(sc) = is_sc {
sc
} else {
yaml
};
if let Some(v) = yaml["version"].as_str() {
a = a.version(v);
}
if let Some(v) = yaml["author"].as_str() {
a = a.author(v);
}
if let Some(v) = yaml["bin_name"].as_str() {
a = a.bin_name(v);
}
if let Some(v) = yaml["about"].as_str() {
a = a.about(v);
}
if let Some(v) = yaml["after_help"].as_str() {
a = a.after_help(v);
}
if let Some(v) = yaml["usage"].as_str() {
a = a.usage(v);
}
if let Some(v) = yaml["help"].as_str() {
a = a.help(v);
}
if let Some(v) = yaml["help_short"].as_str() {
a = a.help_short(v);
}
if let Some(v) = yaml["version_short"].as_str() {
a = a.version_short(v);
}
if let Some(v) = yaml["settings"].as_vec() {
for ys in v {
if let Some(s) = ys.as_str() {
a = a.setting(s.parse().ok().expect("unknown AppSetting found in YAML file"));
}
}
}
if let Some(v) = yaml["args"].as_vec() {
for arg_yaml in v {
a = a.arg(Arg::from_yaml(&arg_yaml.as_hash().unwrap()));
}
}
if let Some(v) = yaml["subcommands"].as_vec() {
for sc_yaml in v {
a = a.subcommand(SubCommand::from_yaml(&sc_yaml));
}
}
if let Some(v) = yaml["arg_groups"].as_vec() {
for ag_yaml in v {
a = a.arg_group(ArgGroup::from_yaml(&ag_yaml.as_hash().unwrap()));
}
}
a
}
/// Sets a string of author(s) and will be displayed to the user when they request the help
/// information with `--help` or `-h`.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .author("Me, me@mymain.com")
/// # ;
/// ```
pub fn author(mut self, a: &'a str) -> Self {
self.author = Some(a);
self
}
/// Overrides the system-determined binary name. This should only be used when absolutely
/// neccessary, such as the binary name for your application is misleading, or perhaps *not*
/// how the user should invoke your program.
///
/// **NOTE:** This command **should not** be used for SubCommands.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .bin_name("my_binary")
/// # ;
/// ```
pub fn bin_name(mut self, a: &str) -> Self {
self.bin_name = Some(a.to_owned());
self
}
/// Sets a string briefly describing what the program does and will be displayed when
/// displaying help information.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .about("Does really amazing things to great people")
/// # ;
/// ```
pub fn about(mut self, a: &'ab str) -> Self {
self.about = Some(a);
self
}
/// Adds additional help information to be displayed in addition to and directly after
/// auto-generated help. This information is displayed **after** the auto-generated help
/// information. This additional help is often used to describe how to use the arguments,
/// or caveats to be noted.
///
/// # Examples
///
/// ```no_run
/// # use clap::App;
/// App::new("myprog")
/// .after_help("Does really amazing things to great people")
/// # ;
/// ```
pub fn after_help(mut self, h: &'h str) -> Self {
self.more_help = Some(h);
self
}
/// Allows subcommands to override all requirements of the parent (this command). For example
/// if you had a subcommand or even top level application which had a required arguments that
/// are only required as long as there is no subcommand present.
///
/// **Deprecated:** Use `App::setting()` with `AppSettings::SubcommandsNegateReqs` instead.
/// This method will be removed at 2.x
///
/// **NOTE:** This defaults to false (using subcommand does *not* negate requirements)
///
/// # Examples
///
/// ```no_run
/// # use clap::App;
/// App::new("myprog")
/// .subcommands_negate_reqs(true)
/// # ;
/// ```
pub fn subcommands_negate_reqs(mut self, n: bool) -> Self {
if n {
self.settings.set(&AppSettings::SubcommandsNegateReqs);
} else {
self.settings.unset(&AppSettings::SubcommandsNegateReqs);
}
self
}
/// Allows specifying that if no subcommand is present at runtime, error and exit gracefully
///
/// **Deprecated:** Use `App::setting()` with `AppSettings::SubcommandRequired` instead. This
/// method will be removed at 2.x
///
/// **NOTE:** This defaults to false (subcommands do *not* need to be present)
///
/// # Examples
///
/// ```no_run
/// # use clap::App;
/// App::new("myprog")
/// .subcommand_required(true)
/// # ;
/// ```
pub fn subcommand_required(mut self, n: bool) -> Self {
if n {
self.settings.set(&AppSettings::SubcommandRequired);
} else {
self.settings.unset(&AppSettings::SubcommandRequired);
}
self
}
/// Sets a string of the version number to be displayed when displaying version or help
/// information.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .version("v0.1.24")
/// # ;
/// ```
pub fn version(mut self, v: &'v str) -> Self {
self.version = Some(v);
self
}
/// Sets a custom usage string to override the auto-generated usage string.
///
/// This will be displayed to the user when errors are found in argument parsing, or when you
/// call `ArgMatcher::usage()`
2015-11-09 08:48:49 +00:00
///
/// **NOTE:** You do not need to specify the "USAGE: \n\t" portion, as that will
/// still be applied by `clap`, you only need to specify the portion starting
/// with the binary name.
///
/// **NOTE:** This will not replace the entire help message, *only* the portion
/// showing the usage.
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .usage("myapp [-clDas] <some_file>")
/// # ;
/// ```
pub fn usage(mut self, u: &'u str) -> Self {
self.usage_str = Some(u);
self
}
/// Sets a custom help message and overrides the auto-generated one. This should only be used
/// when the auto-generated message does not suffice.
///
/// This will be displayed to the user when they use the default `--help` or `-h`
///
/// **NOTE:** This replaces the **entire** help message, so nothing will be auto-generated.
///
/// **NOTE:** This **only** replaces the help message for the current command, meaning if you
/// are using subcommands, those help messages will still be auto-generated unless you
/// specify a `.help()` for them as well.
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myapp")
/// .help("myapp v1.0\n\
/// Does awesome things\n\
/// (C) me@mail.com\n\n\
///
/// USAGE: myapp <opts> <comamnd>\n\n\
///
/// Options:\n\
/// -h, --helpe Dispay this message\n\
/// -V, --version Display version info\n\
/// -s <stuff> Do something with stuff\n\
/// -v Be verbose\n\n\
///
/// Commmands:\n\
/// help Prints this message\n\
/// work Do some work")
/// # ;
/// ```
pub fn help(mut self, h: &'u str) -> Self {
self.help_str = Some(h);
self
}
/// Sets the short version of the `help` argument without the preceding `-`.
///
/// By default `clap` automatically assigns `h`, but this can be overridden
///
/// **NOTE:** Any leading `-` characters will be stripped, and only the first
/// non `-` chacter will be used as the `short` version
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// // Using an uppercase `H` instead of the default lowercase `h`
/// .help_short("H")
/// # ;
pub fn help_short(mut self, s: &str) -> Self {
self.help_short = s.trim_left_matches(|c| c == '-')
.chars()
.nth(0);
self
}
/// Sets the short version of the `version` argument without the preceding `-`.
///
/// By default `clap` automatically assigns `V`, but this can be overridden
///
/// **NOTE:** Any leading `-` characters will be stripped, and only the first
/// non `-` chacter will be used as the `short` version
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// // Using a lowercase `v` instead of the default capital `V`
/// .version_short("v")
/// # ;
pub fn version_short(mut self, s: &str) -> Self {
self.version_short = s.trim_left_matches(|c| c == '-')
.chars()
.nth(0);
self
}
/// Specifies that the help text sould be displayed (and then exit gracefully), if no
/// arguments are present at runtime (i.e. an empty run such as, `$ myprog`.
///
/// **Deprecated:** Use `App::setting()` with `AppSettings::ArgRequiredElseHelp` instead. This
/// method will be removed at 2.x
///
/// **NOTE:** Subcommands count as arguments
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .arg_required_else_help(true)
/// # ;
/// ```
pub fn arg_required_else_help(mut self, tf: bool) -> Self {
if tf {
self.settings.set(&AppSettings::ArgRequiredElseHelp);
} else {
self.settings.unset(&AppSettings::ArgRequiredElseHelp);
}
self
}
/// Hides a subcommand from help message output.
///
/// **NOTE:** This does **not** hide the subcommand from usage strings on error
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, SubCommand};
/// # let matches = App::new("myprog")
/// # .subcommand(
/// # SubCommand::with_name("debug")
/// .hidden(true)
/// # ).get_matches();
pub fn hidden(mut self, h: bool) -> Self {
if h {
self.settings.set(&AppSettings::Hidden);
} else {
self.settings.unset(&AppSettings::Hidden);
}
self
}
/// Uses version of the current command for all subcommands. (Defaults to false; subcommands
/// have independant version strings)
///
/// **Deprecated:** Use `App::setting()` with `AppSettings::GlobalVersion` instead. This
/// method will be removed at 2.x
///
/// **NOTE:** The version for the current command and this setting must be set **prior** to
/// adding any subcommands
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, SubCommand};
/// App::new("myprog")
/// .version("v1.1")
/// .global_version(true)
/// .subcommand(SubCommand::with_name("test"))
/// .get_matches();
/// // running `myprog test --version` will display
/// // "myprog-test v1.1"
/// ```
pub fn global_version(mut self, gv: bool) -> Self {
if gv {
self.settings.set(&AppSettings::GlobalVersion);
} else {
self.settings.unset(&AppSettings::GlobalVersion);
}
self
}
/// Disables `-V` and `--version` for all subcommands (Defaults to false; subcommands have
/// version flags)
///
/// **Deprecated:** Use `App::setting()` with `AppSettings::VersionlessSubcommands` instead.
/// This method will be removed at 2.x
///
/// **NOTE:** This setting must be set **prior** adding any subcommands
///
/// **NOTE:** Do not set this value to false, it will have undesired results!
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, SubCommand};
/// App::new("myprog")
/// .version("v1.1")
/// .versionless_subcommands(true)
/// .subcommand(SubCommand::with_name("test"))
/// .get_matches();
/// // running `myprog test --version` will display unknown argument error
/// ```
pub fn versionless_subcommands(mut self, vers: bool) -> Self {
if vers {
self.settings.set(&AppSettings::VersionlessSubcommands);
} else {
self.settings.unset(&AppSettings::VersionlessSubcommands);
}
self
}
/// By default the auto-generated help message groups flags, options, and positional arguments
/// separately. This setting disable that and groups flags and options together presenting a
/// more unified help message (a la getopts or docopt style).
///
/// **Deprecated:** Use `App::setting()` with `AppSettings::UnifiedHelpMessage` instead. This
/// method will be removed at 2.x
///
/// **NOTE:** This setting is cosmetic only and does not affect any functionality.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, SubCommand};
/// App::new("myprog")
/// .unified_help_message(true)
/// .get_matches();
/// // running `myprog --help` will display a unified "docopt" or "getopts" style help message
/// ```
pub fn unified_help_message(mut self, uni_help: bool) -> Self {
if uni_help {
self.settings.set(&AppSettings::UnifiedHelpMessage);
} else {
self.settings.unset(&AppSettings::UnifiedHelpMessage);
}
self
}
/// Will display a message "Press [ENTER]/[RETURN] to continue..." and wait user before
/// exiting
///
/// This is most useful when writing an application which is run from a GUI shortcut, or on
/// Windows where a user tries to open the binary by double-clicking instead of using the
/// command line (i.e. set `.arg_required_else_help(true)` and `.wait_on_error(true)` to
/// display the help in such a case).
///
/// **Deprecated:** Use `App::setting()` with `AppSettings::WaitOnError` instead. This
/// method will be removed at 2.x
///
/// **NOTE:** This setting is **not** recursive with subcommands, meaning if you wish this
/// behavior for all subcommands, you must set this on each command (needing this is extremely
/// rare)
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .arg_required_else_help(true)
/// # ;
/// ```
pub fn wait_on_error(mut self, w: bool) -> Self {
if w {
self.settings.set(&AppSettings::WaitOnError);
} else {
self.settings.unset(&AppSettings::WaitOnError);
}
self
}
/// Specifies that the help text sould be displayed (and then exit gracefully), if no
/// subcommands are present at runtime (i.e. an empty run such as, `$ myprog`.
///
/// **Deprecated:** Use `App::setting()` with `AppSettings::SubcommandRequiredElseHelp`
/// instead. This method will be removed at 2.x
///
/// **NOTE:** This should *not* be used with `.subcommand_required()` as they do the same
/// thing, except one prints the help text, and one prints an error.
///
/// **NOTE:** If the user specifies arguments at runtime, but no subcommand the help text will
/// still be displayed and exit. If this is *not* the desired result, consider using
/// `.arg_required_else_help()`
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .subcommand_required_else_help(true)
/// # ;
/// ```
pub fn subcommand_required_else_help(mut self, tf: bool) -> Self {
if tf {
self.settings.set(&AppSettings::SubcommandRequiredElseHelp);
} else {
self.settings.unset(&AppSettings::SubcommandRequiredElseHelp);
}
self
}
/// Enables Application level settings, passed as argument
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::SubcommandRequired)
/// .setting(AppSettings::WaitOnError)
/// # ;
/// ```
pub fn setting(mut self, setting: AppSettings) -> Self {
self.settings.set(&setting);
self
}
/// Enables multiple Application level settings, passed as argument
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, AppSettings};
/// App::new("myprog")
/// .settings( &[AppSettings::SubcommandRequired,
/// AppSettings::WaitOnError])
/// # ;
/// ```
pub fn settings(mut self, settings: &[AppSettings]) -> Self {
for s in settings {
self.settings.set(s);
}
self
}
/// Adds an argument to the list of valid possibilties manually. This method allows you full
/// control over the arguments settings and options (as well as dynamic generation). It also
/// allows you specify several more advanced configuration options such as relational rules
/// (exclusions and requirements).
///
/// The only disadvantage to this method is that it's more verbose, and arguments must be added
/// one at a time. Using `Arg::from_usage` helps with the verbosity, and still allows full
/// control over the advanced configuration options.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// // Adding a single "flag" argument with a short and help text, using Arg::with_name()
/// .arg(
/// Arg::with_name("debug")
/// .short("d")
/// .help("turns on debugging mode")
/// )
/// // Adding a single "option" argument with a short, a long, and help text using the less
/// // verbose Arg::from_usage()
/// .arg(
/// Arg::from_usage("-c --config=[CONFIG] 'Optionally sets a config file to use'")
/// )
/// # ;
/// ```
pub fn arg(mut self, a: Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar>) -> Self {
self.add_arg(a);
self
}
// actually adds the arguments
fn add_arg(&mut self, a: Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar>) {
if self.flags.iter().any(|f| &f.name == &a.name) ||
self.opts.iter().any(|o| o.name == a.name) ||
self.positionals.values().any(|p| p.name == a.name) {
panic!("Non-unique argument name: {} is already in use", a.name);
}
if let Some(grp) = a.group {
let ag = self.groups.entry(grp).or_insert(ArgGroup::with_name(grp));
ag.args.push(a.name);
}
if let Some(s) = a.short {
if self.short_list.contains(&s) {
panic!("Argument short must be unique\n\n\t-{} is already in use",
s);
} else {
self.short_list.push(s);
}
}
if let Some(l) = a.long {
if self.long_list.contains(&l) {
panic!("Argument long must be unique\n\n\t--{} is already in use",
l);
} else {
self.long_list.push(l);
}
if l == "help" {
self.settings.set(&AppSettings::NeedsLongHelp);
} else if l == "version" {
self.settings.set(&AppSettings::NeedsLongVersion);
}
}
if a.required {
self.required.push(a.name);
}
if a.index.is_some() || (a.short.is_none() && a.long.is_none()) {
let i = if a.index.is_none() {
(self.positionals.len() + 1)
} else {
a.index.unwrap() as usize
};
if self.positionals.contains_key(&i) {
panic!("Argument \"{}\" has the same index as another positional \
argument\n\n\tPerhaps try .multiple(true) to allow one positional argument \
to take multiple values",
a.name);
}
let pb = PosBuilder::from_arg(&a, i as u8, &mut self.required);
// self.positionals_name.insert(pb.name, i);
self.positionals.insert(i, pb);
} else if a.takes_value {
let ob = OptBuilder::from_arg(&a, &mut self.required);
self.opts.push(ob);
} else {
let fb = FlagBuilder::from(&a);
self.flags.push(fb);
}
if a.global {
if a.required {
panic!("Global arguments cannot be required.\n\n\t'{}' is marked as global and \
required",
a.name);
}
self.global_args.push(a);
}
}
/// Adds multiple arguments to the list of valid possibilties by iterating over a Vec of Args
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .args(
/// vec![Arg::from_usage("[debug] -d 'turns on debugging info"),
/// Arg::with_name("input").index(1).help("the input file to use")]
/// )
/// # ;
/// ```
pub fn args(mut self, args: Vec<Arg<'ar, 'ar, 'ar, 'ar, 'ar, 'ar>>) -> Self {
for arg in args.into_iter() {
self = self.arg(arg);
}
self
}
/// A convienience method for adding a single basic argument (one without advanced
/// relational rules) from a usage type string. The string used follows the same rules and
/// syntax as `Arg::from_usage()`
///
/// The downside to using this method is that you can not set any additional properties of the
/// `Arg` other than what `Arg::from_usage()` supports.
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .arg_from_usage("-c --conf=<config> 'Sets a configuration file to use'")
/// # ;
/// ```
pub fn arg_from_usage(mut self, usage: &'ar str) -> Self {
self = self.arg(Arg::from_usage(usage));
self
}
/// Adds multiple arguments at once from a usage string, one per line. See `Arg::from_usage()`
/// for details on the syntax and rules supported.
///
/// Like `App::arg_from_usage()` the downside is you only set properties for the `Arg`s which
/// `Arg::from_usage()` supports. But here the benefit is pretty strong, as the readability is
/// greatly enhanced, especially if you don't need any of the more advanced configuration
/// options.
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .args_from_usage(
/// "-c --conf=[config] 'Sets a configuration file to use'
/// [debug]... -d 'Sets the debugging level'
/// <input> 'The input file to use'"
/// )
/// # ;
/// ```
pub fn args_from_usage(mut self, usage: &'ar str) -> Self {
for l in usage.lines() {
self = self.arg(Arg::from_usage(l.trim()));
}
self
}
/// Adds an ArgGroup to the application. ArgGroups are a family of related arguments. By
/// placing them in a logical group, you make easier requirement and exclusion rules. For
/// instance, you can make an ArgGroup required, this means that one (and *only* one) argument
/// from that group must be present. Using more than one argument from an ArgGroup causes a
/// failure (graceful exit).
///
/// You can also do things such as name an ArgGroup as a confliction, meaning any of the
/// arguments that belong to that group will cause a failure if present.
///
/// Perhaps the most common use of ArgGroups is to require one and *only* one argument to be
/// present out of a given set. For example, lets say that you were building an application
/// where one could set a given version number by supplying a string using an option argument,
/// such as `--set-ver v1.2.3`, you also wanted to support automatically using a previous
/// version numer and simply incrementing one of the three numbers, so you create three flags
/// `--major`, `--minor`, and `--patch`. All of these arguments shouldn't be used at one time
/// but perhaps you want to specify that *at least one* of them is used. You can create a
/// group
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, ArgGroup};
/// # App::new("app")
/// .args_from_usage("--set-ver [ver] 'set the version manually'
/// --major 'auto increase major'
/// --minor 'auto increase minor'
/// --patch 'auto increase patch")
/// .arg_group(ArgGroup::with_name("vers")
/// .add_all(&["ver", "major", "minor","patch"])
/// .required(true))
/// # ;
pub fn arg_group(mut self, group: ArgGroup<'ar, 'ar>) -> Self {
if group.required {
self.required.push(group.name);
if let Some(ref reqs) = group.requires {
for r in reqs {
self.required.push(r);
}
}
if let Some(ref bl) = group.conflicts {
for b in bl {
self.blacklist.push(b);
}
}
}
let mut found = false;
if let Some(ref mut grp) = self.groups.get_mut(group.name) {
for a in &group.args {
grp.args.push(a);
}
grp.requires = group.requires.clone();
grp.conflicts = group.conflicts.clone();
grp.required = group.required;
found = true;
}
if !found {
self.groups.insert(group.name, group);
}
self
}
/// Adds a ArgGroups to the application. ArgGroups are a family of related arguments. By
/// placing them in a logical group, you make easier requirement and exclusion rules. For
/// instance, you can make an ArgGroup required, this means that one (and *only* one) argument
/// from that group must be present. Using more than one argument from an ArgGroup causes a
/// failure (graceful exit).
///
/// You can also do things such as name an ArgGroup as a confliction, meaning any of the
/// arguments that belong to that group will cause a failure if present.
///
/// Perhaps the most common use of ArgGroups is to require one and *only* one argument to be
/// present out of a given set. For example, lets say that you were building an application
/// where one could set a given version number by supplying a string using an option argument,
/// such as `--set-ver v1.2.3`, you also wanted to support automatically using a previous
/// version numer and simply incrementing one of the three numbers, so you create three flags
/// `--major`, `--minor`, and `--patch`. All of these arguments shouldn't be used at one time
/// but perhaps you want to specify that *at least one* of them is used. You can create a
/// group
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, ArgGroup};
/// # App::new("app")
/// .args_from_usage("--set-ver [ver] 'set the version manually'
/// --major 'auto increase major'
/// --minor 'auto increase minor'
/// --patch 'auto increase patch")
/// .arg_group(ArgGroup::with_name("vers")
/// .add_all(&["ver", "major", "minor","patch"])
/// .required(true))
/// # ;
pub fn arg_groups(mut self, groups: Vec<ArgGroup<'ar, 'ar>>) -> Self {
for g in groups {
self = self.arg_group(g);
}
self
}
/// Adds a subcommand to the list of valid possibilties. Subcommands are effectively sub apps,
/// because they can contain their own arguments, subcommands, version, usage, etc. They also
/// function just like apps, in that they get their own auto generated help, version, and
/// usage.
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, SubCommand};
/// # App::new("myprog")
/// .subcommand(SubCommand::with_name("config")
/// .about("Controls configuration features")
/// .arg_from_usage("<config> 'Required configuration file to use'"))
/// // Additional subcommand configuration goes here, such as other arguments...
/// # ;
/// ```
pub fn subcommand(mut self, mut subcmd: App<'a, 'v, 'ab, 'u, 'h, 'ar>) -> Self {
if subcmd.name == "help" {
self.settings.set(&AppSettings::NeedsSubcommandHelp);
}
if self.settings.is_set(&AppSettings::VersionlessSubcommands) {
subcmd.settings.set(&AppSettings::DisableVersion);
}
if self.settings.is_set(&AppSettings::GlobalVersion) && subcmd.version.is_none() &&
self.version.is_some() {
subcmd.version = Some(self.version.unwrap());
}
self.subcommands.push(subcmd);
self
}
/// Adds multiple subcommands to the list of valid possibilties by iterating over a Vec of
/// `SubCommand`s
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, SubCommand};
/// # App::new("myprog")
/// .subcommands( vec![
/// SubCommand::with_name("config").about("Controls configuration functionality")
/// .arg(Arg::with_name("config_file").index(1)),
/// SubCommand::with_name("debug").about("Controls debug functionality")])
/// # ;
/// ```
pub fn subcommands(mut self, subcmds: Vec<App<'a, 'v, 'ab, 'u, 'h, 'ar>>) -> Self {
for subcmd in subcmds.into_iter() {
self = self.subcommand(subcmd);
}
self
}
// Creates a usage string if one was not provided by the user manually. This
// happens just
// after all arguments were parsed, but before any subcommands have been parsed
// (so as to
// give subcommands their own usage recursively)
fn create_usage(&self, matcher: &[&'ar str]) -> ClapResult<String> {
2015-11-09 08:48:49 +00:00
let mut usage = String::with_capacity(75);
usage.push_str("USAGE:\n\t");
if let Some(u) = self.usage_str {
usage.push_str(u);
} else if !matcher.is_empty() {
try!(self.usage_from_matcher(&mut usage, matcher));
2015-11-09 08:48:49 +00:00
} else {
usage.push_str(&*self.usage
.as_ref()
.unwrap_or(self.bin_name
.as_ref()
.unwrap_or(&self.name)));
let reqs = self.required.iter().map(|n| *n).collect::<Vec<_>>();
let req_strings = self.get_required_from(&reqs, None);
let req_string = req_strings.iter()
.fold(String::new(), |a, s| a + &format!(" {}", s)[..]);
if !self.flags.is_empty() && !self.settings.is_set(&AppSettings::UnifiedHelpMessage) {
usage.push_str(" [FLAGS]");
} else {
usage.push_str(" [OPTIONS]");
}
if !self.settings.is_set(&AppSettings::UnifiedHelpMessage) && !self.opts.is_empty() &&
self.opts.iter().any(|a| !a.settings.is_set(&ArgSettings::Required)) {
usage.push_str(" [OPTIONS]");
}
usage.push_str(&req_string[..]);
// places a '--' in the usage string if there are args and options
// supporting multiple values
if !self.positionals.is_empty() &&
(self.opts.iter().any(|a| a.settings.is_set(&ArgSettings::Multiple)) ||
self.positionals.values().any(|a| a.settings.is_set(&ArgSettings::Multiple))) &&
!self.opts.iter().any(|a| a.settings.is_set(&ArgSettings::Required)) &&
self.subcommands.is_empty() {
usage.push_str(" [--]")
}
if !self.positionals.is_empty() &&
self.positionals
.values()
.any(|a| !a.settings.is_set(&ArgSettings::Required)) {
usage.push_str(" [ARGS]");
}
if !self.subcommands.is_empty() &&
!self.settings.is_set(&AppSettings::SubcommandRequired) {
usage.push_str(" [SUBCOMMAND]");
} else if self.settings.is_set(&AppSettings::SubcommandRequired) &&
!self.subcommands.is_empty() {
usage.push_str(" <SUBCOMMAND>");
}
}
usage.shrink_to_fit();
Ok(usage)
}
// Creates a context aware usage string, or "smart usage" from currently used
// args, and
// requirements
fn usage_from_matcher(&self, usage: &mut String, matcher: &[&'ar str]) -> ClapResult<()> {
2015-11-09 08:48:49 +00:00
use std::fmt::Write;
let mut hs: Vec<&str> = self.required.iter().map(|n| *n).collect();
for n in matcher {
2015-11-09 08:48:49 +00:00
hs.push(*n);
}
let reqs = self.get_required_from(&hs, None);
let r_string = reqs.iter().fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]);
try!(write!(usage,
"{}{}",
self.usage
.as_ref()
.unwrap_or(self.bin_name.as_ref().unwrap_or(&self.name)),
r_string));
if self.settings.is_set(&AppSettings::SubcommandRequired) {
try!(write!(usage, " <SUBCOMMAND>"));
}
Ok(())
}
/// Prints the full help message to `io::stdout()` using a `BufWriter`
///
/// # Examples
/// ```no_run
/// # use clap::App;
/// # use std::io;
/// let mut app = App::new("myprog");
/// let mut out = io::stdout();
/// app.write_help(&mut out).ok().expect("failed to write to stdout");
/// ```
pub fn print_help(&self) -> ClapResult<()> {
let out = io::stdout();
let mut buf_w = BufWriter::new(out.lock());
self.write_help(&mut buf_w)
}
/// Writes the full help message to the user to a `io::Write` object
///
/// ```no_run
/// # use clap::App;
/// # use std::io;
/// let mut app = App::new("myprog");
/// let mut out = io::stdout();
/// app.write_help(&mut out).ok().expect("failed to write to stdout");
/// ```
pub fn write_help<W: Write>(&self, w: &mut W) -> ClapResult<()> {
if let Some(h) = self.help_str {
return writeln!(w, "{}", h).map_err(ClapError::from);
}
// Print the version
try!(write!(w,
"{} {}\n",
&self.bin_name.as_ref().unwrap_or(&self.name)[..].replace(" ", "-"),
self.version.unwrap_or("")));
let flags = !self.flags.is_empty();
let pos = !self.positionals.is_empty();
let opts = !self.opts.is_empty();
let subcmds = !self.subcommands.is_empty();
let unified_help = self.settings.is_set(&AppSettings::UnifiedHelpMessage);
let mut longest_flag = 0;
for fl in self.flags
.iter()
.filter(|f| f.long.is_some() && !f.settings.is_set(&ArgSettings::Hidden))
.map(|a| a.to_string().len()) {
if fl > longest_flag {
longest_flag = fl;
}
}
let mut longest_opt = 0;
for ol in self.opts
.iter()
.filter(|o| !o.settings.is_set(&ArgSettings::Hidden))
.map(|a| a.to_string().len()) {
if ol > longest_opt {
longest_opt = ol;
}
}
let mut longest_pos = 0;
for pl in self.positionals
.values()
.filter(|p| !p.settings.is_set(&ArgSettings::Hidden))
.map(|f| f.to_string().len()) {
if pl > longest_pos {
longest_pos = pl;
}
}
let mut longest_sc = 0;
for scl in self.subcommands
.iter()
.filter(|s| !s.settings.is_set(&AppSettings::Hidden))
.map(|f| f.name.len()) {
if scl > longest_sc {
longest_sc = scl;
}
}
if let Some(author) = self.author {
try!(write!(w, "{}\n", author));
}
if let Some(about) = self.about {
try!(write!(w, "{}\n", about));
}
try!(write!(w, "\n{}", try!(self.create_usage(&[]))));
if flags || opts || pos || subcmds {
try!(write!(w, "\n"));
}
let tab = " ";
let longest = if !unified_help || longest_opt == 0 {
longest_flag
} else {
longest_opt
};
if unified_help && (flags || opts) {
try!(write!(w, "\nOPTIONS:\n"));
let mut combined = BTreeMap::new();
for f in self.flags.iter().filter(|f| !f.settings.is_set(&ArgSettings::Hidden)) {
let mut v = vec![];
try!(f.write_help(&mut v, tab, longest));
combined.insert(f.name, v);
}
for o in self.opts.iter().filter(|o| !o.settings.is_set(&ArgSettings::Hidden)) {
let mut v = vec![];
try!(o.write_help(&mut v, tab, longest));
combined.insert(o.name, v);
}
for (_, a) in combined {
try!(write!(w, "{}", String::from_utf8_lossy(&*a)));
}
} else {
if flags {
try!(write!(w, "\nFLAGS:\n"));
for (_, f) in self.flags
.iter()
.filter(|f| !f.settings.is_set(&ArgSettings::Hidden))
.map(|f| (f.name, f))
.collect::<BTreeMap<_, _>>() {
try!(f.write_help(w, tab, longest));
}
}
if opts {
try!(write!(w, "\nOPTIONS:\n"));
for (_, o) in self.opts
.iter()
.filter(|o| !o.settings.is_set(&ArgSettings::Hidden))
.map(|o| (o.name, o))
.collect::<BTreeMap<_, _>>() {
try!(o.write_help(w, tab, longest_opt));
}
}
}
if pos {
try!(write!(w, "\nARGS:\n"));
for v in self.positionals
.values()
.filter(|p| !p.settings.is_set(&ArgSettings::Hidden)) {
try!(v.write_help(w, tab, longest_pos));
}
}
if subcmds {
try!(write!(w, "\nSUBCOMMANDS:\n"));
for (name, sc) in self.subcommands
.iter()
.filter(|s| !s.settings.is_set(&AppSettings::Hidden))
.map(|s| (s.name_slice, s))
.collect::<BTreeMap<_, _>>() {
try!(write!(w, "{}{}", tab, name));
write_spaces!((longest_sc + 4) - (name.len()), w);
if let Some(a) = sc.about {
if a.contains("{n}") {
let mut ab = a.split("{n}");
while let Some(part) = ab.next() {
try!(write!(w, "{}\n", part));
write_spaces!(longest_sc + 8, w);
try!(write!(w, "{}", ab.next().unwrap_or("")));
}
} else {
try!(write!(w, "{}", a));
}
}
try!(write!(w, "\n"));
}
}
if let Some(h) = self.more_help {
try!(write!(w, "\n{}", h));
}
// flush the buffer
debugln!("Flushing the buffer...");
w.flush().map_err(ClapError::from)
}
/// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls
/// the real parsing function for all subcommands
///
/// # Panics
///
/// If any arguments contain invalid unicode characters. If this is not desired it is
/// recommended to use the `*_safe()` or `*_lossy()` versions of this method.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let matches = App::new("myprog")
/// // Args and options go here...
/// .get_matches();
/// ```
pub fn get_matches(self) -> ArgMatches<'ar, 'ar> {
// Start the parsing
self.get_matches_from(env::args())
}
/// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls
/// the real parsing function for all subcommands. Invalid unicode characters are replaced with
/// `U+FFFD REPLACEMENT CHARACTER`
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let matches = App::new("myprog")
/// // Args and options go here...
/// .get_matches();
/// ```
pub fn get_matches_lossy(self) -> ArgMatches<'ar, 'ar> {
// Start the parsing
self.get_matches_from_lossy(env::args_os())
}
/// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls
/// the real parsing function for all subcommands
///
/// **NOTE:** This method WILL NOT exit when `--help` or `--version` (or short versions) are
/// used. It will return an error, where the `error_type` is a `ClapErrorType::HelpDisplayed`
/// or `ClapErrorType::VersionDisplayed` respectively. You must call `error.exit()` or
/// perform a `std::process::exit` yourself.
///
/// **NOTE:** This method should only be used when is absolutely necessary to handle errors
/// manually.
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let matches = App::new("myprog")
/// // Args and options go here...
/// .get_matches_safe()
/// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) });
/// ```
pub fn get_matches_safe(self) -> ClapResult<ArgMatches<'ar, 'ar>> {
// Start the parsing
self.get_matches_from_safe(env::args_os())
}
/// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls
/// the real parsing function for all subcommands. Invalid unicode characters are replaced with
/// `U+FFFD REPLACEMENT CHARACTER`
///
/// **NOTE:** This method WILL NOT exit when `--help` or `--version` (or short versions) are
/// used. It will return an error, where the `error_type` is a `ClapErrorType::HelpDisplayed`
/// or `ClapErrorType::VersionDisplayed` respectively. You must call `error.exit()` or
/// perform a `std::process::exit` yourself.
///
/// **NOTE:** This method should only be used when is absolutely necessary to handle errors
/// manually.
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let matches = App::new("myprog")
/// // Args and options go here...
/// .get_matches_safe()
/// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) });
/// ```
pub fn get_matches_safe_lossy(self) -> ClapResult<ArgMatches<'ar, 'ar>> {
// Start the parsing
self.get_matches_from_safe_lossy(env::args_os())
}
/// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls
/// the real parsing function for all subcommands
///
/// **NOTE:** The first argument will be parsed as the binary name.
///
/// **NOTE:** This method should only be used when absolutely necessary, such as needing to
/// parse arguments from something other than `std::env::args()`. If you are unsure, use
/// `App::get_matches()`
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let arg_vec = vec!["my_prog", "some", "args", "to", "parse"];
///
/// let matches = App::new("myprog")
/// // Args and options go here...
/// .get_matches_from(arg_vec);
/// ```
pub fn get_matches_from<I, T>(mut self, itr: I) -> ArgMatches<'ar, 'ar>
where I: IntoIterator<Item = T>,
T: AsRef<OsStr>
{
self.get_matches_from_safe_borrow(itr).unwrap_or_else(|e| {
// Otherwise, write to stderr and exit
self.maybe_wait_for_exit(e);
})
}
/// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls
/// the real parsing function for all subcommands. Invalid unicode characters are replaced with
/// `U+FFFD REPLACEMENT CHARACTER`
///
/// **NOTE:** The first argument will be parsed as the binary name.
///
/// **NOTE:** This method should only be used when absolutely necessary, such as needing to
/// parse arguments from something other than `std::env::args()`. If you are unsure, use
/// `App::get_matches()`
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let arg_vec = vec!["my_prog", "some", "args", "to", "parse"];
///
/// let matches = App::new("myprog")
/// // Args and options go here...
/// .get_matches_from(arg_vec);
/// ```
pub fn get_matches_from_lossy<I, T>(mut self, itr: I) -> ArgMatches<'ar, 'ar>
where I: IntoIterator<Item = T>,
T: AsRef<OsStr>
{
self.get_matches_from_safe_borrow_lossy(itr).unwrap_or_else(|e| {
// Otherwise, write to stderr and exit
self.maybe_wait_for_exit(e);
})
}
/// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls
/// the real parsing function for all subcommands
///
/// **NOTE:** This method WILL NOT exit when `--help` or `--version` (or short versions) are
/// used. It will return an error, where the `error_type` is a `ClapErrorType::HelpDisplayed`
/// or `ClapErrorType::VersionDisplayed` respectively. You must call `error.exit()` or
/// perform a `std::process::exit` yourself.
///
/// **NOTE:** The first argument will be parsed as the binary name.
///
/// **NOTE:** This method should only be used when absolutely necessary, such as needing to
/// parse arguments from something other than `std::env::args()`. If you are unsure, use
/// `App::get_matches_safe()`
///
/// **NOTE:** This method should only be used when is absolutely necessary to handle errors
/// manually.
///
/// **NOTE:** Invalid unicode characters will result in an `Err` with type
/// `ClapErrorType::InvalidUnicode`
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let arg_vec = vec!["my_prog", "some", "args", "to", "parse"];
///
/// let matches = App::new("myprog")
/// // Args and options go here...
/// .get_matches_from_safe(arg_vec)
/// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) });
/// ```
pub fn get_matches_from_safe<I, T>(mut self, itr: I) -> ClapResult<ArgMatches<'ar, 'ar>>
where I: IntoIterator<Item = T>,
T: AsRef<OsStr>
{
self.get_matches_from_safe_borrow(itr)
}
/// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls
/// the real parsing function for all subcommands. Invalid unicode characters are replaced with
/// `U+FFFD REPLACEMENT CHARACTER`
///
/// **NOTE:** This method WILL NOT exit when `--help` or `--version` (or short versions) are
/// used. It will return an error, where the `error_type` is a `ClapErrorType::HelpDisplayed`
/// or `ClapErrorType::VersionDisplayed` respectively. You must call `error.exit()` or
/// perform a `std::process::exit` yourself.
///
/// **NOTE:** The first argument will be parsed as the binary name.
///
/// **NOTE:** This method should only be used when absolutely necessary, such as needing to
/// parse arguments from something other than `std::env::args()`. If you are unsure, use
/// `App::get_matches_safe()`
///
/// **NOTE:** This method should only be used when is absolutely necessary to handle errors
/// manually.
///
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let arg_vec = vec!["my_prog", "some", "args", "to", "parse"];
///
/// let matches = App::new("myprog")
/// // Args and options go here...
/// .get_matches_from_safe(arg_vec)
/// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) });
/// ```
pub fn get_matches_from_safe_lossy<I, T>(mut self, itr: I) -> ClapResult<ArgMatches<'ar, 'ar>>
where I: IntoIterator<Item = T>,
T: AsRef<OsStr>
{
self._get_matches_from_safe_borrow(itr, true)
}
/// Starts the parsing process without consuming the `App` struct `self`. This is normally not
/// the desired functionality, instead prefer `App::get_matches_from_safe` which *does*
/// consume `self`.
///
/// **NOTE:** The first argument will be parsed as the binary name.
///
/// **NOTE:** This method should only be used when absolutely necessary, such as needing to
/// parse arguments from something other than `std::env::args()`. If you are unsure, use
/// `App::get_matches_safe()`
///
/// **NOTE:** This method should only be used when is absolutely necessary to handle errors
/// manually.
///
/// **NOTE:** Invalid unicode characters will result in an `Err` with type
/// `ClapErrorType::InvalidUnicode`
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let arg_vec = vec!["my_prog", "some", "args", "to", "parse"];
///
/// let mut app = App::new("myprog");
/// // Args and options go here...
/// let matches = app.get_matches_from_safe_borrow(arg_vec)
/// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) });
/// ```
pub fn get_matches_from_safe_borrow<I, T>(&mut self, itr: I) -> ClapResult<ArgMatches<'ar, 'ar>>
where I: IntoIterator<Item = T>,
T: AsRef<OsStr>
{
self._get_matches_from_safe_borrow(itr, false)
}
/// Starts the parsing process without consuming the `App` struct `self`. This is normally not
/// the desired functionality, instead prefer `App::get_matches_from_safe` which *does*
/// consume `self`. Invalid unicode characters are replaced with `U+FFFD REPLACEMENT CHARACTER`
///
/// **NOTE:** The first argument will be parsed as the binary name.
///
/// **NOTE:** This method should only be used when absolutely necessary, such as needing to
/// parse arguments from something other than `std::env::args()`. If you are unsure, use
/// `App::get_matches_safe()`
///
/// **NOTE:** This method should only be used when is absolutely necessary to handle errors
/// manually.
///
/// **NOTE:** Invalid unicode characters will result in an `Err` with type
/// `ClapErrorType::InvalidUnicode`
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let arg_vec = vec!["my_prog", "some", "args", "to", "parse"];
///
/// let mut app = App::new("myprog");
/// // Args and options go here...
/// let matches = app.get_matches_from_safe_borrow(arg_vec)
/// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) });
/// ```
pub fn get_matches_from_safe_borrow_lossy<I, T>(&mut self,
itr: I)
-> ClapResult<ArgMatches<'ar, 'ar>>
where I: IntoIterator<Item = T>,
T: AsRef<OsStr>
{
self._get_matches_from_safe_borrow(itr, true)
}
fn _get_matches_from_safe_borrow<I, T>(&mut self,
itr: I,
lossy: bool)
-> ClapResult<ArgMatches<'ar, 'ar>>
where I: IntoIterator<Item = T>,
T: AsRef<OsStr>
{
// Verify all positional assertions pass
self.verify_positionals();
// If there are global arguments, we need to propgate them down to subcommands
// before parsing incase we run into a subcommand
self.propogate_globals();
let mut matcher = ArgMatcher::new();
2015-11-09 08:48:49 +00:00
let mut it = itr.into_iter();
// Get the name of the program (argument 1 of env::args()) and determine the
// actual file
// that was used to execute the program. This is because a program called
// ./target/release/my_prog -a
// will have two arguments, './target/release/my_prog', '-a' but we don't want
// to display
// the full path when displaying help messages and such
if !self.settings.is_set(&AppSettings::NoBinaryName) {
if let Some(name) = it.next() {
let p = Path::new(name.as_ref());
if let Some(f) = p.file_name() {
if let Ok(s) = f.to_os_string().into_string() {
if let None = self.bin_name {
self.bin_name = Some(s);
}
}
}
}
}
// do the real parsing
if let Err(e) = self.get_matches_with(&mut matcher, &mut it, lossy) {
2015-11-09 08:48:49 +00:00
return Err(e);
}
Ok(matcher.into())
2015-11-09 08:48:49 +00:00
}
// The actual parsing function
#[cfg_attr(feature="lints", allow(while_let_on_iterator))]
fn get_matches_with<I, T>(&mut self,
matcher: &mut ArgMatcher<'ar>,
2015-11-09 08:48:49 +00:00
it: &mut I,
lossy: bool)
-> ClapResult<()>
where I: Iterator<Item = T>,
T: AsRef<OsStr>
{
// First we create the `--help` and `--version` arguments and add them if
// necessary
self.create_help_and_version();
let mut pos_only = false;
let mut subcmd_name: Option<String> = None;
let mut needs_val_of: Option<&str> = None;
let mut pos_counter = 1;
while let Some(arg) = it.next() {
let arg_cow = match arg.as_ref().to_str() {
Some(s) => s.into(),
None => {
if !lossy {
return Err(
error_builder::InvalidUnicode(
&*try!(self.create_current_usage(matcher)))
2015-11-09 08:48:49 +00:00
);
}
arg.as_ref().to_string_lossy()
}
};
let arg_slice: &str = arg_cow.borrow();
// we need to know if we're parsing a new argument, or the value of previous
// argument,
// perhaps one with multiple values such as --option val1 val2. We determine
// when to
// stop parsing multiple values by finding a '-'
// let new_arg = if arg_slice.starts_with("-") {
// // If we come to a single `-` it's a value, not a new argument...this happens
// // when
// // one wants to use the Unix standard of '-' to mean 'stdin'
// arg_slice.len() > 1
// } else {
// true
// };
2015-11-09 08:48:49 +00:00
// pos_only is determined later, and set true when a user uses the Unix
// standard of
// '--' to mean only positionals follow
// If the user passed `--` we don't check for subcommands, because the argument
// they
// may be trying to pass might match a subcommand name
2015-11-09 08:48:49 +00:00
let starts_new_arg = if arg_slice.starts_with("-") {
!(arg_slice.len() == 1)
} else {
false
};
if !pos_only {
if !starts_new_arg {
// Check to see if parsing a value from an option
if let Some(nvo) = needs_val_of {
// get the OptBuilder so we can check the settings
if let Some(opt) = self.opts.iter().filter(|o| &o.name == &nvo).next() {
needs_val_of = try!(self.add_val_to_arg(opt, arg_slice, matcher));
// get the next value from the iterator
continue;
2015-11-09 08:48:49 +00:00
}
}
}
let mut skip = false;
if arg_slice.starts_with("--") {
if arg_slice.len() == 2 {
// The user has passed '--' which means only positional args follow no matter
// what they start with
pos_only = true;
continue;
2015-11-09 08:48:49 +00:00
}
needs_val_of = try!(self.parse_long_arg(matcher, arg_slice));
} else if arg_slice.starts_with("-") && arg_slice.len() != 1 {
needs_val_of = try!(self.parse_short_arg(matcher, arg_slice));
} else {
skip = true;
2015-11-09 08:48:49 +00:00
}
if !skip { continue; }
if self.subcommands.iter().any(|s| s.name_slice == arg_slice) {
if arg_slice == "help" &&
self.settings.is_set(&AppSettings::NeedsSubcommandHelp) {
return self._help();
2015-11-09 08:48:49 +00:00
}
subcmd_name = Some(arg_slice.to_owned());
break;
} else if let Some(candidate) = suggestions::did_you_mean(
arg_slice,
self.subcommands.iter().map(|s| &s.name)) {
return Err(error_builder::InvalidSubcommand(
arg_slice,
candidate,
self.bin_name.as_ref().unwrap_or(&self.name),
&*try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
}
2015-11-09 08:48:49 +00:00
if let Some(p) = self.positionals.get(&pos_counter) {
try!(self.validate_arg(p, matcher));
2015-11-09 08:48:49 +00:00
try!(self.add_val_to_arg(p, arg_slice, matcher));
2015-11-09 08:48:49 +00:00
if !pos_only &&
(self.settings.is_set(&AppSettings::TrailingVarArg) &&
pos_counter == self.positionals.len()) {
pos_only = true;
}
arg_post_processing!(self, p, matcher);
2015-11-09 08:48:49 +00:00
// Only increment the positional counter if it doesn't allow multiples
if !p.settings.is_set(&ArgSettings::Multiple) {
pos_counter += 1;
2015-11-09 08:48:49 +00:00
}
} else {
return Err(error_builder::UnexpectedArgument(
arg_slice,
self.bin_name.as_ref().unwrap_or(&self.name),
&*try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
}
if let Some(a) = needs_val_of {
if let Some(o) = self.opts.iter().filter(|o| &o.name == &a).next() {
if (o.settings.is_set(&ArgSettings::Multiple) && self.required.is_empty()) ||
!o.settings.is_set(&ArgSettings::Multiple){
let should_err = match matcher.values_of(o.name) {
2015-11-09 08:48:49 +00:00
Some(ref v) => v.is_empty(),
None => true,
};
if should_err {
return Err(error_builder::EmptyValue(
&*o.to_string(),
&*try!(self.create_current_usage(matcher))
2015-11-09 08:48:49 +00:00
));
}
} else {
return Err(error_builder::MissingRequiredArgument(
&*self.get_required_from(&self.required, Some(matcher))
2015-11-09 08:48:49 +00:00
.iter()
.fold(String::new(),
|acc, s| acc + &format!("\n\t{}", Format::Error(s))[..]),
&*try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
} else {
return Err(error_builder::EmptyValue(&*format!("{}",
self.positionals
.values()
.filter(|p| &p.name == &a)
2015-11-09 08:48:49 +00:00
.next()
.expect(INTERNAL_ERROR_MSG)),
&*try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
}
if let Err(e) = self.validate_blacklist(matcher) {
return Err(e);
2015-11-09 08:48:49 +00:00
}
if let Err(e) = self.validate_num_args(matcher) {
return Err(e);
2015-11-09 08:48:49 +00:00
}
matcher.usage(try!(self.create_usage(&[])));
2015-11-09 08:48:49 +00:00
if !(self.settings.is_set(&AppSettings::SubcommandsNegateReqs) && subcmd_name.is_some()) {
try!(self.validate_required(&matcher));
}
2015-11-09 08:48:49 +00:00
if let Some(sc_name) = subcmd_name {
use std::fmt::Write;
let mut mid_string = String::new();
if !self.settings.is_set(&AppSettings::SubcommandsNegateReqs) {
let mut hs = self.required.iter().map(|n| *n).collect::<Vec<_>>();
for k in matcher.arg_names() {
2015-11-09 08:48:49 +00:00
hs.push(*k);
}
let reqs = self.get_required_from(&hs, Some(matcher));
2015-11-09 08:48:49 +00:00
for s in reqs.iter() {
write!(&mut mid_string, " {}", s).ok().expect(INTERNAL_ERROR_MSG);
}
}
mid_string.push_str(" ");
if let Some(ref mut sc) = self.subcommands
.iter_mut()
.filter(|s| s.name_slice == &sc_name)
.next() {
let mut sc_matcher = ArgMatcher::new();
2015-11-09 08:48:49 +00:00
// bin_name should be parent's bin_name + [<reqs>] + the sc's name separated by
// a space
sc.usage = Some(format!("{}{}{}",
self.bin_name.as_ref().unwrap_or(&String::new()),
if self.bin_name.is_some() {
&*mid_string
} else {
""
},
&*sc.name));
sc.bin_name = Some(format!("{}{}{}",
self.bin_name.as_ref().unwrap_or(&String::new()),
if self.bin_name.is_some() {
" "
} else {
""
},
&*sc.name));
if let Err(e) = sc.get_matches_with(&mut sc_matcher, it, lossy) {
2015-11-09 08:48:49 +00:00
e.exit();
}
matcher.subcommand(SubCommand {
2015-11-09 08:48:49 +00:00
name: sc.name_slice,
matches: sc_matcher.into(),
});
2015-11-09 08:48:49 +00:00
}
} else if self.settings.is_set(&AppSettings::SubcommandRequired) {
let bn = self.bin_name.as_ref().unwrap_or(&self.name);
return Err(error_builder::MissingSubcommand(bn,
&try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
} else if self.settings.is_set(&AppSettings::SubcommandRequiredElseHelp) {
let mut out = vec![];
try!(self.write_help(&mut out));
return Err(ClapError {
error: String::from_utf8_lossy(&*out).into_owned(),
error_type: ClapErrorType::MissingArgumentOrSubcommand,
});
}
if matcher.is_empty() && matcher.subcommand_name().is_none() &&
2015-11-09 08:48:49 +00:00
self.settings.is_set(&AppSettings::ArgRequiredElseHelp) {
let mut out = vec![];
try!(self.write_help(&mut out));
return Err(ClapError {
error: String::from_utf8_lossy(&*out).into_owned(),
error_type: ClapErrorType::MissingArgumentOrSubcommand,
});
}
Ok(())
}
// basically re-implements ClapError::exit except it checks if we should wait
// for input before
// exiting since ClapError doesn't have that info and the error message must be
// printed before
// exiting
fn maybe_wait_for_exit(&self, e: ClapError) -> ! {
if e.use_stderr() {
wlnerr!("{}", e.error);
if self.settings.is_set(&AppSettings::WaitOnError) {
wlnerr!("\nPress [ENTER] / [RETURN] to continue...");
let mut s = String::new();
let i = io::stdin();
i.lock().read_line(&mut s).unwrap();
}
process::exit(1);
}
e.exit()
}
// Prints the version to the user and exits if quit=true
fn print_version<W: Write>(&self, w: &mut W) -> ClapResult<()> {
// Print the binary name if existing, but replace all spaces with hyphens in
// case we're
// dealing with subcommands i.e. git mv is translated to git-mv
try!(writeln!(w,
"{} {}",
&self.bin_name.as_ref().unwrap_or(&self.name)[..].replace(" ", "-"),
self.version.unwrap_or("")));
w.flush().map_err(ClapError::from)
}
#[doc(hidden)]
pub fn create_current_usage(&self, matcher: &ArgMatcher) -> ClapResult<String> {
self.create_usage(&matcher.arg_names()
2015-11-09 08:48:49 +00:00
.filter(|k| {
if let Some(o) = self.opts
.iter()
.filter(|o| &&o.name == k)
.next() {
!o.settings.is_set(&ArgSettings::Required)
} else if let Some(p) = self.positionals
.values()
.filter(|p| &&p.name == k)
.next() {
!p.settings.is_set(&ArgSettings::Required)
} else {
true
}
})
.map(|k| *k)
.collect::<Vec<_>>())
}
fn groups_for_arg(&self, name: &str) -> Option<Vec<&'ar str>> {
if self.groups.is_empty() {
return None;
}
let mut res = vec![];
for (g_name, grp) in &self.groups {
for a in &grp.args {
if a == &name {
res.push(*g_name);
}
}
}
if res.is_empty() {
return None;
}
Some(res)
}
fn args_in_group(&self, group: &str) -> Vec<String> {
let mut g_vec = vec![];
let mut args = vec![];
for n in &self.groups.get(group).unwrap().args {
if let Some(f) = self.flags.iter().filter(|f| &f.name == n).next() {
args.push(f.to_string());
} else if let Some(f) = self.opts.iter().filter(|o| &o.name == n).next() {
args.push(f.to_string());
} else if self.groups.contains_key(n) {
g_vec.push(*n);
} else {
if let Some(p) = self.positionals
.values()
.filter(|p| &p.name == n)
.next() {
args.push(p.to_string());
}
}
}
if !g_vec.is_empty() {
for av in g_vec.iter().map(|g| self.args_in_group(g)) {
for a in av {
args.push(a);
}
}
}
assert!(!args.is_empty(),
"ArgGroup '{}' doesn't contain any args",
group);
args.dedup();
args.iter().map(ToOwned::to_owned).collect()
}
fn arg_names_in_group(&self, group: &'ar str) -> Vec<&'ar str> {
let mut g_vec = vec![];
let mut args = vec![];
for n in &self.groups.get(group).unwrap().args {
if self.flags.iter().any(|f| &f.name == n) {
args.push(*n);
} else if self.opts.iter().any(|o| &o.name == n) {
args.push(*n);
} else if self.groups.contains_key(n) {
g_vec.push(*n);
} else if self.positionals.values().any(|p| &p.name == n) {
args.push(*n);
}
}
if !g_vec.is_empty() {
for av in g_vec.iter().map(|g| self.arg_names_in_group(g)) {
for a in av {
args.push(a);
}
}
}
assert!(!args.is_empty(),
"ArgGroup '{}' doesn't contain any args",
group);
args.dedup();
args.iter().map(|s| *s).collect()
}
fn get_required_from(&self,
reqs: &[&'ar str],
matcher: Option<&ArgMatcher>)
2015-11-09 08:48:49 +00:00
-> VecDeque<String> {
let mut c_flags = vec![];
let mut c_pos = vec![];
let mut c_opt = vec![];
let mut grps = vec![];
for name in reqs {
if self.flags.iter().any(|f| &f.name == name) {
c_flags.push(name);
} else if self.opts.iter().any(|o| &o.name == name) {
c_opt.push(name);
} else if self.groups.contains_key(name) {
grps.push(*name);
} else {
c_pos.push(name);
}
}
let mut tmp_f = vec![];
for f in &c_flags {
if let Some(f) = self.flags.iter().filter(|flg| &&flg.name == f).next() {
if let Some(ref rl) = f.requires {
for r in rl.iter() {
if !reqs.contains(r) {
if self.flags.iter().any(|f| &f.name == r) {
tmp_f.push(r);
} else if self.opts.iter().any(|o| &o.name == r) {
c_opt.push(r);
} else if self.groups.contains_key(r) {
grps.push(*r);
} else {
c_pos.push(r);
}
}
}
}
}
}
for f in tmp_f.into_iter() {
c_flags.push(f);
}
let mut tmp_o = vec![];
for f in &c_opt {
if let Some(f) = self.opts.iter().filter(|o| &&o.name == f).next() {
if let Some(ref rl) = f.requires {
for r in rl.iter() {
if !reqs.contains(r) {
if self.flags.iter().any(|f| &f.name == r) {
c_flags.push(r);
} else if self.opts.iter().any(|o| &o.name == r) {
tmp_o.push(r);
} else if self.groups.contains_key(r) {
grps.push(*r);
} else {
c_pos.push(r);
}
}
}
}
}
}
for f in tmp_o.into_iter() {
c_opt.push(f);
}
let mut tmp_p = vec![];
for p in c_pos.iter() {
if let Some(p) = self.positionals.values().filter(|pos| &&pos.name == p).next() {
if let Some(ref rl) = p.requires {
for r in rl.iter() {
if !reqs.contains(r) {
if self.flags.iter().any(|f| &f.name == r) {
c_flags.push(r);
} else if self.opts.iter().any(|o| &o.name == r) {
c_opt.push(r);
} else if self.groups.contains_key(r) {
grps.push(*r);
} else {
tmp_p.push(r);
}
}
}
}
}
}
for f in tmp_p.into_iter() {
c_pos.push(f);
}
let mut ret_val = VecDeque::new();
let mut pmap = BTreeMap::new();
for p in c_pos.into_iter() {
if matcher.is_some() && matcher.as_ref().unwrap().contains(p) {
2015-11-09 08:48:49 +00:00
continue;
}
if let Some(p) = self.positionals.values().filter(|x| &x.name == p).next() {
pmap.insert(p.index, format!("{}", p));
}
}
for (_, s) in pmap {
ret_val.push_back(s);
}
for f in c_flags.into_iter() {
if matcher.is_some() && matcher.as_ref().unwrap().contains(f) {
2015-11-09 08:48:49 +00:00
continue;
}
ret_val.push_back(format!("{}",
self.flags
.iter()
.filter(|flg| &flg.name == f)
.next()
.unwrap()));
}
for o in c_opt.into_iter() {
if matcher.is_some() && matcher.as_ref().unwrap().contains(o) {
2015-11-09 08:48:49 +00:00
continue;
}
ret_val.push_back(format!("{}",
self.opts
.iter()
.filter(|opt| &opt.name == o)
.next()
.unwrap()));
}
for g in grps.into_iter() {
let g_string = self.args_in_group(g)
.iter()
.fold(String::new(), |acc, s| acc + &format!(" {} |", s)[..]);
ret_val.push_back(format!("[{}]", &g_string[..g_string.len() - 1]));
}
ret_val
}
fn verify_positionals(&mut self) {
// Because you must wait until all arguments have been supplied, this is the first chance
// to make assertions on positional argument indexes
//
// Firt we verify that the index highest supplied index, is equal to the number of
// positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3
// but no 2)
if let Some((idx, ref p)) = self.positionals.iter().rev().next() {
if idx != self.positionals.len() {
panic!("Found positional argument \"{}\" who's index is {} but there are only {} \
positional arguments defined",
p.name,
idx,
self.positionals.len());
}
}
// Next we verify that only the highest index has a .multiple(true) (if any)
assert!(!self.positionals
.values()
.any(|a| {
a.settings.is_set(&ArgSettings::Multiple) &&
(a.index as usize != self.positionals.len())
}),
"Only the positional argument with the highest index may accept multiple values");
// If it's required we also need to ensure all previous positionals are
// required too
let mut found = false;
for p in self.positionals.values().rev() {
if !found {
if p.settings.is_set(&ArgSettings::Required) {
found = true;
continue;
}
} else {
assert!(p.settings.is_set(&ArgSettings::Required),
"Found positional argument which is not required with a lower index than a \
required positional argument: {:?} index {}",
p.name,
p.index);
}
}
}
fn propogate_globals(&mut self) {
for sc in self.subcommands.iter_mut() {
// We have to create a new scope in order to tell rustc the borrow of `sc` is
// done and
// to recursively call this method
{
for a in self.global_args.iter() {
sc.add_arg(a.into());
}
}
sc.propogate_globals();
}
}
fn blacklisted_from(&self, name: &str, matcher: &ArgMatcher) -> Option<String> {
for k in matcher.arg_names() {
2015-11-09 08:48:49 +00:00
if let Some(f) = self.flags.iter().filter(|f| &f.name == k).next() {
if let Some(ref bl) = f.blacklist {
if bl.contains(&name) {
return Some(format!("{}", f));
}
}
}
if let Some(o) = self.opts.iter().filter(|o| &o.name == k).next() {
if let Some(ref bl) = o.blacklist {
if bl.contains(&name) {
return Some(format!("{}", o));
}
}
}
if let Some(pos) = self.positionals.values().filter(|p| &p.name == k).next() {
if let Some(ref bl) = pos.blacklist {
if bl.contains(&name) {
return Some(format!("{}", pos));
}
}
}
}
None
}
fn overriden_from(&self, name: &'ar str, matcher: &ArgMatcher) -> Option<&'ar str> {
for k in matcher.arg_names() {
2015-11-09 08:48:49 +00:00
if let Some(f) = self.flags.iter().filter(|f| &f.name == k).next() {
if let Some(ref bl) = f.overrides {
if bl.contains(&name) {
return Some(f.name);
}
}
}
if let Some(o) = self.opts.iter().filter(|o| &o.name == k).next() {
if let Some(ref bl) = o.overrides {
if bl.contains(&name) {
return Some(o.name);
}
}
}
if let Some(pos) = self.positionals.values().filter(|p| &p.name == k).next() {
if let Some(ref bl) = pos.overrides {
if bl.contains(&name) {
return Some(pos.name);
}
}
}
}
None
}
fn create_help_and_version(&mut self) {
// name is "hclap_help" because flags are sorted by name
if !self.flags.iter().any(|a| a.long.is_some() && a.long.unwrap() == "help") {
if self.help_short.is_none() && !self.short_list.contains(&'h') {
self.help_short = Some('h');
}
let arg = FlagBuilder {
name: "hclap_help",
short: self.help_short,
long: Some("help"),
help: Some("Prints help information"),
blacklist: None,
requires: None,
overrides: None,
settings: ArgFlags::new(),
};
self.long_list.push("help");
self.flags.push(arg);
}
if !self.settings.is_set(&AppSettings::DisableVersion) &&
!self.flags.iter().any(|a| a.long.is_some() && a.long.unwrap() == "version") {
if self.version_short.is_none() && !self.short_list.contains(&'V') {
self.version_short = Some('V');
}
// name is "vclap_version" because flags are sorted by name
let arg = FlagBuilder {
name: "vclap_version",
short: self.version_short,
long: Some("version"),
help: Some("Prints version information"),
blacklist: None,
requires: None,
overrides: None,
settings: ArgFlags::new(),
};
self.long_list.push("version");
self.flags.push(arg);
}
if !self.subcommands.is_empty() &&
!self.subcommands
.iter()
.any(|a| a.name_slice == "help") {
self.subcommands.push(App::new("help").about("Prints this message"));
}
}
fn check_for_help_and_version_str(&self, arg: &str) -> ClapResult<()> {
if arg == "help" && self.settings.is_set(&AppSettings::NeedsLongHelp) {
try!(self._help());
2015-11-09 08:48:49 +00:00
}
if arg == "version" && self.settings.is_set(&AppSettings::NeedsLongVersion) {
try!(self._version());
2015-11-09 08:48:49 +00:00
}
Ok(())
}
fn check_for_help_and_version_char(&self, arg: char) -> ClapResult<()> {
if let Some(h) = self.help_short { if arg == h { try!(self._help()); } }
if let Some(v) = self.version_short { if arg == v { try!(self._version()); } }
Ok(())
}
2015-11-09 08:48:49 +00:00
fn _help(&self) -> ClapResult<()> {
try!(self.print_help());
Err(ClapError {
error: String::new(),
error_type: ClapErrorType::HelpDisplayed,
})
}
2015-11-09 08:48:49 +00:00
fn _version(&self) -> ClapResult<()> {
let out = io::stdout();
let mut buf_w = BufWriter::new(out.lock());
try!(self.print_version(&mut buf_w));
Err(ClapError {
error: String::new(),
error_type: ClapErrorType::VersionDisplayed,
})
}
2015-11-09 08:48:49 +00:00
fn parse_long_arg<'av>(&mut self,
matcher: &mut ArgMatcher<'ar>,
full_arg: &'av str)
-> ClapResult<Option<&'ar str>> {
let mut val = "";
let arg = if full_arg.contains("=") {
let parts: Vec<_> = full_arg.trim_left_matches(|c| c == '-').splitn(2, "=").collect();
val = parts[1];
parts[0]
} else {
full_arg.trim_left_matches(|c| c == '-')
};
2015-11-09 08:48:49 +00:00
if let Some(opt) = self.opts
.iter()
.filter(|v| v.long.is_some() && v.long.unwrap() == arg)
.next() {
let ret = try!(self.parse_opt(val, opt, matcher));
arg_post_processing!(self, opt, matcher);
2015-11-09 08:48:49 +00:00
return Ok(ret);
} else if let Some(flag) = self.flags
.iter()
.filter(|v| v.long.is_some() && v.long.unwrap() == arg)
.next() {
// Only flags could be help or version, and we need to check the raw long
// so this is the first point to check
try!(self.check_for_help_and_version_str(arg));
2015-11-09 08:48:49 +00:00
try!(self.parse_flag(flag, matcher));
2015-11-09 08:48:49 +00:00
// Handle conflicts, requirements, etc.
arg_post_processing!(self, flag, matcher);
2015-11-09 08:48:49 +00:00
return Ok(None);
2015-11-09 08:48:49 +00:00
}
self.did_you_mean_error(arg, matcher).map(|_| None)
}
2015-11-09 08:48:49 +00:00
fn parse_short_arg(&mut self,
matcher: &mut ArgMatcher<'ar>,
full_arg: &str)
-> ClapResult<Option<&'ar str>> {
let arg = &*full_arg.trim_left_matches(|c| c == '-');
for c in arg.chars() {
// Check for matching short options, and return the name if there is no trailing
// concatenated value: -oval
// Option: -o
// Value: val
if let Some(opt) = self.opts
.iter()
.filter(|&v| v.short.is_some() && v.short.unwrap() == c)
.next() {
// Check for trailing concatenated value
let val = arg.splitn(2, c).collect::<Vec<_>>()[1];
// Default to "we're expecting a value later"
let ret = try!(self.parse_opt(val, opt, matcher));
arg_post_processing!(self, opt, matcher);
2015-11-09 08:48:49 +00:00
return Ok(ret);
2015-11-09 08:48:49 +00:00
}
try!(self.check_for_help_and_version_char(c));
if let Some(flag) = self.flags.iter().filter(|&v| v.short.is_some() && v.short.unwrap() == c).next() {
// Only flags can be help or version
try!(self.parse_flag(flag, matcher));
// Handle conflicts, requirements, overrides, etc.
// Must be called here due to mutablilty
arg_post_processing!(self, flag, matcher);
2015-11-09 08:48:49 +00:00
}
}
Ok(None)
}
2015-11-09 08:48:49 +00:00
fn parse_opt(&self, val: &str, opt: &OptBuilder<'ar>, matcher: &mut ArgMatcher<'ar>) -> ClapResult<Option<&'ar str>> {
try!(self.validate_arg(opt, matcher));
2015-11-09 08:48:49 +00:00
if matcher.contains(opt.name) && !opt.settings.is_set(&ArgSettings::Multiple) {
// Not the first time, but we don't allow multiples
return Err(error_builder::UnexpectedMultipleUsage(
&*opt.to_string(),
&*try!(self.create_current_usage(matcher))));
}
2015-11-09 08:48:49 +00:00
if val.len() != 0 {
if opt.is_set(ArgSettings::EmptyValues) {
try!(self.add_val_to_arg(opt, val, matcher));
return Ok(None);
}
return Err(error_builder::EmptyValue(
&*opt.to_string(),
&*try!(self.create_current_usage(matcher))));
}
2015-11-09 08:48:49 +00:00
// If it doesn't allow mutliples, (or a specific number of values), or we don't have any
// values yet, we want to return the name of this arg so the next arg is parsed as a value
// otherwise we're done getting values
if opt.settings.is_set(&ArgSettings::Multiple) || opt.num_vals.is_some() || !matcher.contains(opt.name) {
return Ok(Some(opt.name));
2015-11-09 08:48:49 +00:00
}
Ok(None)
}
2015-11-09 08:48:49 +00:00
fn add_val_to_arg<A>(&self,
arg: &A,
val: &str,
matcher: &mut ArgMatcher<'ar>)
-> ClapResult<Option<&'ar str>>
where A: AnyArg<'ar> + Display {
matcher.add_val_to(arg.name(), val.to_owned());
// Increment or create the group "args"
if let Some(grps) = self.groups_for_arg(arg.name()) {
for grp in grps {
matcher.add_val_to(grp, val.to_owned());
2015-11-09 08:48:49 +00:00
}
}
// The validation must come AFTER inserting into 'matcher' or the usage string
// can't be built
self.validate_value(arg, val, matcher)
2015-11-09 08:48:49 +00:00
}
fn validate_value<A>(&self, arg: &A, val: &str, matcher: &ArgMatcher<'ar>) -> ClapResult<Option<&'ar str>>
where A: AnyArg<'ar> + Display {
if let Some(ref p_vals) = arg.possible_vals() {
if !p_vals.contains(&val) {
2015-11-09 08:48:49 +00:00
return Err(
error_builder::InvalidValue(val,
2015-11-09 08:48:49 +00:00
p_vals,
&arg.to_string(),
&*try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
}
if !arg.is_set(ArgSettings::EmptyValues) &&
val.is_empty() &&
matcher.contains(arg.name()) {
return Err(error_builder::EmptyValue(&*arg.to_string(),
&*try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
if let Some(ref vtor) = arg.validator() {
if let Err(e) = vtor(val.to_owned()) {
2015-11-09 08:48:49 +00:00
return Err(error_builder::ValueValidationError(&*e));
}
}
let vals = matcher.get(arg.name())
.expect(INTERNAL_ERROR_MSG)
.values.as_ref()
.expect(INTERNAL_ERROR_MSG)
.len();
if let Some(max) = arg.max_vals() {
if (vals as u8) < max {
return Ok(Some(arg.name()));
} else {
return Ok(None);
2015-11-09 08:48:49 +00:00
}
}
if let Some(..) = arg.min_vals() {
return Ok(Some(arg.name()));
}
if let Some(num) = arg.num_vals() {
if arg.is_set(ArgSettings::Multiple) {
if (vals as u8) < num {
return Ok(Some(arg.name()));
}
} else {
if (vals as u8 % num) != 0 {
return Ok(Some(arg.name()));
2015-11-09 08:48:49 +00:00
}
}
}
if arg.is_set(ArgSettings::Multiple) {
return Ok(Some(arg.name()));
}
2015-11-09 08:48:49 +00:00
Ok(None)
}
fn parse_flag(&self, flag: &FlagBuilder<'ar>, matcher: &mut ArgMatcher<'ar>) -> ClapResult<()> {
// Validate that we can actually accept this arg
try!(self.validate_arg(flag, matcher));
2015-11-09 08:48:49 +00:00
// First occurrence or not?
if !matcher.contains(flag.name) {
// If this is the first, then add this flag itself
matcher.insert(flag.name);
} else if !flag.settings.is_set(&ArgSettings::Multiple) {
// Not the first time, but we don't allow multiples
return Err(error_builder::UnexpectedMultipleUsage(
&*flag.to_string(),
&*try!(self.create_current_usage(matcher))));
} else {
matcher.inc_occurrence_of(flag.name);
}
2015-11-09 08:48:49 +00:00
// Increment or create the group "args"
self.groups_for_arg(flag.name).and_then(|vec| Some(matcher.inc_occurrences_of(&vec)));
2015-11-09 08:48:49 +00:00
Ok(())
2015-11-09 08:48:49 +00:00
}
fn validate_arg<A>(&self, arg: &A, matcher: &mut ArgMatcher<'ar>) -> ClapResult<()>
where A: AnyArg<'ar> + Display {
// May not be needed since we can't get here without being an Arg of some type
//
// if arg.has_switch() && (!self.flags.iter().any(|f| f.name == arg.name()) &&
// !self.opts.iter().any(|f| f.name == arg.name())) {
// return Err(error_builder::InvalidArgument(
// &*format!("{}", arg),
// None,
// &*try!(self.create_current_usage(matcher))));
// }
// Ensure this arg isn't on the mutually excludes list
if self.blacklist.contains(&arg.name()) {
matcher.remove(arg.name());
return Err(error_builder::ArgumentConflict(
arg.to_string(),
self.blacklisted_from(arg.name(), &matcher),
try!(self.create_current_usage(matcher))));
}
// Make sure this isn't one being added multiple times if it doesn't support it
if matcher.contains(arg.name()) && !arg.is_set(ArgSettings::Multiple) {
return Err(error_builder::UnexpectedMultipleUsage(
&*format!("{}", arg),
&*try!(self.create_current_usage(matcher))));
}
Ok(())
}
fn validate_blacklist(&self, matcher: &mut ArgMatcher<'ar>) -> ClapResult<()> {
2015-11-09 08:48:49 +00:00
for name in self.blacklist.iter() {
if matcher.contains(name) {
matcher.remove(name);
2015-11-09 08:48:49 +00:00
return Err(error_builder::ArgumentConflict(
format!("{}", Format::Warning(
if let Some(f) = self.flags.iter().filter(|f| &f.name == name).next() {
f.to_string()
} else if let Some(o) = self.opts.iter()
.filter(|o| &o.name == name)
.next() {
o.to_string()
} else {
match self.positionals.values()
.filter(|p| p.name == *name)
.next() {
Some(p) => p.to_string(),
None => format!("'{}'", name)
}
}
)),
self.blacklisted_from(name, &matcher),
try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
} else if self.groups.contains_key(name) {
for n in self.arg_names_in_group(name) {
if matcher.contains(n) {
matcher.remove(n);
2015-11-09 08:48:49 +00:00
return Err(error_builder::ArgumentConflict(
format!("{}", Format::Warning(
if let Some(f) = self.flags.iter()
.filter(|f| &f.name == name)
.next() {
f.to_string()
} else if let Some(o) = self.opts
.iter()
.filter(|o| &o.name == name)
.next() {
o.to_string()
} else {
match self.positionals.values()
.filter(|p| p.name == n)
.next() {
Some(p) => p.to_string(),
None => format!("'{}'", name)
}
}
)),
self.blacklisted_from(name, &matcher),
try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
}
}
}
Ok(())
}
fn validate_num_args(&self, matcher: &mut ArgMatcher<'ar>) -> ClapResult<()> {
for (name, ma) in matcher.iter() {
2015-11-09 08:48:49 +00:00
if self.groups.contains_key(name) {
continue;
} else if let Some(ref vals) = ma.values {
if let Some(f) = self.opts
.iter()
.filter(|o| &o.name == name)
.next() {
if let Some(num) = f.num_vals {
let should_err = if f.settings.is_set(&ArgSettings::Multiple) {
((vals.len() as u8) % num) != 0
} else {
num != (vals.len() as u8)
};
if should_err {
return Err(error_builder::WrongNumValues(
&*f.to_string(),
num,
if f.settings.is_set(&ArgSettings::Multiple) {
(vals.len() % num as usize)
} else {
vals.len()
},
if vals.len() == 1 ||
(f.settings.is_set(&ArgSettings::Multiple) &&
(vals.len() % num as usize) == 1) {
"as"
} else {
"ere"
},
&*try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
}
if let Some(num) = f.max_vals {
if (vals.len() as u8) > num {
return Err(error_builder::TooManyValues(
vals.get(&vals.keys()
2015-11-09 08:48:49 +00:00
.last()
.expect(INTERNAL_ERROR_MSG))
.expect(INTERNAL_ERROR_MSG),
2015-11-09 08:48:49 +00:00
&f.to_string(),
&try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
}
if let Some(num) = f.min_vals {
if (vals.len() as u8) < num {
return Err(error_builder::TooFewValues(
&*f.to_string(),
num,
vals.len(),
&*try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
}
} else if let Some(f) = self.positionals
.values()
.filter(|p| &p.name == name)
.next() {
if let Some(num) = f.num_vals {
if num != vals.len() as u8 {
return Err(error_builder::WrongNumValues(
&*f.to_string(),
num,
vals.len(),
if vals.len() == 1 {
"as"
} else {
"ere"
},
&*try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
}
if let Some(max) = f.max_vals {
if (vals.len() as u8) > max {
return Err(error_builder::TooManyValues(
vals.get(&vals.keys()
2015-11-09 08:48:49 +00:00
.last()
.expect(INTERNAL_ERROR_MSG))
.expect(INTERNAL_ERROR_MSG),
2015-11-09 08:48:49 +00:00
&f.to_string(),
&try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
}
if let Some(min) = f.min_vals {
if (vals.len() as u8) < min {
return Err(error_builder::TooFewValues(
&*f.to_string(),
min,
vals.len(),
&*try!(self.create_current_usage(matcher))));
2015-11-09 08:48:49 +00:00
}
}
}
}
}
Ok(())
}
fn validate_required(&self, matcher: &ArgMatcher<'ar>) -> ClapResult<()> {
2015-11-09 08:48:49 +00:00
'outer: for name in self.required.iter() {
if matcher.contains(name) {
2015-11-09 08:48:49 +00:00
continue 'outer;
}
if let Some(grp) = self.groups.get(name) {
for arg in &grp.args {
if matcher.contains(arg) { continue 'outer; }
2015-11-09 08:48:49 +00:00
}
}
if self.groups.values().any(|g| g.args.contains(name)) {
continue 'outer;
}
2015-11-09 08:48:49 +00:00
if let Some(a) = self.flags.iter().filter(|f| &f.name == name).next() {
if self._validate_blacklist_required(a, matcher) { continue 'outer; }
2015-11-09 08:48:49 +00:00
}
if let Some(a) = self.opts.iter().filter(|o| &o.name == name).next() {
if self._validate_blacklist_required(a, matcher) { continue 'outer; }
2015-11-09 08:48:49 +00:00
}
if let Some(a) = self.positionals.values().filter(|p| &p.name == name).next() {
if self._validate_blacklist_required(a, matcher) { continue 'outer; }
}
return Err(error_builder::MissingRequiredArgument(
&*self.get_required_from(&self.required, Some(matcher))
.iter()
.fold(String::new(),
|acc, s| acc + &format!("\n\t{}", Format::Error(s))[..]),
&*try!(self.create_current_usage(matcher))));
}
Ok(())
}
fn _validate_blacklist_required<A>(&self, a: &A, matcher: &ArgMatcher) -> bool where A: AnyArg<'ar> {
if let Some(bl) = a.blacklist() {
for n in bl.iter() {
if matcher.contains(n) {
return true;
} else if self.groups
.get(n)
.map(|g| g.args.iter().any(|an| matcher.contains(an)))
.unwrap_or(false) {
return true;
2015-11-09 08:48:49 +00:00
}
}
}
false
}
fn did_you_mean_error(&self, arg: &str, matcher: &mut ArgMatcher<'ar>) -> ClapResult<()> {
// Didn't match a flag or option...maybe it was a typo and close to one
let suffix = suggestions::did_you_mean_suffix(arg,
self.long_list.iter(),
suggestions::DidYouMeanMessageStyle::LongFlag);
// Add the arg to the matches to build a proper usage string
if let Some(name) = suffix.1 {
if let Some(opt) = self.opts
.iter()
.filter(|o| o.long.is_some() && o.long.unwrap() == name)
.next() {
self.groups_for_arg(opt.name).and_then(|grps| Some(matcher.inc_occurrences_of(&grps)));
matcher.insert(opt.name);
} else if let Some(flg) = self.flags
.iter()
.filter(|f| f.long.is_some() && f.long.unwrap() == name)
.next() {
self.groups_for_arg(flg.name).and_then(|grps| Some(matcher.inc_occurrences_of(&grps)));
matcher.insert(flg.name);
}
}
Err(error_builder::InvalidArgument(&*format!("--{}", arg),
Some(&*suffix.0),
&*try!(self.create_current_usage(matcher))))
}
2015-11-09 08:48:49 +00:00
}