clap/src/app/parser.rs

1890 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 {
Shell::Bash => "_bash.sh",
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 {
($i:expr, $m:ident, $v:ident, $r:ident) => {
for f in $v.into_iter() {
if $m.is_some() && $m.as_ref().unwrap().contains(f) {
continue;
}
2016-02-22 23:36:57 +00:00
$r.push_back($i.filter(|flg| &flg.name == &f).next().unwrap().to_string());
}
}
}
write_arg!(self.flags.iter(), matcher, c_flags, ret_val);
write_arg!(self.opts.iter(), matcher, c_opt, ret_val);
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)
if let Some((idx, ref 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;
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()));
}
}
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!("{}{}{}",
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name.clone()),
if self.meta.bin_name.is_some() {
" "
} else {
""
},
&*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;
let should_err = if let Some(ref 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<()> {
try!(self.print_help());
Err(Error {
message: String::new(),
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()));
}
if let Some(ref 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()));
}
if let Some(ref 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.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,
}
}
}