clap/src/app/parser.rs

1893 lines
76 KiB
Rust
Raw Normal View History

use std::collections::{BTreeMap, HashMap, VecDeque};
use std::slice::Iter;
use std::io::{self, BufWriter, Write};
use std::ffi::{OsStr, OsString};
use std::fmt::Display;
#[cfg(feature = "debug")]
use std::os::unix::ffi::OsStrExt;
use vec_map::{self, VecMap};
use app::help::Help;
use app::App;
2016-05-06 21:35:53 +00:00
use args::{Arg, ArgGroup, FlagBuilder, OptBuilder, PosBuilder};
use app::settings::{AppFlags, AppSettings};
use args::{AnyArg, ArgMatcher};
2016-03-09 22:41:02 +00:00
use args::settings::ArgSettings;
2016-05-06 21:35:53 +00:00
use errors::{Error, ErrorKind};
use errors::Result as ClapResult;
2016-01-26 19:02:10 +00:00
use INVALID_UTF8;
use suggestions;
use INTERNAL_ERROR_MSG;
use SubCommand;
use fmt::{Format, ColorWhen};
use osstringext::OsStrExt2;
use app::meta::AppMeta;
use args::MatchedArg;
use shell::Shell;
use completions::ComplGen;
use std::fs::File;
use std::path::PathBuf;
#[allow(missing_debug_implementations)]
2016-01-26 19:02:10 +00:00
#[doc(hidden)]
2016-05-06 21:35:53 +00:00
pub struct Parser<'a, 'b>
where 'a: 'b
{
required: Vec<&'b str>,
pub short_list: Vec<char>,
pub long_list: Vec<&'b str>,
blacklist: Vec<&'b str>,
// A list of possible flags
pub flags: Vec<FlagBuilder<'a, 'b>>,
// A list of possible options
pub opts: Vec<OptBuilder<'a, 'b>>,
// A list of positional arguments
pub positionals: VecMap<PosBuilder<'a, 'b>>,
// A list of subcommands
#[doc(hidden)]
pub subcommands: Vec<App<'a, 'b>>,
groups: HashMap<&'a str, ArgGroup<'a>>,
global_args: Vec<Arg<'a, 'b>>,
overrides: Vec<&'b str>,
help_short: Option<char>,
version_short: Option<char>,
settings: AppFlags,
pub g_settings: Vec<AppSettings>,
pub meta: AppMeta<'b>,
trailing_vals: bool,
}
impl<'a, 'b> Default for Parser<'a, 'b> {
fn default() -> Self {
Parser {
flags: vec![],
opts: vec![],
positionals: VecMap::new(),
subcommands: vec![],
help_short: None,
version_short: None,
required: vec![],
short_list: vec![],
long_list: vec![],
blacklist: vec![],
groups: HashMap::new(),
global_args: vec![],
overrides: vec![],
g_settings: vec![],
settings: AppFlags::new(),
meta: AppMeta::new(),
trailing_vals: false,
}
}
}
2016-05-06 21:35:53 +00:00
impl<'a, 'b> Parser<'a, 'b>
where 'a: 'b
{
pub fn with_name(n: String) -> Self {
Parser { meta: AppMeta::with_name(n), ..Default::default() }
}
2016-01-23 11:46:16 +00:00
pub fn help_short(&mut self, s: &str) {
self.help_short = s.trim_left_matches(|c| c == '-')
.chars()
.nth(0);
}
pub fn version_short(&mut self, s: &str) {
self.version_short = s.trim_left_matches(|c| c == '-')
.chars()
.nth(0);
}
pub fn gen_completions_to<W: Write>(&mut self, for_shell: Shell, buf: &mut W) {
self.propogate_help_version();
self.build_bin_names();
ComplGen::new(self).generate(for_shell, buf)
}
pub fn gen_completions(&mut self, for_shell: Shell, od: OsString) {
use std::error::Error;
let out_dir = PathBuf::from(od);
let suffix = match for_shell {
Issues rollup (#637) * feat: adds App::with_defaults to automatically use crate_authors! and crate_version! macros One can now use ```rust let a = App::with_defaults("My Program"); // same as let a2 = App::new("My Program") .version(crate_version!()) .author(crate_authors!()); ``` Closes #600 * imp(YAML Errors): vastly improves error messages when using YAML When errors are made while developing, the panic error messages have been improved instead of relying on the default panic message which is extremely unhelpful. Closes #574 * imp(Completions): uses standard conventions for bash completion files, namely '{bin}.bash-completion' Closes #567 * imp(Help): automatically moves help text to the next line and wraps when term width is determined to be too small, or help text is too long Now `clap` will check if it should automatically place long help messages on the next line after the flag/option. This is determined by checking to see if the space taken by flag/option plus spaces and values doesn't leave enough room for the entirety of the help message, with the single exception of of if the flag/option/spaces/values is less than 25% of the width. Closes #597 * tests: updates help tests to new forced new line rules * fix(Groups): fixes some usage strings that contain both args in groups and ones that conflict with each other Args that conflict *and* are in a group will now only display in the group and not in the usage string itself. Closes #616 * chore: updates dep graph Closes #633 * chore: clippy run * style: changes debug header to match other Rust projects * chore: increase version
2016-08-28 03:42:31 +00:00
Shell::Bash => ".bash-completion",
Shell::Fish => ".fish",
};
let mut file = match File::create(out_dir.join(format!("{}{}", &*self.meta.bin_name.as_ref().unwrap(), suffix))) {
Err(why) => panic!("couldn't create bash completion file: {}",
why.description()),
Ok(file) => file,
};
self.gen_completions_to(for_shell, &mut file)
}
// actually adds the arguments
pub fn add_arg(&mut self, a: &Arg<'a, 'b>) {
2016-05-06 21:35:53 +00:00
debug_assert!(!(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)),
format!("Non-unique argument name: {} is already in use", a.name));
if let Some(ref grps) = a.group {
for g in grps {
let ag = self.groups.entry(g).or_insert_with(|| ArgGroup::with_name(g));
ag.args.push(a.name);
}
}
if let Some(s) = a.short {
debug_assert!(!self.short_list.contains(&s),
2016-05-06 21:35:53 +00:00
format!("Argument short must be unique\n\n\t-{} is already in use",
s));
2016-01-26 19:02:10 +00:00
self.short_list.push(s);
}
if let Some(l) = a.long {
debug_assert!(!self.long_list.contains(&l),
2016-05-06 21:35:53 +00:00
format!("Argument long must be unique\n\n\t--{} is already in use",
l));
2016-01-26 19:02:10 +00:00
self.long_list.push(l);
if l == "help" {
self.unset(AppSettings::NeedsLongHelp);
} else if l == "version" {
self.unset(AppSettings::NeedsLongVersion);
}
}
if a.is_set(ArgSettings::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
};
debug_assert!(!self.positionals.contains_key(i),
2016-05-06 21:35:53 +00:00
format!("Argument \"{}\" has the same index as another positional \
argument\n\n\tPerhaps try .multiple(true) to allow one positional argument \
2016-05-06 21:35:53 +00:00
to take multiple values",
a.name));
2016-06-08 04:10:56 +00:00
let pb = PosBuilder::from_arg(a, i as u64, &mut self.required);
self.positionals.insert(i, pb);
} else if a.is_set(ArgSettings::TakesValue) {
2016-06-08 04:10:56 +00:00
let mut ob = OptBuilder::from_arg(a, &mut self.required);
if self.settings.is_set(AppSettings::DeriveDisplayOrder) && a.disp_ord == 999 {
ob.disp_ord = if self.settings.is_set(AppSettings::UnifiedHelpMessage) {
self.flags.len() + self.opts.len()
} else {
self.opts.len()
};
}
self.opts.push(ob);
} else {
let mut fb = FlagBuilder::from(a);
if self.settings.is_set(AppSettings::DeriveDisplayOrder) && a.disp_ord == 999 {
fb.disp_ord = if self.settings.is_set(AppSettings::UnifiedHelpMessage) {
self.flags.len() + self.opts.len()
} else {
self.flags.len()
};
}
self.flags.push(fb);
}
if a.is_set(ArgSettings::Global) {
debug_assert!(!a.is_set(ArgSettings::Required),
2016-05-06 21:35:53 +00:00
format!("Global arguments cannot be required.\n\n\t'{}' is marked as \
global and required",
a.name));
self.global_args.push(a.into());
}
}
pub fn add_group(&mut self, group: ArgGroup<'a>) {
if group.required {
self.required.push(group.name.into());
if let Some(ref reqs) = group.requires {
2016-02-14 06:10:44 +00:00
self.required.extend_from_slice(reqs);
}
if let Some(ref bl) = group.conflicts {
2016-02-14 06:10:44 +00:00
self.blacklist.extend_from_slice(bl);
}
}
let mut found = false;
if let Some(ref mut grp) = self.groups.get_mut(&group.name) {
2016-02-14 06:10:44 +00:00
grp.args.extend_from_slice(&group.args);
grp.requires = group.requires.clone();
grp.conflicts = group.conflicts.clone();
grp.required = group.required;
found = true;
}
if !found {
self.groups.insert(group.name.into(), group);
}
}
pub fn add_subcommand(&mut self, mut subcmd: App<'a, 'b>) {
debugln!("fn=Parser::add_subcommand;");
debugln!("Term widnth...{:?}", self.meta.term_w);
subcmd.p.meta.term_w = self.meta.term_w;
debug!("Is help...");
if subcmd.p.meta.name == "help" {
sdebugln!("Yes");
self.settings.set(AppSettings::NeedsSubcommandHelp);
2016-05-06 21:35:53 +00:00
} else {
sdebugln!("No");
}
debug!("Using Setting VersionlessSubcommands...");
if self.settings.is_set(AppSettings::VersionlessSubcommands) {
sdebugln!("Yes");
subcmd.p.settings.set(AppSettings::DisableVersion);
2016-05-06 21:35:53 +00:00
} else {
sdebugln!("No");
}
debug!("Using Setting GlobalVersion...");
if self.settings.is_set(AppSettings::GlobalVersion) && subcmd.p.meta.version.is_none() &&
self.meta.version.is_some() {
sdebugln!("Yes");
subcmd = subcmd.setting(AppSettings::GlobalVersion)
.version(self.meta.version.unwrap());
2016-05-06 21:35:53 +00:00
} else {
sdebugln!("No");
}
if self.settings.is_set(AppSettings::DeriveDisplayOrder) {
subcmd.p.meta.disp_ord = self.subcommands.len();
}
for s in &self.g_settings {
subcmd.p.set(*s);
subcmd.p.g_settings.push(*s);
}
self.subcommands.push(subcmd);
}
pub fn required(&self) -> Iter<&str> {
self.required.iter()
}
2016-02-14 06:10:44 +00:00
#[cfg_attr(feature = "lints", allow(for_kv_map))]
pub fn get_required_from(&self,
2016-05-06 21:35:53 +00:00
reqs: &[&'a str],
matcher: Option<&ArgMatcher<'a>>)
-> VecDeque<String> {
let mut c_flags: Vec<&str> = vec![];
let mut c_pos: Vec<&str> = vec![];
let mut c_opt: Vec<&str> = vec![];
let mut grps: Vec<&str> = 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);
}
}
macro_rules! fill_vecs {
($_self:ident {
$t1:ident => $v1:ident => $i1:ident,
$t2:ident => $v2:ident => $i2:ident,
$t3:ident => $v3:ident => $i3:ident,
$gv:ident, $tmp:ident
}) => {
for a in &$v1 {
if let Some(a) = self.$t1.$i1().filter(|arg| &arg.name == a).next() {
if let Some(ref rl) = a.requires {
for r in rl {
if !reqs.contains(r) {
if $_self.$t1.$i1().any(|t| &t.name == r) {
$tmp.push(*r);
} else if $_self.$t2.$i2().any(|t| &t.name == r) {
$v2.push(r);
} else if $_self.$t3.$i3().any(|t| &t.name == r) {
$v3.push(r);
} else if $_self.groups.contains_key(r) {
$gv.push(r);
}
}
}
}
}
}
$v1.extend(&$tmp);
};
}
let mut tmp = vec![];
fill_vecs!(self {
flags => c_flags => iter,
opts => c_opt => iter,
positionals => c_pos => values,
grps, tmp
});
tmp.clear();
fill_vecs!(self {
opts => c_opt => iter,
flags => c_flags => iter,
positionals => c_pos => values,
grps, tmp
});
tmp.clear();
fill_vecs!(self {
positionals => c_pos => values,
opts => c_opt => iter,
flags => c_flags => iter,
grps, tmp
});
let mut ret_val = VecDeque::new();
c_pos.dedup();
c_flags.dedup();
c_opt.dedup();
grps.dedup();
let mut args_in_groups = vec![];
2016-06-08 04:10:56 +00:00
for g in &grps {
for a in self.arg_names_in_group(g).into_iter() {
args_in_groups.push(a);
}
}
let mut pmap = BTreeMap::new();
for p in c_pos.into_iter() {
if matcher.is_some() && matcher.as_ref().unwrap().contains(p) {
continue;
}
if let Some(p) = self.positionals.values().filter(|x| &x.name == &p).next() {
if args_in_groups.contains(&p.name) {
continue;
}
pmap.insert(p.index, p.to_string());
}
}
debugln!("args_in_groups={:?}", args_in_groups);
for (_, s) in pmap {
2016-06-08 04:10:56 +00:00
if (!args_in_groups.is_empty()) && (args_in_groups.contains(&&*s)) {
continue;
}
2016-06-08 04:10:56 +00:00
ret_val.push_back(s);
}
macro_rules! write_arg {
Issues rollup (#637) * feat: adds App::with_defaults to automatically use crate_authors! and crate_version! macros One can now use ```rust let a = App::with_defaults("My Program"); // same as let a2 = App::new("My Program") .version(crate_version!()) .author(crate_authors!()); ``` Closes #600 * imp(YAML Errors): vastly improves error messages when using YAML When errors are made while developing, the panic error messages have been improved instead of relying on the default panic message which is extremely unhelpful. Closes #574 * imp(Completions): uses standard conventions for bash completion files, namely '{bin}.bash-completion' Closes #567 * imp(Help): automatically moves help text to the next line and wraps when term width is determined to be too small, or help text is too long Now `clap` will check if it should automatically place long help messages on the next line after the flag/option. This is determined by checking to see if the space taken by flag/option plus spaces and values doesn't leave enough room for the entirety of the help message, with the single exception of of if the flag/option/spaces/values is less than 25% of the width. Closes #597 * tests: updates help tests to new forced new line rules * fix(Groups): fixes some usage strings that contain both args in groups and ones that conflict with each other Args that conflict *and* are in a group will now only display in the group and not in the usage string itself. Closes #616 * chore: updates dep graph Closes #633 * chore: clippy run * style: changes debug header to match other Rust projects * chore: increase version
2016-08-28 03:42:31 +00:00
($i:expr, $m:ident, $v:ident, $r:ident, $aig:ident) => {
for f in $v.into_iter() {
Issues rollup (#637) * feat: adds App::with_defaults to automatically use crate_authors! and crate_version! macros One can now use ```rust let a = App::with_defaults("My Program"); // same as let a2 = App::new("My Program") .version(crate_version!()) .author(crate_authors!()); ``` Closes #600 * imp(YAML Errors): vastly improves error messages when using YAML When errors are made while developing, the panic error messages have been improved instead of relying on the default panic message which is extremely unhelpful. Closes #574 * imp(Completions): uses standard conventions for bash completion files, namely '{bin}.bash-completion' Closes #567 * imp(Help): automatically moves help text to the next line and wraps when term width is determined to be too small, or help text is too long Now `clap` will check if it should automatically place long help messages on the next line after the flag/option. This is determined by checking to see if the space taken by flag/option plus spaces and values doesn't leave enough room for the entirety of the help message, with the single exception of of if the flag/option/spaces/values is less than 25% of the width. Closes #597 * tests: updates help tests to new forced new line rules * fix(Groups): fixes some usage strings that contain both args in groups and ones that conflict with each other Args that conflict *and* are in a group will now only display in the group and not in the usage string itself. Closes #616 * chore: updates dep graph Closes #633 * chore: clippy run * style: changes debug header to match other Rust projects * chore: increase version
2016-08-28 03:42:31 +00:00
if $m.is_some() && $m.as_ref().unwrap().contains(f) || $aig.contains(&f) {
continue;
}
2016-02-22 23:36:57 +00:00
$r.push_back($i.filter(|flg| &flg.name == &f).next().unwrap().to_string());
}
}
}
Issues rollup (#637) * feat: adds App::with_defaults to automatically use crate_authors! and crate_version! macros One can now use ```rust let a = App::with_defaults("My Program"); // same as let a2 = App::new("My Program") .version(crate_version!()) .author(crate_authors!()); ``` Closes #600 * imp(YAML Errors): vastly improves error messages when using YAML When errors are made while developing, the panic error messages have been improved instead of relying on the default panic message which is extremely unhelpful. Closes #574 * imp(Completions): uses standard conventions for bash completion files, namely '{bin}.bash-completion' Closes #567 * imp(Help): automatically moves help text to the next line and wraps when term width is determined to be too small, or help text is too long Now `clap` will check if it should automatically place long help messages on the next line after the flag/option. This is determined by checking to see if the space taken by flag/option plus spaces and values doesn't leave enough room for the entirety of the help message, with the single exception of of if the flag/option/spaces/values is less than 25% of the width. Closes #597 * tests: updates help tests to new forced new line rules * fix(Groups): fixes some usage strings that contain both args in groups and ones that conflict with each other Args that conflict *and* are in a group will now only display in the group and not in the usage string itself. Closes #616 * chore: updates dep graph Closes #633 * chore: clippy run * style: changes debug header to match other Rust projects * chore: increase version
2016-08-28 03:42:31 +00:00
write_arg!(self.flags.iter(), matcher, c_flags, ret_val, args_in_groups);
write_arg!(self.opts.iter(), matcher, c_opt, ret_val, args_in_groups);
let mut g_vec = vec![];
for g in grps.into_iter() {
let g_string = self.args_in_group(g)
.join("|");
g_vec.push(format!("<{}>", &g_string[..g_string.len()]));
}
g_vec.dedup();
for g in g_vec.into_iter() {
ret_val.push_back(g);
}
ret_val
}
pub fn get_args_tag(&self) -> Option<String> {
let mut count = 0;
'outer: for p in self.positionals.values().filter(|p| !p.is_set(ArgSettings::Required)) {
if let Some(g_vec) = self.groups_for_arg(p.name) {
for grp_s in &g_vec {
debugln!("iter;grp_s={};", grp_s);
if let Some(grp) = self.groups.get(grp_s) {
debug!("Is group required...");
if grp.required {
sdebugln!("Yes (continuing)");
continue 'outer;
} else {
sdebugln!("No (breaking)");
}
}
}
debugln!("Arg not required...");
count +=1;
} else {
debugln!("Arg not required...");
count +=1;
}
}
2016-07-26 00:56:22 +00:00
if count > 1 || self.positionals.len() > 1 {
return None;
} else if count == 1 {
let p = self.positionals.values().next().expect(INTERNAL_ERROR_MSG);
return Some(format!(" [{}]{}", p.name_no_brackets(), p.multiple_str()));
}
Some("".into())
}
pub fn needs_flags_tag(&self) -> bool {
debugln!("exec=needs_flags_tag;");
'outer: for f in &self.flags {
debugln!("iter;f={};", f.name);
if let Some(l) = f.long {
if l == "help" || l == "version" {
continue;
}
}
if let Some(g_vec) = self.groups_for_arg(f.name) {
for grp_s in &g_vec {
debugln!("iter;grp_s={};", grp_s);
if let Some(grp) = self.groups.get(grp_s) {
debug!("Is group required...");
if grp.required {
sdebugln!("Yes (continuing)");
continue 'outer;
} else {
sdebugln!("No (breaking)");
}
}
}
debugln!("Flag not required...(returning true)");
return true;
} else {
debugln!("Flag not required...(returning true)");
return true;
}
}
false
}
pub fn has_opts(&self) -> bool {
!self.opts.is_empty()
}
pub fn has_flags(&self) -> bool {
!self.flags.is_empty()
}
pub fn has_positionals(&self) -> bool {
!self.positionals.is_empty()
}
pub fn has_subcommands(&self) -> bool {
!self.subcommands.is_empty()
}
pub fn is_set(&self, s: AppSettings) -> bool {
self.settings.is_set(s)
}
pub fn set(&mut self, s: AppSettings) {
self.settings.set(s)
}
pub fn unset(&mut self, s: AppSettings) {
self.settings.unset(s)
}
pub 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)
2016-08-20 21:33:58 +00:00
if let Some((idx, p)) = self.positionals.iter().rev().next() {
2016-05-06 21:59:52 +00:00
debug_assert!(!(idx != self.positionals.len()),
2016-05-06 21:35:53 +00:00
format!("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)
2016-05-06 21:59:52 +00:00
debug_assert!(!self.positionals
2016-05-06 21:35:53 +00:00
.values()
2016-05-06 21:59:52 +00:00
.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() {
2016-03-08 15:39:43 +00:00
if found {
debug_assert!(p.settings.is_set(ArgSettings::Required),
2016-05-06 21:35:53 +00:00
"Found positional argument which is not required with a lower index \
than a required positional argument: {:?} index {}",
p.name,
p.index);
2016-03-08 15:39:43 +00:00
} else if p.settings.is_set(ArgSettings::Required) {
found = true;
continue;
}
}
}
pub fn propogate_globals(&mut self) {
2016-01-23 11:46:16 +00:00
for sc in &mut self.subcommands {
// We have to create a new scope in order to tell rustc the borrow of `sc` is
// done and to recursively call this method
{
2016-01-23 11:46:16 +00:00
for a in &self.global_args {
sc.p.add_arg(a);
}
}
sc.p.propogate_globals();
}
}
// The actual parsing function
#[cfg_attr(feature = "lints", allow(while_let_on_iterator))]
pub fn get_matches_with<I, T>(&mut self,
2016-05-06 21:35:53 +00:00
matcher: &mut ArgMatcher<'a>,
it: &mut I)
-> ClapResult<()>
where I: Iterator<Item = T>,
T: Into<OsString>
{
debugln!("fn=get_matches_with;");
// First we create the `--help` and `--version` arguments and add them if
// necessary
self.create_help_and_version();
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_os = arg.into();
debugln!("Begin parsing '{:?}' ({:?})", arg_os, &*arg_os.as_bytes());
// Is this a new argument, or values from a previous option?
debug!("Starts new arg...");
let starts_new_arg = if arg_os.starts_with(b"-") {
sdebugln!("Yes");
!(arg_os.len_() == 1)
} else {
sdebugln!("No");
false
};
// Has the user already passed '--'?
if !self.trailing_vals {
// Does the arg match a subcommand name, or any of it's aliases (if defined)
let pos_sc = self.subcommands
.iter()
.any(|s| &s.p.meta.name[..] == &*arg_os ||
(s.p.meta.aliases.is_some() &&
s.p.meta.aliases
.as_ref()
.unwrap()
.iter()
.any(|&(a, _)| a == &*arg_os)));
if (!starts_new_arg || self.is_set(AppSettings::AllowLeadingHyphen)) && !pos_sc {
// 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_os, matcher));
// get the next value from the iterator
continue;
}
}
}
if arg_os.starts_with(b"--") {
if arg_os.len_() == 2 {
2016-05-06 21:35:53 +00:00
// The user has passed '--' which means only positional args follow no
// matter what they start with
self.trailing_vals = true;
continue;
}
needs_val_of = try!(self.parse_long_arg(matcher, &arg_os));
if !(needs_val_of.is_none() && self.is_set(AppSettings::AllowLeadingHyphen)) {
continue;
}
} else if arg_os.starts_with(b"-") && arg_os.len_() != 1 {
needs_val_of = try!(self.parse_short_arg(matcher, &arg_os));
2016-01-27 16:28:52 +00:00
if !(needs_val_of.is_none() && self.is_set(AppSettings::AllowLeadingHyphen)) {
continue;
}
}
if pos_sc {
if &*arg_os == "help" &&
self.settings.is_set(AppSettings::NeedsSubcommandHelp) {
let cmds: Vec<OsString> = it.map(|c| c.into()).collect();
let mut help_help = false;
2016-08-20 21:33:58 +00:00
let mut bin_name = self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name).clone();
let mut sc = {
let mut sc: &Parser = self;
for (i, cmd) in cmds.iter().enumerate() {
if &*cmd.to_string_lossy() == "help" { // cmd help help
help_help = true;
}
2016-05-06 21:35:53 +00:00
if let Some(c) = sc.subcommands
.iter()
.filter(|s| &*s.p.meta.name == cmd)
.next()
.map(|sc| &sc.p) {
sc = c;
if i == cmds.len() - 1 {
break;
}
} else if let Some(c) = sc.subcommands
.iter()
.filter(|s|
if let Some(ref als) = s.p
.meta
.aliases {
als.iter()
.any(|&(a, _)| &a == &&*cmd.to_string_lossy())
} else {
false
}
)
.next()
.map(|sc| &sc.p) {
sc = c;
if i == cmds.len() - 1 {
break;
}
} else {
return Err(
Error::unrecognized_subcommand(
cmd.to_string_lossy().into_owned(),
2016-05-06 21:35:53 +00:00
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name),
self.color()));
}
bin_name = format!("{} {}",
bin_name,
&*sc.meta.name);
}
sc.clone()
};
if help_help {
let mut pb = PosBuilder::new("subcommand", 1);
pb.help = Some("The subcommand whose help message to display");
pb.set(ArgSettings::Multiple);
sc.positionals.insert(1, pb);
for s in self.g_settings.clone() {
sc.set(s);
}
} else {
sc.create_help_and_version();
}
if sc.meta.bin_name != self.meta.bin_name {
sc.meta.bin_name = Some(format!("{} {}", bin_name, sc.meta.name));
}
return sc._help();
}
subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned());
break;
2016-05-06 21:35:53 +00:00
} else if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(),
self.subcommands
.iter()
.map(|s| &s.p.meta.name)) {
return Err(Error::invalid_subcommand(arg_os.to_string_lossy().into_owned(),
cdate,
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name),
&*self.create_current_usage(matcher),
self.color()));
}
}
2016-03-04 18:42:22 +00:00
if let Some(p) = self.positionals.get(pos_counter) {
parse_positional!(self, p, arg_os, pos_counter, matcher);
2016-07-26 00:56:22 +00:00
} else if self.settings.is_set(AppSettings::AllowExternalSubcommands) {
// Get external subcommand name
let sc_name = match arg_os.to_str() {
Some(s) => s.to_string(),
None => {
if !self.settings.is_set(AppSettings::StrictUtf8) {
return Err(
Error::invalid_utf8(&*self.create_current_usage(matcher), self.color())
);
}
arg_os.to_string_lossy().into_owned()
}
};
// Collect the external subcommand args
2016-07-26 00:56:22 +00:00
let mut sc_m = ArgMatcher::new();
while let Some(v) = it.next() {
let a = v.into();
if let None = a.to_str() {
if !self.settings.is_set(AppSettings::StrictUtf8) {
return Err(
Error::invalid_utf8(&*self.create_current_usage(matcher), self.color())
);
}
}
sc_m.add_val_to("", &a);
}
2016-07-26 00:56:22 +00:00
matcher.subcommand(SubCommand {
name: sc_name,
2016-07-26 00:56:22 +00:00
matches: sc_m.into(),
});
} else {
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
"",
&*self.create_current_usage(matcher),
self.color()));
}
}
let mut reqs_validated = false;
if let Some(a) = needs_val_of {
if let Some(o) = self.opts.iter().filter(|o| &o.name == &a).next() {
try!(self.validate_required(matcher));
reqs_validated = true;
2016-08-20 21:33:58 +00:00
let should_err = if let Some(v) = matcher.0.args.get(&*o.name) {
v.vals.is_empty() && !(o.min_vals.is_some() && o.min_vals.unwrap() == 0)
} else {
true
};
if should_err {
return Err(Error::empty_value(o, &*self.create_current_usage(matcher), self.color()));
}
} else {
return Err(Error::empty_value(self.positionals
2016-05-06 21:35:53 +00:00
.values()
.filter(|p| &p.name == &a)
.next()
.expect(INTERNAL_ERROR_MSG),
&*self.create_current_usage(matcher),
self.color()));
}
}
try!(self.add_defaults(matcher));
try!(self.validate_blacklist(matcher));
try!(self.validate_num_args(matcher));
matcher.usage(self.create_usage(&[]));
if !(self.settings.is_set(AppSettings::SubcommandsNegateReqs) && subcmd_name.is_some()) &&
2016-05-06 21:35:53 +00:00
!reqs_validated {
try!(self.validate_required(matcher));
}
if let Some(pos_sc_name) = subcmd_name {
// is this is a real subcommand, or an alias
let sc_name = if self.subcommands.iter().any(|sc| sc.p.meta.name == pos_sc_name) {
pos_sc_name
} else {
self.subcommands
.iter()
.filter(|sc| sc.p.meta.aliases.is_some())
.filter_map(|sc| if sc.p.meta.aliases
.as_ref()
.unwrap()
.iter()
.any(|&(a, _)| &a == &&*pos_sc_name) {
Some(sc.p.meta.name.clone())
} else {
None
})
.next()
.expect(INTERNAL_ERROR_MSG)
};
try!(self.parse_subcommand(sc_name, matcher, it));
} else if self.is_set(AppSettings::SubcommandRequired) {
let bn = self.meta.bin_name.as_ref().unwrap_or(&self.meta.name);
return Err(Error::missing_subcommand(bn, &self.create_current_usage(matcher), self.color()));
} else if self.is_set(AppSettings::SubcommandRequiredElseHelp) {
let mut out = vec![];
try!(self.write_help_err(&mut out));
return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
}
if matcher.is_empty() && matcher.subcommand_name().is_none() &&
self.is_set(AppSettings::ArgRequiredElseHelp) {
let mut out = vec![];
try!(self.write_help_err(&mut out));
return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
}
Ok(())
}
fn propogate_help_version(&mut self) {
debugln!("exec=propogate_help_version;");
self.create_help_and_version();
2016-07-26 00:56:22 +00:00
for sc in &mut self.subcommands {
sc.p.propogate_help_version();
}
}
fn build_bin_names(&mut self) {
debugln!("exec=build_bin_names;");
2016-07-26 00:56:22 +00:00
for sc in &mut self.subcommands {
debug!("bin_name set...");
if sc.p.meta.bin_name.is_none() {
sdebugln!("No");
let bin_name = format!("{}{}{}",
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name.clone()),
if self.meta.bin_name.is_some() {
" "
} else {
""
},
&*sc.p.meta.name);
debugln!("Setting bin_name of {} to {}", self.meta.name, bin_name);
sc.p.meta.bin_name = Some(bin_name);
} else {
sdebugln!("yes ({:?})", sc.p.meta.bin_name);
}
debugln!("Calling build_bin_names from...{}", sc.p.meta.name);
sc.p.build_bin_names();
}
}
2016-05-06 21:35:53 +00:00
fn parse_subcommand<I, T>(&mut self,
sc_name: String,
matcher: &mut ArgMatcher<'a>,
it: &mut I)
-> ClapResult<()>
where I: Iterator<Item = T>,
T: Into<OsString>
{
use std::fmt::Write;
debugln!("fn=parse_subcommand;");
let mut mid_string = String::new();
if !self.settings.is_set(AppSettings::SubcommandsNegateReqs) {
let mut hs: Vec<&str> = self.required.iter().map(|n| &**n).collect();
for k in matcher.arg_names() {
hs.push(k);
}
let reqs = self.get_required_from(&hs, Some(matcher));
2016-01-23 11:46:16 +00:00
for s in &reqs {
write!(&mut mid_string, " {}", s).expect(INTERNAL_ERROR_MSG);
}
}
mid_string.push_str(" ");
if let Some(ref mut sc) = self.subcommands
.iter_mut()
.filter(|s| &s.p.meta.name == &sc_name)
.next() {
let mut sc_matcher = ArgMatcher::new();
// bin_name should be parent's bin_name + [<reqs>] + the sc's name separated by
// a space
sc.p.meta.usage = Some(format!("{}{}{}",
2016-05-06 21:35:53 +00:00
self.meta.bin_name.as_ref().unwrap_or(&String::new()),
if self.meta.bin_name.is_some() {
&*mid_string
} else {
""
},
&*sc.p.meta.name));
sc.p.meta.bin_name = Some(format!("{}{}{}",
2016-05-06 21:35:53 +00:00
self.meta
.bin_name
.as_ref()
.unwrap_or(&String::new()),
if self.meta.bin_name.is_some() {
" "
} else {
""
},
&*sc.p.meta.name));
try!(sc.p.get_matches_with(&mut sc_matcher, it));
matcher.subcommand(SubCommand {
name: sc.p.meta.name.clone(),
matches: sc_matcher.into(),
});
}
Ok(())
}
fn blacklisted_from(&self, name: &str, matcher: &ArgMatcher) -> Option<String> {
for k in matcher.arg_names() {
if let Some(f) = self.flags.iter().filter(|f| &f.name == &k).next() {
if let Some(ref bl) = f.blacklist {
if bl.contains(&name) {
2016-02-22 23:36:57 +00:00
return Some(f.to_string());
}
}
}
if let Some(o) = self.opts.iter().filter(|o| &o.name == &k).next() {
if let Some(ref bl) = o.blacklist {
if bl.contains(&name) {
2016-02-22 23:36:57 +00:00
return Some(o.to_string());
}
}
}
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(pos.name.to_owned());
}
}
}
}
None
}
fn overriden_from(&self, name: &str, matcher: &ArgMatcher) -> Option<&'a str> {
for k in matcher.arg_names() {
if let Some(f) = self.flags.iter().filter(|f| &f.name == &k).next() {
if let Some(ref bl) = f.overrides {
if bl.contains(&name.into()) {
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.into()) {
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.into()) {
return Some(pos.name);
}
}
}
}
None
}
fn groups_for_arg(&self, name: &str) -> Option<Vec<&'a str>> {
debugln!("fn=groups_for_arg;");
if self.groups.is_empty() {
debugln!("No groups defined");
return None;
}
let mut res = vec![];
debugln!("Searching through groups...");
for (gn, grp) in &self.groups {
for a in &grp.args {
if a == &name {
sdebugln!("\tFound '{}'", gn);
res.push(*gn);
}
}
}
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);
2016-02-02 10:05:54 +00:00
} else if let Some(p) = self.positionals
2016-05-06 21:35:53 +00:00
.values()
.filter(|p| &p.name == n)
.next() {
args.push(p.name.to_owned());
}
}
2016-02-02 10:05:54 +00:00
for av in g_vec.iter().map(|g| self.args_in_group(g)) {
args.extend(av);
}
args.dedup();
args.iter().map(ToOwned::to_owned).collect()
}
fn arg_names_in_group(&self, group: &str) -> Vec<&'a str> {
let mut g_vec = vec![];
let mut args = vec![];
for n in &self.groups.get(group).unwrap().args {
2016-02-14 06:10:44 +00:00
if self.groups.contains_key(&*n) {
g_vec.push(*n);
2016-02-14 06:10:44 +00:00
} else {
args.push(*n);
}
}
2016-02-02 10:05:54 +00:00
for av in g_vec.iter().map(|g| self.arg_names_in_group(g)) {
args.extend(av);
}
args.dedup();
args.iter().map(|s| *s).collect()
}
pub fn create_help_and_version(&mut self) {
debugln!("fn=create_help_and_version;");
// name is "hclap_help" because flags are sorted by name
if self.is_set(AppSettings::NeedsLongHelp) {
2016-03-09 22:35:40 +00:00
debugln!("Building --help");
if self.help_short.is_none() && !self.short_list.contains(&'h') {
self.help_short = Some('h');
}
let arg = FlagBuilder {
2016-03-09 22:35:40 +00:00
name: "hclap_help",
short: self.help_short,
2016-03-09 22:35:40 +00:00
long: Some("help"),
help: Some("Prints help information"),
..Default::default()
};
if let Some(h) = self.help_short {
self.short_list.push(h);
}
2016-03-09 22:35:40 +00:00
self.long_list.push("help");
self.flags.push(arg);
}
if !self.settings.is_set(AppSettings::DisableVersion) &&
self.is_set(AppSettings::NeedsLongVersion) {
2016-03-09 22:35:40 +00:00
debugln!("Building --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 {
2016-03-09 22:35:40 +00:00
name: "vclap_version",
short: self.version_short,
2016-03-09 22:35:40 +00:00
long: Some("version"),
help: Some("Prints version information"),
..Default::default()
};
if let Some(v) = self.version_short {
self.short_list.push(v);
}
2016-03-09 22:35:40 +00:00
self.long_list.push("version");
self.flags.push(arg);
}
if !self.subcommands.is_empty() && self.is_set(AppSettings::NeedsSubcommandHelp) {
2016-03-09 22:35:40 +00:00
debugln!("Building help");
2016-05-06 21:35:53 +00:00
self.subcommands
.push(App::new("help")
.about("Prints this message or the help of the given subcommand(s)"));
}
}
// Retrieves the names of all args the user has supplied thus far, except required ones
// because those will be listed in self.required
pub fn create_current_usage(&self, matcher: &'b ArgMatcher<'a>) -> String {
2016-05-06 21:35:53 +00:00
self.create_usage(&*matcher.arg_names()
.iter()
.filter(|n| {
if let Some(o) = self.opts
.iter()
.filter(|&o| &&o.name == n)
.next() {
!o.settings.is_set(ArgSettings::Required)
} else if let Some(p) = self.positionals
.values()
.filter(|&p| &&p.name == n)
.next() {
!p.settings.is_set(ArgSettings::Required)
} else {
true // flags can't be required, so they're always true
}
})
.map(|&n| n)
.collect::<Vec<_>>())
}
fn check_for_help_and_version_str(&self, arg: &OsStr) -> ClapResult<()> {
2016-05-06 21:35:53 +00:00
debug!("Checking if --{} is help or version...",
arg.to_str().unwrap());
if arg == "help" && self.settings.is_set(AppSettings::NeedsLongHelp) {
sdebugln!("Help");
try!(self._help());
}
if arg == "version" && self.settings.is_set(AppSettings::NeedsLongVersion) {
sdebugln!("Version");
try!(self._version());
}
sdebugln!("Neither");
Ok(())
}
fn check_for_help_and_version_char(&self, arg: char) -> ClapResult<()> {
debug!("Checking if -{} is help or version...", arg);
if let Some(h) = self.help_short {
sdebugln!("Help");
if arg == h && self.settings.is_set(AppSettings::NeedsLongHelp) {
2016-05-06 21:35:53 +00:00
try!(self._help());
}
}
if let Some(v) = self.version_short {
sdebugln!("Help");
if arg == v && self.settings.is_set(AppSettings::NeedsLongVersion) {
2016-05-06 21:35:53 +00:00
try!(self._version());
}
}
sdebugln!("Neither");
Ok(())
}
fn _help(&self) -> ClapResult<()> {
let mut buf = vec![];
try!(Help::write_parser_help(&mut buf, self));
// let out = io::stdout();
// let mut out_buf = BufWriter::new(out.lock());
// try!(out_buf.write(&*buf));
Err(Error {
message: unsafe { String::from_utf8_unchecked(buf) },
kind: ErrorKind::HelpDisplayed,
info: None,
})
}
fn _version(&self) -> ClapResult<()> {
let out = io::stdout();
let mut buf_w = BufWriter::new(out.lock());
try!(self.print_version(&mut buf_w));
Err(Error {
message: String::new(),
kind: ErrorKind::VersionDisplayed,
info: None,
})
}
fn parse_long_arg(&mut self,
matcher: &mut ArgMatcher<'a>,
full_arg: &OsStr)
2016-05-06 21:35:53 +00:00
-> ClapResult<Option<&'b str>> {
// maybe here lifetime should be 'a
debugln!("fn=parse_long_arg;");
let mut val = None;
debug!("Does it contain '='...");
let arg = if full_arg.contains_byte(b'=') {
let (p0, p1) = full_arg.trim_left_matches(b'-').split_at_byte(b'=');
sdebugln!("Yes '{:?}'", p1);
val = Some(p1);
p0
} else {
sdebugln!("No");
full_arg.trim_left_matches(b'-')
};
if let Some(opt) = self.opts
2016-05-06 21:35:53 +00:00
.iter()
.filter(|v| v.long.is_some() && &*v.long.unwrap() == arg)
.next() {
debugln!("Found valid opt '{}'", opt.to_string());
let ret = try!(self.parse_opt(val, opt, matcher));
arg_post_processing!(self, opt, matcher);
return Ok(ret);
} else if let Some(flag) = self.flags
2016-05-06 21:35:53 +00:00
.iter()
.filter(|v| v.long.is_some() && &*v.long.unwrap() == arg)
.next() {
debugln!("Found valid flag '{}'", flag.to_string());
// 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));
try!(self.parse_flag(flag, matcher));
// Handle conflicts, requirements, etc.
arg_post_processing!(self, flag, matcher);
return Ok(None);
} else if self.is_set(AppSettings::AllowLeadingHyphen) {
return Ok(None);
}
debugln!("Didn't match anything");
self.did_you_mean_error(arg.to_str().expect(INVALID_UTF8), matcher).map(|_| None)
}
fn parse_short_arg(&mut self,
matcher: &mut ArgMatcher<'a>,
full_arg: &OsStr)
-> ClapResult<Option<&'a str>> {
debugln!("fn=parse_short_arg;");
// let mut utf8 = true;
let arg_os = full_arg.trim_left_matches(b'-');
let arg = arg_os.to_string_lossy();
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
2016-05-06 21:35:53 +00:00
.iter()
.filter(|&v| v.short.is_some() && v.short.unwrap() == c)
.next() {
debugln!("Found valid short opt -{} in '{}'", c, arg);
// Check for trailing concatenated value
let p: Vec<_> = arg.splitn(2, c).collect();
2016-05-06 21:35:53 +00:00
debugln!("arg: {:?}, arg_os: {:?}, full_arg: {:?}",
arg,
arg_os,
full_arg);
debugln!("p[0]: {:?}, p[1]: {:?}", p[0].as_bytes(), p[1].as_bytes());
let i = p[0].as_bytes().len() + 1;
let val = if p[1].as_bytes().len() > 0 {
2016-05-06 21:35:53 +00:00
debugln!("setting val: {:?} (bytes), {:?} (ascii)",
arg_os.split_at(i).1.as_bytes(),
arg_os.split_at(i).1);
Some(arg_os.split_at(i).1)
} else {
None
};
// Default to "we're expecting a value later"
let ret = try!(self.parse_opt(val, opt, matcher));
arg_post_processing!(self, opt, matcher);
return Ok(ret);
2016-05-06 21:35:53 +00:00
} else if let Some(flag) = self.flags
.iter()
.filter(|&v| v.short.is_some() && v.short.unwrap() == c)
.next() {
debugln!("Found valid short flag -{}", c);
// Only flags can be help or version
try!(self.check_for_help_and_version_char(c));
try!(self.parse_flag(flag, matcher));
// Handle conflicts, requirements, overrides, etc.
// Must be called here due to mutablilty
arg_post_processing!(self, flag, matcher);
} else if !self.is_set(AppSettings::AllowLeadingHyphen) {
let mut arg = String::new();
arg.push('-');
arg.push(c);
2016-05-06 21:35:53 +00:00
return Err(Error::unknown_argument(&*arg,
"",
&*self.create_current_usage(matcher),
self.color()));
}
}
Ok(None)
}
fn parse_opt(&self,
val: Option<&OsStr>,
opt: &OptBuilder<'a, 'b>,
matcher: &mut ArgMatcher<'a>)
-> ClapResult<Option<&'a str>> {
debugln!("fn=parse_opt;");
validate_multiples!(self, opt, matcher);
let mut has_eq = false;
debug!("Checking for val...");
2016-01-27 16:28:52 +00:00
if let Some(fv) = val {
has_eq = fv.starts_with(&[b'=']);
2016-01-27 16:28:52 +00:00
let v = fv.trim_left_matches(b'=');
if !opt.is_set(ArgSettings::EmptyValues) && v.len_() == 0 {
sdebugln!("Found Empty - Error");
return Err(Error::empty_value(opt, &*self.create_current_usage(matcher), self.color()));
}
2016-03-09 22:35:40 +00:00
sdebugln!("Found - {:?}, len: {}", v, v.len_());
debugln!("{:?} contains '='...{:?}", fv, fv.starts_with(&[b'=']));
try!(self.add_val_to_arg(opt, v, matcher));
2016-05-06 21:35:53 +00:00
} else {
sdebugln!("None");
}
matcher.inc_occurrence_of(opt.name);
// Increment or create the group "args"
self.groups_for_arg(opt.name).and_then(|vec| Some(matcher.inc_occurrences_of(&*vec)));
if val.is_none() || !has_eq && (opt.is_set(ArgSettings::Multiple) && matcher.needs_more_vals(opt)) {
return Ok(Some(opt.name));
}
Ok(None)
}
fn add_val_to_arg<A>(&self,
2016-05-06 21:35:53 +00:00
arg: &A,
val: &OsStr,
matcher: &mut ArgMatcher<'a>)
-> ClapResult<Option<&'a str>>
where A: AnyArg<'a, 'b> + Display
{
debugln!("fn=add_val_to_arg;");
let mut ret = None;
if !(self.trailing_vals && self.is_set(AppSettings::DontDelimitTrailingValues)) {
if let Some(delim) = arg.val_delim() {
if val.is_empty_() {
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
} else {
for v in val.split(delim as u32 as u8) {
ret = try!(self.add_single_val_to_arg(arg, v, matcher));
}
// If there was a delimiter used, we're not looking for more values
feat(Arg): adds new setting `Arg::require_delimiter` which requires val delimiter to parse multiple values Using this setting requires a value delimiter be present in order to parse multiple values. Otherwise it is assumed no values follow, and moves on to the next arg with a clean slate. These examples demonstrate what happens when `require_delimiter(true)` is used. Notice everything works in this first example, as we use a delimiter, as expected. ```rust let delims = App::new("reqdelims") .arg(Arg::with_name("opt") .short("o") .takes_value(true) .multiple(true) .require_delimiter(true)) // Simulate "$ reqdelims -o val1,val2,val3" .get_matches_from(vec![ "reqdelims", "-o", "val1,val2,val3", ]); assert!(delims.is_present("opt")); assert_eq!(delims.values_of("opt").unwrap().collect::<Vec<_>>(), ["val1", "val2", "val3"]); ``` In this next example, we will *not* use a delimiter. Notice it's now an error. ```rust let res = App::new("reqdelims") .arg(Arg::with_name("opt") .short("o") .takes_value(true) .multiple(true) .require_delimiter(true)) // Simulate "$ reqdelims -o val1 val2 val3" .get_matches_from_safe(vec![ "reqdelims", "-o", "val1", "val2", "val3", ]); assert!(res.is_err()); let err = res.unwrap_err(); assert_eq!(err.kind, ErrorKind::UnknownArgument); ``` What's happening is `-o` is getting `val1`, and because delimiters are required yet none were present, it stops parsing `-o`. At this point it reaches `val2` and because no positional arguments have been defined, it's an error of an unexpected argument. In this final example, we contrast the above with `clap`'s default behavior where the above is *not* an error. ```rust let delims = App::new("reqdelims") .arg(Arg::with_name("opt") .short("o") .takes_value(true) .multiple(true)) // Simulate "$ reqdelims -o val1 val2 val3" .get_matches_from(vec![ "reqdelims", "-o", "val1", "val2", "val3", ]); assert!(delims.is_present("opt")); assert_eq!(delims.values_of("opt").unwrap().collect::<Vec<_>>(), ["val1", "val2", "val3"]); ```
2016-06-30 00:28:09 +00:00
if val.contains_byte(delim as u32 as u8) || arg.is_set(ArgSettings::RequireDelimiter) {
ret = None;
}
}
} else {
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
}
} else {
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
}
Ok(ret)
}
2016-05-06 21:35:53 +00:00
fn add_single_val_to_arg<A>(&self,
arg: &A,
v: &OsStr,
matcher: &mut ArgMatcher<'a>)
-> ClapResult<Option<&'a str>>
where A: AnyArg<'a, 'b> + Display
{
debugln!("adding val: {:?}", v);
matcher.add_val_to(arg.name(), v);
// 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, v);
}
}
// The validation must come AFTER inserting into 'matcher' or the usage string
// can't be built
self.validate_value(arg, v, matcher)
}
2016-05-06 21:35:53 +00:00
fn validate_value<A>(&self,
arg: &A,
val: &OsStr,
matcher: &ArgMatcher<'a>)
-> ClapResult<Option<&'a str>>
where A: AnyArg<'a, 'b> + Display
{
debugln!("fn=validate_value; val={:?}", val);
if self.is_set(AppSettings::StrictUtf8) && val.to_str().is_none() {
return Err(Error::invalid_utf8(&*self.create_current_usage(matcher), self.color()));
}
2016-08-20 21:33:58 +00:00
if let Some(p_vals) = arg.possible_vals() {
let val_str = val.to_string_lossy();
if !p_vals.contains(&&*val_str) {
2016-05-06 21:35:53 +00:00
return Err(Error::invalid_value(val_str,
p_vals,
arg,
&*self.create_current_usage(matcher),
self.color()));
}
}
2016-05-06 21:35:53 +00:00
if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty_() &&
matcher.contains(&*arg.name()) {
return Err(Error::empty_value(arg, &*self.create_current_usage(matcher), self.color()));
}
2016-08-20 21:33:58 +00:00
if let Some(vtor) = arg.validator() {
if let Err(e) = vtor(val.to_string_lossy().into_owned()) {
return Err(Error::value_validation(e, self.color()));
}
}
if matcher.needs_more_vals(arg) {
return Ok(Some(arg.name()));
}
Ok(None)
}
2016-05-06 21:35:53 +00:00
fn parse_flag(&self,
flag: &FlagBuilder<'a, 'b>,
matcher: &mut ArgMatcher<'a>)
-> ClapResult<()> {
debugln!("fn=parse_flag;");
validate_multiples!(self, flag, matcher);
matcher.inc_occurrence_of(flag.name);
// Increment or create the group "args"
self.groups_for_arg(flag.name).and_then(|vec| Some(matcher.inc_occurrences_of(&*vec)));
Ok(())
}
fn validate_blacklist(&self, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("fn=validate_blacklist;");
macro_rules! build_err {
($me:ident, $name:expr, $matcher:ident) => ({
debugln!("macro=build_err;");
let c_with = $me.blacklisted_from($name, &$matcher);
debugln!("'{:?}' conflicts with '{}'", c_with, $name);
$matcher.remove($name);
let usg = $me.create_current_usage($matcher);
if let Some(f) = $me.flags.iter().filter(|f| f.name == $name).next() {
debugln!("It was a flag...");
Error::argument_conflict(f, c_with, &*usg, self.color())
} else if let Some(o) = $me.opts.iter()
.filter(|o| o.name == $name)
.next() {
debugln!("It was an option...");
Error::argument_conflict(o, c_with, &*usg, self.color())
} else {
match $me.positionals.values()
.filter(|p| p.name == $name)
.next() {
Some(p) => {
debugln!("It was a positional...");
Error::argument_conflict(p, c_with, &*usg, self.color())
},
None => panic!(INTERNAL_ERROR_MSG)
}
}
});
}
for name in &self.blacklist {
debugln!("Checking blacklisted name: {}", name);
if self.groups.contains_key(name) {
debugln!("groups contains it...");
for n in self.arg_names_in_group(name) {
debugln!("Checking arg '{}' in group...", n);
2016-06-08 04:10:56 +00:00
if matcher.contains(n) {
debugln!("matcher contains it...");
return Err(build_err!(self, n, matcher));
}
}
} else if matcher.contains(name) {
debugln!("matcher contains it...");
return Err(build_err!(self, *name, matcher));
}
}
Ok(())
}
fn validate_num_args(&self, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("fn=validate_num_args;");
for (name, ma) in matcher.iter() {
if self.groups.contains_key(&**name) {
continue;
2016-07-26 00:56:22 +00:00
} else if let Some(opt) = self.opts
2016-05-06 21:35:53 +00:00
.iter()
.filter(|o| &o.name == name)
.next() {
2016-07-26 00:56:22 +00:00
try!(self._validate_num_vals(opt, ma, matcher));
} else if let Some(pos) = self.positionals
.values()
.filter(|p| &p.name == name)
.next() {
try!(self._validate_num_vals(pos, ma, matcher));
}
}
Ok(())
}
fn _validate_num_vals<A>(&self, a: &A, ma: &MatchedArg, matcher: &ArgMatcher) -> ClapResult<()>
where A: AnyArg<'a, 'b> + Display
{
debugln!("fn=_validate_num_vals;");
if let Some(num) = a.num_vals() {
debugln!("num_vals set: {}", num);
let should_err = if a.is_set(ArgSettings::Multiple) {
((ma.vals.len() as u64) % num) != 0
} else {
num != (ma.vals.len() as u64)
};
if should_err {
debugln!("Sending error WrongNumberOfValues");
2016-05-06 21:35:53 +00:00
return Err(Error::wrong_number_of_values(a,
num,
if a.is_set(ArgSettings::Multiple) {
(ma.vals.len() % num as usize)
} else {
ma.vals.len()
},
if ma.vals.len() == 1 ||
(a.is_set(ArgSettings::Multiple) &&
(ma.vals.len() % num as usize) ==
1) {
"as"
} else {
"ere"
},
&*self.create_current_usage(matcher),
self.color()));
}
}
if let Some(num) = a.max_vals() {
debugln!("max_vals set: {}", num);
if (ma.vals.len() as u64) > num {
debugln!("Sending error TooManyValues");
2016-05-06 21:35:53 +00:00
return Err(Error::too_many_values(ma.vals
.get(ma.vals
.keys()
.last()
.expect(INTERNAL_ERROR_MSG))
.expect(INTERNAL_ERROR_MSG)
.to_str()
.expect(INVALID_UTF8),
a,
&*self.create_current_usage(matcher),
self.color()));
}
}
if let Some(num) = a.min_vals() {
debugln!("min_vals set: {}", num);
if (ma.vals.len() as u64) < num {
debugln!("Sending error TooFewValues");
2016-05-06 21:35:53 +00:00
return Err(Error::too_few_values(a,
num,
ma.vals.len(),
&*self.create_current_usage(matcher),
self.color()));
}
}
Ok(())
}
fn validate_required(&self, matcher: &ArgMatcher) -> ClapResult<()> {
2016-01-23 11:46:16 +00:00
'outer: for name in &self.required {
if matcher.contains(name) {
continue 'outer;
}
if let Some(grp) = self.groups.get(name) {
for arg in &grp.args {
2016-05-06 21:35:53 +00:00
if matcher.contains(arg) {
continue 'outer;
}
}
}
if self.groups.values().any(|g| g.args.contains(name)) {
continue 'outer;
}
if let Some(a) = self.flags.iter().filter(|f| &f.name == name).next() {
2016-05-06 21:35:53 +00:00
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
} else if let Some(a) = self.opts.iter().filter(|o| &o.name == name).next() {
2016-05-06 21:35:53 +00:00
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
} else if let Some(a) = self.positionals.values().filter(|p| &p.name == name).next() {
2016-05-06 21:35:53 +00:00
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
}
2016-05-06 21:35:53 +00:00
let err = if self.settings.is_set(AppSettings::ArgRequiredElseHelp) &&
matcher.is_empty() {
self._help().unwrap_err()
} else {
let mut reqs = self.required.iter().map(|&r| &*r).collect::<Vec<_>>();
reqs.retain(|n| !matcher.contains(n));
reqs.dedup();
Error::missing_required_argument(
&*self.get_required_from(&*reqs, Some(matcher))
.iter()
.fold(String::new(),
|acc, s| acc + &format!("\n {}", Format::Error(s))[..]),
&*self.create_current_usage(matcher),
self.color())
};
return Err(err);
}
Ok(())
}
2016-05-06 21:35:53 +00:00
fn is_missing_required_ok<A>(&self, a: &A, matcher: &ArgMatcher) -> bool
where A: AnyArg<'a, 'b>
{
if let Some(bl) = a.blacklist() {
for n in bl.iter() {
2016-05-06 21:35:53 +00:00
if matcher.contains(n) ||
self.groups
.get(n)
.map_or(false, |g| g.args.iter().any(|an| matcher.contains(an))) {
return true;
}
}
} else if let Some(ru) = a.required_unless() {
let mut found_any = false;
for n in ru.iter() {
2016-05-06 21:35:53 +00:00
if matcher.contains(n) ||
self.groups
.get(n)
.map_or(false, |g| g.args.iter().any(|an| matcher.contains(an))) {
if !a.is_set(ArgSettings::RequiredUnlessAll) {
return true;
}
found_any = true;
} else if a.is_set(ArgSettings::RequiredUnlessAll) {
return false;
}
}
return found_any;
}
false
}
fn did_you_mean_error(&self, arg: &str, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
// Didn't match a flag or option...maybe it was a typo and close to one
2016-05-06 21:35:53 +00:00
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
2016-05-06 21:35:53 +00:00
.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)));
2016-02-14 06:10:44 +00:00
matcher.insert(&*opt.name);
} else if let Some(flg) = self.flags
2016-05-06 21:35:53 +00:00
.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)));
2016-02-14 06:10:44 +00:00
matcher.insert(&*flg.name);
}
}
2016-01-25 05:28:52 +00:00
let used_arg = format!("--{}", arg);
Err(Error::unknown_argument(&*used_arg, &*suffix.0, &*self.create_current_usage(matcher), self.color()))
}
// 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)
pub fn create_usage(&self, used: &[&str]) -> String {
debugln!("fn=create_usage;");
let mut usage = String::with_capacity(75);
usage.push_str("USAGE:\n ");
2016-06-08 04:10:56 +00:00
usage.push_str(&self.create_usage_no_title(used));
usage
}
// Creates a usage string (*without title*) 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)
pub fn create_usage_no_title(&self, used: &[&str]) -> String {
debugln!("fn=create_usage_no_title;");
let mut usage = String::with_capacity(75);
if let Some(u) = self.meta.usage_str {
usage.push_str(&*u);
2016-03-08 15:39:43 +00:00
} else if used.is_empty() {
2016-05-06 21:35:53 +00:00
usage.push_str(&*self.meta
.usage
.as_ref()
2016-05-06 21:35:53 +00:00
.unwrap_or(self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name)));
let mut reqs: Vec<&str> = self.required().map(|r| &**r).collect();
reqs.dedup();
let req_string = self.get_required_from(&reqs, None)
.iter()
.fold(String::new(), |a, s| a + &format!(" {}", s)[..]);
let flags = self.needs_flags_tag();
if flags && !self.is_set(AppSettings::UnifiedHelpMessage) {
usage.push_str(" [FLAGS]");
} else if flags {
usage.push_str(" [OPTIONS]");
}
2016-05-06 21:35:53 +00:00
if !self.is_set(AppSettings::UnifiedHelpMessage) && self.has_opts() &&
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
2016-05-06 21:35:53 +00:00
if self.has_positionals() &&
self.opts.iter().any(|a| a.settings.is_set(ArgSettings::Multiple)) &&
self.positionals.values().any(|a| !a.settings.is_set(ArgSettings::Required)) &&
!self.has_subcommands() {
usage.push_str(" [--]")
}
2016-05-06 21:35:53 +00:00
if self.has_positionals() &&
self.positionals.values().any(|a| !a.settings.is_set(ArgSettings::Required)) {
if let Some(args_tag) = self.get_args_tag() {
usage.push_str(&*args_tag);
} else {
usage.push_str(" [ARGS]");
}
}
if self.has_subcommands() && !self.is_set(AppSettings::SubcommandRequired) {
usage.push_str(" [SUBCOMMAND]");
} else if self.is_set(AppSettings::SubcommandRequired) && self.has_subcommands() {
usage.push_str(" <SUBCOMMAND>");
}
2016-03-08 15:39:43 +00:00
} else {
self.smart_usage(&mut usage, used);
}
usage.shrink_to_fit();
usage
}
// Creates a context aware usage string, or "smart usage" from currently used
// args, and requirements
fn smart_usage(&self, usage: &mut String, used: &[&str]) {
debugln!("fn=smart_usage;");
let mut hs: Vec<&str> = self.required().map(|s| &**s).collect();
2016-02-14 06:10:44 +00:00
hs.extend_from_slice(used);
let r_string = self.get_required_from(&hs, None)
2016-05-06 21:35:53 +00:00
.iter()
.fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]);
usage.push_str(&self.meta
.usage
.as_ref()
.unwrap_or(self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta
.name))[..]);
usage.push_str(&*r_string);
if self.is_set(AppSettings::SubcommandRequired) {
usage.push_str(" <SUBCOMMAND>");
}
}
// Prints the version to the user and exits if quit=true
fn print_version<W: Write>(&self, w: &mut W) -> ClapResult<()> {
try!(self.write_version(w));
w.flush().map_err(Error::from)
}
pub fn write_version<W: Write>(&self, w: &mut W) -> io::Result<()> {
if let Some(bn) = self.meta.bin_name.as_ref() {
2016-02-19 04:56:44 +00:00
if bn.contains(' ') {
// Incase we're dealing with subcommands i.e. git mv is translated to git-mv
write!(w,
2016-05-06 21:35:53 +00:00
"{} {}",
bn.replace(" ", "-"),
self.meta.version.unwrap_or("".into()))
} else {
write!(w,
2016-05-06 21:35:53 +00:00
"{} {}",
&self.meta.name[..],
self.meta.version.unwrap_or("".into()))
}
} else {
write!(w,
2016-05-06 21:35:53 +00:00
"{} {}",
&self.meta.name[..],
self.meta.version.unwrap_or("".into()))
}
}
pub fn print_help(&self) -> ClapResult<()> {
let out = io::stdout();
let mut buf_w = BufWriter::new(out.lock());
self.write_help(&mut buf_w)
}
pub fn write_help<W: Write>(&self, w: &mut W) -> ClapResult<()> {
2016-06-08 04:10:56 +00:00
Help::write_parser_help(w, self)
}
pub fn write_help_err<W: Write>(&self, w: &mut W) -> ClapResult<()> {
2016-06-08 04:10:56 +00:00
Help::write_parser_help_to_stderr(w, self)
}
fn add_defaults(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
macro_rules! add_val {
($_self:ident, $a:ident, $m:ident) => {
if $m.get($a.name).is_none() {
2016-05-06 21:35:53 +00:00
try!($_self.add_val_to_arg($a, OsStr::new($a.default_val
.as_ref()
.unwrap()), $m));
arg_post_processing!($_self, $a, $m);
}
};
}
for o in self.opts.iter().filter(|o| o.default_val.is_some()) {
add_val!(self, o, matcher);
}
for p in self.positionals.values().filter(|p| p.default_val.is_some()) {
add_val!(self, p, matcher);
}
Ok(())
}
pub fn iter_flags(&self) -> Iter<FlagBuilder> {
self.flags.iter()
}
pub fn iter_opts(&self) -> Iter<OptBuilder> {
self.opts.iter()
}
pub fn iter_positionals(&self) -> vec_map::Values<PosBuilder> {
self.positionals.values()
}
// Should we color the output? None=determined by output location, true=yes, false=no
#[doc(hidden)]
pub fn color(&self) -> ColorWhen {
debugln!("exec=color;");
debug!("Color setting...");
if self.is_set(AppSettings::ColorNever) {
sdebugln!("Never");
ColorWhen::Never
} else if self.is_set(AppSettings::ColorAlways) {
sdebugln!("Always");
ColorWhen::Always
} else {
sdebugln!("Auto");
ColorWhen::Auto
}
}
}
2016-05-06 21:35:53 +00:00
impl<'a, 'b> Clone for Parser<'a, 'b>
where 'a: 'b
{
fn clone(&self) -> Self {
Parser {
required: self.required.clone(),
short_list: self.short_list.clone(),
long_list: self.long_list.clone(),
blacklist: self.blacklist.clone(),
flags: self.flags.clone(),
opts: self.opts.clone(),
positionals: self.positionals.clone(),
subcommands: self.subcommands.clone(),
groups: self.groups.clone(),
global_args: self.global_args.clone(),
overrides: self.overrides.clone(),
2016-03-23 01:15:11 +00:00
help_short: self.help_short,
version_short: self.version_short,
settings: self.settings.clone(),
g_settings: self.g_settings.clone(),
meta: self.meta.clone(),
trailing_vals: self.trailing_vals,
}
}
}