mirror of
https://github.com/clap-rs/clap
synced 2025-01-05 17:28:42 +00:00
1859 lines
75 KiB
Rust
1859 lines
75 KiB
Rust
// Std
|
|
use std::ffi::{OsStr, OsString};
|
|
use std::fmt::Display;
|
|
use std::fs::File;
|
|
use std::io::{self, BufWriter, Write};
|
|
#[cfg(feature = "debug")]
|
|
use std::os::unix::ffi::OsStrExt;
|
|
use std::path::PathBuf;
|
|
use std::slice::Iter;
|
|
use std::iter::Peekable;
|
|
|
|
// Third Party
|
|
use vec_map::{self, VecMap};
|
|
|
|
// Internal
|
|
use INTERNAL_ERROR_MSG;
|
|
use INVALID_UTF8;
|
|
use SubCommand;
|
|
use app::App;
|
|
use app::help::Help;
|
|
use app::meta::AppMeta;
|
|
use app::settings::AppFlags;
|
|
use args::{AnyArg, ArgMatcher, Base, Switched, Arg, ArgGroup, FlagBuilder, OptBuilder, PosBuilder};
|
|
use args::settings::ArgSettings;
|
|
use completions::ComplGen;
|
|
use errors::{Error, ErrorKind};
|
|
use errors::Result as ClapResult;
|
|
use fmt::ColorWhen;
|
|
use osstringext::OsStrExt2;
|
|
use completions::Shell;
|
|
use suggestions;
|
|
use app::settings::AppSettings as AS;
|
|
use app::validator::Validator;
|
|
use app::usage;
|
|
|
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
|
#[doc(hidden)]
|
|
pub enum ParseResult<'a> {
|
|
Flag,
|
|
Opt(&'a str),
|
|
Pos(&'a str),
|
|
MaybeHyphenValue,
|
|
MaybeNegNum,
|
|
NotFound,
|
|
ValuesDone,
|
|
}
|
|
|
|
#[allow(missing_debug_implementations)]
|
|
#[doc(hidden)]
|
|
#[derive(Clone, Default)]
|
|
pub struct Parser<'a, 'b>
|
|
where 'a: 'b
|
|
{
|
|
pub meta: AppMeta<'b>,
|
|
settings: AppFlags,
|
|
pub g_settings: AppFlags,
|
|
pub flags: Vec<FlagBuilder<'a, 'b>>,
|
|
pub opts: Vec<OptBuilder<'a, 'b>>,
|
|
pub positionals: VecMap<PosBuilder<'a, 'b>>,
|
|
pub subcommands: Vec<App<'a, 'b>>,
|
|
pub groups: Vec<ArgGroup<'a>>,
|
|
pub global_args: Vec<Arg<'a, 'b>>,
|
|
pub required: Vec<&'a str>,
|
|
pub r_ifs: Vec<(&'a str, &'b str, &'a str)>,
|
|
pub blacklist: Vec<&'b str>,
|
|
pub overrides: Vec<&'b str>,
|
|
help_short: Option<char>,
|
|
version_short: Option<char>,
|
|
cache: Option<&'a str>,
|
|
pub help_message: Option<&'a str>,
|
|
pub version_message: Option<&'a str>,
|
|
}
|
|
|
|
impl<'a, 'b> Parser<'a, 'b>
|
|
where 'a: 'b
|
|
{
|
|
pub fn with_name(n: String) -> Self {
|
|
Parser {
|
|
meta: AppMeta::with_name(n),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
pub fn help_short(&mut self, s: &str) {
|
|
let c = s.trim_left_matches(|c| c == '-')
|
|
.chars()
|
|
.nth(0)
|
|
.unwrap_or('h');
|
|
self.help_short = Some(c);
|
|
}
|
|
|
|
pub fn version_short(&mut self, s: &str) {
|
|
let c = s.trim_left_matches(|c| c == '-')
|
|
.chars()
|
|
.nth(0)
|
|
.unwrap_or('V');
|
|
self.version_short = Some(c);
|
|
}
|
|
|
|
pub fn gen_completions_to<W: Write>(&mut self, for_shell: Shell, buf: &mut W) {
|
|
if !self.is_set(AS::Propogated) {
|
|
self.propogate_help_version();
|
|
self.build_bin_names();
|
|
self.propogate_globals();
|
|
self.propogate_settings();
|
|
self.set(AS::Propogated);
|
|
}
|
|
|
|
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 name = &*self.meta.bin_name.as_ref().unwrap().clone();
|
|
let file_name = match for_shell {
|
|
Shell::Bash => format!("{}.bash-completion", name),
|
|
Shell::Fish => format!("{}.fish", name),
|
|
Shell::Zsh => format!("_{}", name),
|
|
Shell::PowerShell => format!("_{}.ps1", name),
|
|
};
|
|
|
|
let mut file = match File::create(out_dir.join(file_name)) {
|
|
Err(why) => panic!("couldn't create completion file: {}", why.description()),
|
|
Ok(file) => file,
|
|
};
|
|
self.gen_completions_to(for_shell, &mut file)
|
|
}
|
|
|
|
#[inline]
|
|
fn app_debug_asserts(&mut self) -> bool {
|
|
assert!(self.verify_positionals());
|
|
let should_err = self.groups
|
|
.iter()
|
|
.all(|g| {
|
|
g.args
|
|
.iter()
|
|
.all(|arg| {
|
|
(self.flags.iter().any(|f| &f.b.name == arg) ||
|
|
self.opts.iter().any(|o| &o.b.name == arg) ||
|
|
self.positionals.values().any(|p| &p.b.name == arg) ||
|
|
self.groups.iter().any(|g| &g.name == arg))
|
|
})
|
|
});
|
|
let g = self.groups
|
|
.iter()
|
|
.find(|g| {
|
|
g.args
|
|
.iter()
|
|
.any(|arg| {
|
|
!(self.flags.iter().any(|f| &f.b.name == arg) ||
|
|
self.opts.iter().any(|o| &o.b.name == arg) ||
|
|
self.positionals.values().any(|p| &p.b.name == arg) ||
|
|
self.groups.iter().any(|g| &g.name == arg))
|
|
})
|
|
});
|
|
assert!(should_err,
|
|
"The group '{}' contains the arg '{}' that doesn't actually exist.",
|
|
g.unwrap().name,
|
|
g.unwrap()
|
|
.args
|
|
.iter()
|
|
.find(|arg| {
|
|
!(self.flags.iter().any(|f| &&f.b.name == arg) ||
|
|
self.opts.iter().any(|o| &&o.b.name == arg) ||
|
|
self.positionals.values().any(|p| &&p.b.name == arg) ||
|
|
self.groups.iter().any(|g| &&g.name == arg))
|
|
})
|
|
.unwrap());
|
|
true
|
|
}
|
|
|
|
#[inline]
|
|
fn debug_asserts(&self, a: &Arg) -> bool {
|
|
assert!(!arg_names!(self).any(|name| name == a.b.name),
|
|
format!("Non-unique argument name: {} is already in use", a.b.name));
|
|
if let Some(l) = a.s.long {
|
|
assert!(!self.contains_long(l),
|
|
"Argument long must be unique\n\n\t--{} is already in use",
|
|
l);
|
|
}
|
|
if let Some(s) = a.s.short {
|
|
assert!(!self.contains_short(s),
|
|
"Argument short must be unique\n\n\t-{} is already in use",
|
|
s);
|
|
}
|
|
let i = if a.index.is_none() {
|
|
(self.positionals.len() + 1)
|
|
} else {
|
|
a.index.unwrap() as usize
|
|
};
|
|
assert!(!self.positionals.contains_key(i),
|
|
"Argument \"{}\" has the same index as another positional \
|
|
argument\n\n\tPerhaps try .multiple(true) to allow one positional argument \
|
|
to take multiple values",
|
|
a.b.name);
|
|
assert!(!(a.is_set(ArgSettings::Required) && a.is_set(ArgSettings::Global)),
|
|
"Global arguments cannot be required.\n\n\t'{}' is marked as \
|
|
global and required",
|
|
a.b.name);
|
|
if a.b.is_set(ArgSettings::Last) {
|
|
assert!(!self.positionals
|
|
.values()
|
|
.any(|p| p.b.is_set(ArgSettings::Last)),
|
|
"Only one positional argument may have last(true) set. Found two.");
|
|
assert!(a.s.long.is_none(),
|
|
"Flags or Options may not have last(true) set. {} has both a long and last(true) set.",
|
|
a.b.name);
|
|
assert!(a.s.short.is_none(),
|
|
"Flags or Options may not have last(true) set. {} has both a short and last(true) set.",
|
|
a.b.name);
|
|
}
|
|
true
|
|
}
|
|
|
|
#[inline]
|
|
fn add_conditional_reqs(&mut self, a: &Arg<'a, 'b>) {
|
|
if let Some(ref r_ifs) = a.r_ifs {
|
|
for &(arg, val) in r_ifs {
|
|
self.r_ifs.push((arg, val, a.b.name));
|
|
}
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn add_arg_groups(&mut self, a: &Arg<'a, 'b>) {
|
|
if let Some(ref grps) = a.b.groups {
|
|
for g in grps {
|
|
let mut found = false;
|
|
if let Some(ref mut ag) = self.groups.iter_mut().find(|grp| &grp.name == g) {
|
|
ag.args.push(a.b.name);
|
|
found = true;
|
|
}
|
|
if !found {
|
|
let mut ag = ArgGroup::with_name(g);
|
|
ag.args.push(a.b.name);
|
|
self.groups.push(ag);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn add_reqs(&mut self, a: &Arg<'a, 'b>) {
|
|
if a.is_set(ArgSettings::Required) {
|
|
// If the arg is required, add all it's requirements to master required list
|
|
if let Some(ref areqs) = a.b.requires {
|
|
for name in areqs
|
|
.iter()
|
|
.filter(|&&(val, _)| val.is_none())
|
|
.map(|&(_, name)| name) {
|
|
self.required.push(name);
|
|
}
|
|
}
|
|
self.required.push(a.b.name);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn implied_settings(&mut self, a: &Arg<'a, 'b>) {
|
|
if a.is_set(ArgSettings::Last) {
|
|
// if an arg has `Last` set, we need to imply DontCollapseArgsInUsage so that args
|
|
// in the usage string don't get confused or left out.
|
|
self.set(AS::DontCollapseArgsInUsage);
|
|
self.set(AS::ContainsLast);
|
|
}
|
|
if let Some(l) = a.s.long {
|
|
if l == "version" {
|
|
self.unset(AS::NeedsLongVersion);
|
|
} else if l == "help" {
|
|
self.unset(AS::NeedsLongHelp);
|
|
}
|
|
}
|
|
}
|
|
|
|
// actually adds the arguments
|
|
pub fn add_arg(&mut self, a: Arg<'a, 'b>) {
|
|
// if it's global we have to clone anyways
|
|
if a.is_set(ArgSettings::Global) {
|
|
return self.add_arg_ref(&a);
|
|
}
|
|
debug_assert!(self.debug_asserts(&a));
|
|
self.add_conditional_reqs(&a);
|
|
self.add_arg_groups(&a);
|
|
self.add_reqs(&a);
|
|
self.implied_settings(&a);
|
|
if a.index.is_some() || (a.s.short.is_none() && a.s.long.is_none()) {
|
|
let i = if a.index.is_none() {
|
|
(self.positionals.len() + 1)
|
|
} else {
|
|
a.index.unwrap() as usize
|
|
};
|
|
self.positionals
|
|
.insert(i, PosBuilder::from_arg(a, i as u64));
|
|
} else if a.is_set(ArgSettings::TakesValue) {
|
|
let mut ob = OptBuilder::from(a);
|
|
ob.s.unified_ord = self.flags.len() + self.opts.len();
|
|
self.opts.push(ob);
|
|
} else {
|
|
let mut fb = FlagBuilder::from(a);
|
|
fb.s.unified_ord = self.flags.len() + self.opts.len();
|
|
self.flags.push(fb);
|
|
}
|
|
}
|
|
// actually adds the arguments but from a borrow (which means we have to do some clonine)
|
|
pub fn add_arg_ref(&mut self, a: &Arg<'a, 'b>) {
|
|
debug_assert!(self.debug_asserts(a));
|
|
self.add_conditional_reqs(a);
|
|
self.add_arg_groups(a);
|
|
self.add_reqs(a);
|
|
self.implied_settings(a);
|
|
if a.index.is_some() || (a.s.short.is_none() && a.s.long.is_none()) {
|
|
let i = if a.index.is_none() {
|
|
(self.positionals.len() + 1)
|
|
} else {
|
|
a.index.unwrap() as usize
|
|
};
|
|
let pb = PosBuilder::from_arg_ref(a, i as u64);
|
|
self.positionals.insert(i, pb);
|
|
} else if a.is_set(ArgSettings::TakesValue) {
|
|
let mut ob = OptBuilder::from(a);
|
|
ob.s.unified_ord = self.flags.len() + self.opts.len();
|
|
self.opts.push(ob);
|
|
} else {
|
|
let mut fb = FlagBuilder::from(a);
|
|
fb.s.unified_ord = self.flags.len() + self.opts.len();
|
|
self.flags.push(fb);
|
|
}
|
|
if a.is_set(ArgSettings::Global) {
|
|
self.global_args.push(a.into());
|
|
}
|
|
}
|
|
|
|
pub fn add_group(&mut self, group: ArgGroup<'a>) {
|
|
if group.required {
|
|
self.required.push(group.name);
|
|
if let Some(ref reqs) = group.requires {
|
|
self.required.extend_from_slice(reqs);
|
|
}
|
|
if let Some(ref bl) = group.conflicts {
|
|
self.blacklist.extend_from_slice(bl);
|
|
}
|
|
}
|
|
if self.groups.iter().any(|g| g.name == group.name) {
|
|
let grp = self.groups
|
|
.iter_mut()
|
|
.find(|g| g.name == group.name)
|
|
.expect(INTERNAL_ERROR_MSG);
|
|
grp.args.extend_from_slice(&group.args);
|
|
grp.requires = group.requires.clone();
|
|
grp.conflicts = group.conflicts.clone();
|
|
grp.required = group.required;
|
|
} else {
|
|
self.groups.push(group);
|
|
}
|
|
}
|
|
|
|
pub fn add_subcommand(&mut self, mut subcmd: App<'a, 'b>) {
|
|
debugln!("Parser::add_subcommand: term_w={:?}, name={}",
|
|
self.meta.term_w,
|
|
subcmd.p.meta.name);
|
|
subcmd.p.meta.term_w = self.meta.term_w;
|
|
if subcmd.p.meta.name == "help" {
|
|
self.unset(AS::NeedsSubcommandHelp);
|
|
}
|
|
|
|
self.subcommands.push(subcmd);
|
|
}
|
|
|
|
pub fn propogate_settings(&mut self) {
|
|
debugln!("Parser::propogate_settings: self={}, g_settings={:#?}",
|
|
self.meta.name,
|
|
self.g_settings);
|
|
for sc in &mut self.subcommands {
|
|
debugln!("Parser::propogate_settings: sc={}, settings={:#?}, g_settings={:#?}",
|
|
sc.p.meta.name,
|
|
sc.p.settings,
|
|
sc.p.g_settings);
|
|
// We have to create a new scope in order to tell rustc the borrow of `sc` is
|
|
// done and to recursively call this method
|
|
{
|
|
let vsc = self.settings.is_set(AS::VersionlessSubcommands);
|
|
let gv = self.settings.is_set(AS::GlobalVersion);
|
|
|
|
if vsc {
|
|
sc.p.set(AS::DisableVersion);
|
|
}
|
|
if gv && sc.p.meta.version.is_none() && self.meta.version.is_some() {
|
|
sc.p.set(AS::GlobalVersion);
|
|
sc.p.meta.version = Some(self.meta.version.unwrap());
|
|
}
|
|
sc.p.settings = sc.p.settings | self.g_settings;
|
|
sc.p.g_settings = sc.p.g_settings | self.g_settings;
|
|
sc.p.meta.term_w = self.meta.term_w;
|
|
sc.p.meta.max_w = self.meta.max_w;
|
|
}
|
|
sc.p.propogate_settings();
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(feature = "lints", allow(needless_borrow))]
|
|
pub fn derive_display_order(&mut self) {
|
|
if self.is_set(AS::DeriveDisplayOrder) {
|
|
let unified = self.is_set(AS::UnifiedHelpMessage);
|
|
for (i, o) in self.opts
|
|
.iter_mut()
|
|
.enumerate()
|
|
.filter(|&(_, ref o)| o.s.disp_ord == 999) {
|
|
o.s.disp_ord = if unified { o.s.unified_ord } else { i };
|
|
}
|
|
for (i, f) in self.flags
|
|
.iter_mut()
|
|
.enumerate()
|
|
.filter(|&(_, ref f)| f.s.disp_ord == 999) {
|
|
f.s.disp_ord = if unified { f.s.unified_ord } else { i };
|
|
}
|
|
for (i, sc) in &mut self.subcommands
|
|
.iter_mut()
|
|
.enumerate()
|
|
.filter(|&(_, ref sc)| sc.p.meta.disp_ord == 999) {
|
|
sc.p.meta.disp_ord = i;
|
|
}
|
|
}
|
|
for sc in &mut self.subcommands {
|
|
sc.p.derive_display_order();
|
|
}
|
|
}
|
|
|
|
pub fn required(&self) -> Iter<&str> { self.required.iter() }
|
|
|
|
#[cfg_attr(feature = "lints", allow(needless_borrow))]
|
|
#[inline]
|
|
pub fn has_args(&self) -> bool {
|
|
!(self.flags.is_empty() && self.opts.is_empty() && self.positionals.is_empty())
|
|
}
|
|
|
|
#[inline]
|
|
pub fn has_opts(&self) -> bool { !self.opts.is_empty() }
|
|
|
|
#[inline]
|
|
pub fn has_flags(&self) -> bool { !self.flags.is_empty() }
|
|
|
|
#[inline]
|
|
pub fn has_positionals(&self) -> bool { !self.positionals.is_empty() }
|
|
|
|
#[inline]
|
|
pub fn has_subcommands(&self) -> bool { !self.subcommands.is_empty() }
|
|
|
|
#[inline]
|
|
pub fn has_visible_opts(&self) -> bool {
|
|
if self.opts.is_empty() {
|
|
return false;
|
|
}
|
|
self.opts.iter().any(|o| !o.is_set(ArgSettings::Hidden))
|
|
}
|
|
|
|
#[inline]
|
|
pub fn has_visible_flags(&self) -> bool {
|
|
if self.flags.is_empty() {
|
|
return false;
|
|
}
|
|
self.flags.iter().any(|f| !f.is_set(ArgSettings::Hidden))
|
|
}
|
|
|
|
#[inline]
|
|
pub fn has_visible_positionals(&self) -> bool {
|
|
if self.positionals.is_empty() {
|
|
return false;
|
|
}
|
|
self.positionals
|
|
.values()
|
|
.any(|p| !p.is_set(ArgSettings::Hidden))
|
|
}
|
|
|
|
#[inline]
|
|
pub fn has_visible_subcommands(&self) -> bool {
|
|
if self.subcommands.is_empty() {
|
|
return false;
|
|
}
|
|
self.subcommands.iter().any(|s| !s.p.is_set(AS::Hidden))
|
|
}
|
|
|
|
#[inline]
|
|
pub fn is_set(&self, s: AS) -> bool { self.settings.is_set(s) }
|
|
|
|
#[inline]
|
|
pub fn set(&mut self, s: AS) { self.settings.set(s) }
|
|
|
|
#[inline]
|
|
pub fn unset(&mut self, s: AS) { self.settings.unset(s) }
|
|
|
|
#[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))]
|
|
pub fn verify_positionals(&mut self) -> bool {
|
|
// 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, p)) = self.positionals.iter().rev().next() {
|
|
assert!(!(idx != self.positionals.len()),
|
|
"Found positional argument \"{}\" whose index is {} but there \
|
|
are only {} positional arguments defined",
|
|
p.b.name,
|
|
idx,
|
|
self.positionals.len());
|
|
}
|
|
|
|
// Next we verify that only the highest index has a .multiple(true) (if any)
|
|
if self.positionals
|
|
.values()
|
|
.any(|a| {
|
|
a.b.is_set(ArgSettings::Multiple) &&
|
|
(a.index as usize != self.positionals.len())
|
|
}) {
|
|
let mut it = self.positionals.values().rev();
|
|
let last = it.next().unwrap();
|
|
let second_to_last = it.next().unwrap();
|
|
// Either the final positional is required
|
|
// Or the second to last has a terminator or .last(true) set
|
|
let ok = last.is_set(ArgSettings::Required) ||
|
|
(second_to_last.v.terminator.is_some() ||
|
|
second_to_last.b.is_set(ArgSettings::Last)) ||
|
|
last.is_set(ArgSettings::Last);
|
|
assert!(ok,
|
|
"When using a positional argument with .multiple(true) that is *not the \
|
|
last* positional argument, the last positional argument (i.e the one \
|
|
with the highest index) *must* have .required(true) or .last(true) set.");
|
|
let ok = second_to_last.is_set(ArgSettings::Multiple) || last.is_set(ArgSettings::Last);
|
|
assert!(ok,
|
|
"Only the last positional argument, or second to last positional \
|
|
argument may be set to .multiple(true)");
|
|
|
|
let count = self.positionals
|
|
.values()
|
|
.filter(|p| p.b.settings.is_set(ArgSettings::Multiple) && p.v.num_vals.is_none())
|
|
.count();
|
|
let ok = count <= 1 ||
|
|
(last.is_set(ArgSettings::Last) && last.is_set(ArgSettings::Multiple) &&
|
|
second_to_last.is_set(ArgSettings::Multiple) &&
|
|
count == 2);
|
|
assert!(ok,
|
|
"Only one positional argument with .multiple(true) set is allowed per \
|
|
command, unless the second one also has .last(true) set");
|
|
}
|
|
|
|
|
|
if self.is_set(AS::AllowMissingPositional) {
|
|
// Check that if a required positional argument is found, all positions with a lower
|
|
// index are also required.
|
|
let mut found = false;
|
|
let mut foundx2 = false;
|
|
for p in self.positionals.values().rev() {
|
|
if foundx2 && !p.b.settings.is_set(ArgSettings::Required) {
|
|
assert!(p.b.is_set(ArgSettings::Required),
|
|
"Found positional argument which is not required with a lower \
|
|
index than a required positional argument by two or more: {:?} \
|
|
index {}",
|
|
p.b.name,
|
|
p.index);
|
|
} else if p.b.is_set(ArgSettings::Required) && !p.b.is_set(ArgSettings::Last) {
|
|
// Args that .last(true) don't count since they can be required and have
|
|
// positionals with a lower index that aren't required
|
|
// Imagine: prog <req1> [opt1] -- <req2>
|
|
// Both of these are valid invocations:
|
|
// $ prog r1 -- r2
|
|
// $ prog r1 o1 -- r2
|
|
if found {
|
|
foundx2 = true;
|
|
continue;
|
|
}
|
|
found = true;
|
|
continue;
|
|
} else {
|
|
found = false;
|
|
}
|
|
}
|
|
} else {
|
|
// Check that if a required positional argument is found, all positions with a lower
|
|
// index are also required
|
|
let mut found = false;
|
|
for p in self.positionals.values().rev() {
|
|
if found {
|
|
assert!(p.b.is_set(ArgSettings::Required),
|
|
"Found positional argument which is not required with a lower \
|
|
index than a required positional argument: {:?} index {}",
|
|
p.b.name,
|
|
p.index);
|
|
} else if p.b.is_set(ArgSettings::Required) && !p.b.is_set(ArgSettings::Last) {
|
|
// Args that .last(true) don't count since they can be required and have
|
|
// positionals with a lower index that aren't required
|
|
// Imagine: prog <req1> [opt1] -- <req2>
|
|
// Both of these are valid invocations:
|
|
// $ prog r1 -- r2
|
|
// $ prog r1 o1 -- r2
|
|
found = true;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if self.positionals
|
|
.values()
|
|
.any(|p| {
|
|
p.b.is_set(ArgSettings::Last) && p.b.is_set(ArgSettings::Required)
|
|
}) && self.has_subcommands() &&
|
|
!self.is_set(AS::SubcommandsNegateReqs) {
|
|
panic!("Having a required positional argument with .last(true) set *and* child \
|
|
subcommands without setting SubcommandsNegateReqs isn't compatible.");
|
|
}
|
|
|
|
true
|
|
}
|
|
|
|
pub fn propogate_globals(&mut self) {
|
|
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
|
|
{
|
|
for a in &self.global_args {
|
|
sc.p.add_arg_ref(a);
|
|
}
|
|
}
|
|
sc.p.propogate_globals();
|
|
}
|
|
}
|
|
|
|
// Checks if the arg matches a subcommand name, or any of it's aliases (if defined)
|
|
fn possible_subcommand(&self, arg_os: &OsStr) -> (bool, Option<&str>) {
|
|
debugln!("Parser::possible_subcommand: arg={:?}", arg_os);
|
|
fn starts(h: &str, n: &OsStr) -> bool {
|
|
#[cfg(not(target_os = "windows"))]
|
|
use std::os::unix::ffi::OsStrExt;
|
|
#[cfg(target_os = "windows")]
|
|
use osstringext::OsStrExt3;
|
|
|
|
let n_bytes = n.as_bytes();
|
|
let h_bytes = OsStr::new(h).as_bytes();
|
|
|
|
h_bytes.starts_with(n_bytes)
|
|
}
|
|
|
|
if self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound) {
|
|
return (false, None);
|
|
}
|
|
if !self.is_set(AS::InferSubcommands) {
|
|
if let Some(sc) = find_subcmd!(self, arg_os) {
|
|
return (true, Some(&sc.p.meta.name));
|
|
}
|
|
} else {
|
|
let v = self.subcommands
|
|
.iter()
|
|
.filter(|s| {
|
|
starts(&s.p.meta.name[..], &*arg_os) ||
|
|
(s.p.meta.aliases.is_some() &&
|
|
s.p
|
|
.meta
|
|
.aliases
|
|
.as_ref()
|
|
.unwrap()
|
|
.iter()
|
|
.filter(|&&(a, _)| starts(a, &*arg_os))
|
|
.count() == 1)
|
|
})
|
|
.map(|sc| &sc.p.meta.name)
|
|
.collect::<Vec<_>>();
|
|
|
|
if v.len() == 1 {
|
|
return (true, Some(v[0]));
|
|
}
|
|
}
|
|
(false, None)
|
|
}
|
|
|
|
fn parse_help_subcommand<I, T>(&self, it: &mut I) -> ClapResult<ParseResult<'a>>
|
|
where I: Iterator<Item = T>,
|
|
T: Into<OsString>
|
|
{
|
|
debugln!("Parser::parse_help_subcommand;");
|
|
let cmds: Vec<OsString> = it.map(|c| c.into()).collect();
|
|
let mut help_help = false;
|
|
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;
|
|
}
|
|
if let Some(c) = sc.subcommands
|
|
.iter()
|
|
.find(|s| &*s.p.meta.name == cmd)
|
|
.map(|sc| &sc.p) {
|
|
sc = c;
|
|
if i == cmds.len() - 1 {
|
|
break;
|
|
}
|
|
} else if let Some(c) = sc.subcommands
|
|
.iter()
|
|
.find(|s| if let Some(ref als) = s.p.meta.aliases {
|
|
als.iter().any(|&(a, _)| a == &*cmd.to_string_lossy())
|
|
} else {
|
|
false
|
|
})
|
|
.map(|sc| &sc.p) {
|
|
sc = c;
|
|
if i == cmds.len() - 1 {
|
|
break;
|
|
}
|
|
} else {
|
|
return Err(Error::unrecognized_subcommand(cmd.to_string_lossy().into_owned(),
|
|
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.b.help = Some("The subcommand whose help message to display");
|
|
pb.set(ArgSettings::Multiple);
|
|
sc.positionals.insert(1, pb);
|
|
sc.settings = sc.settings | self.g_settings;
|
|
} 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));
|
|
}
|
|
Err(sc._help(false))
|
|
}
|
|
|
|
// allow wrong self convention due to self.valid_neg_num = true and it's a private method
|
|
#[cfg_attr(feature = "lints", allow(wrong_self_convention))]
|
|
fn is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: ParseResult<'a>) -> bool {
|
|
debugln!("Parser::is_new_arg: arg={:?}, Needs Val of={:?}",
|
|
arg_os,
|
|
needs_val_of);
|
|
let app_wide_settings = if self.is_set(AS::AllowLeadingHyphen) {
|
|
true
|
|
} else if self.is_set(AS::AllowNegativeNumbers) {
|
|
let a = arg_os.to_string_lossy();
|
|
if a.parse::<i64>().is_ok() || a.parse::<f64>().is_ok() {
|
|
self.set(AS::ValidNegNumFound);
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
} else {
|
|
false
|
|
};
|
|
let arg_allows_tac = match needs_val_of {
|
|
ParseResult::Opt(name) => {
|
|
let o = self.opts
|
|
.iter()
|
|
.find(|o| o.b.name == name)
|
|
.expect(INTERNAL_ERROR_MSG);
|
|
(o.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings)
|
|
}
|
|
ParseResult::Pos(name) => {
|
|
let p = self.positionals
|
|
.values()
|
|
.find(|p| p.b.name == name)
|
|
.expect(INTERNAL_ERROR_MSG);
|
|
(p.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings)
|
|
}
|
|
_ => false,
|
|
};
|
|
debugln!("Parser::is_new_arg: Arg::allow_leading_hyphen({:?})",
|
|
arg_allows_tac);
|
|
|
|
// Is this a new argument, or values from a previous option?
|
|
let mut ret = if arg_os.starts_with(b"--") {
|
|
debugln!("Parser::is_new_arg: -- found");
|
|
if arg_os.len_() == 2 && !arg_allows_tac {
|
|
return true; // We have to return true so override everything else
|
|
} else if arg_allows_tac {
|
|
return false;
|
|
}
|
|
true
|
|
} else if arg_os.starts_with(b"-") {
|
|
debugln!("Parser::is_new_arg: - found");
|
|
// a singe '-' by itself is a value and typically means "stdin" on unix systems
|
|
!(arg_os.len_() == 1)
|
|
} else {
|
|
debugln!("Parser::is_new_arg: probably value");
|
|
false
|
|
};
|
|
|
|
ret = ret && !arg_allows_tac;
|
|
|
|
debugln!("Parser::is_new_arg: starts_new_arg={:?}", ret);
|
|
ret
|
|
}
|
|
|
|
// The actual parsing function
|
|
#[cfg_attr(feature = "lints", allow(while_let_on_iterator, collapsible_if))]
|
|
pub fn get_matches_with<I, T>(&mut self,
|
|
matcher: &mut ArgMatcher<'a>,
|
|
it: &mut Peekable<I>)
|
|
-> ClapResult<()>
|
|
where I: Iterator<Item = T>,
|
|
T: Into<OsString> + Clone
|
|
{
|
|
debugln!("Parser::get_matches_with;");
|
|
// Verify all positional assertions pass
|
|
debug_assert!(self.app_debug_asserts());
|
|
if self.positionals
|
|
.values()
|
|
.any(|a| {
|
|
a.b.is_set(ArgSettings::Multiple) &&
|
|
(a.index as usize != self.positionals.len())
|
|
}) &&
|
|
self.positionals
|
|
.values()
|
|
.last()
|
|
.map_or(false, |p| !p.is_set(ArgSettings::Last)) {
|
|
self.settings.set(AS::LowIndexMultiplePositional);
|
|
}
|
|
let has_args = self.has_args();
|
|
|
|
// Next 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: ParseResult<'a> = ParseResult::NotFound;
|
|
let mut pos_counter = 1;
|
|
while let Some(arg) = it.next() {
|
|
let arg_os = arg.into();
|
|
debugln!("Parser::get_matches_with: Begin parsing '{:?}' ({:?})",
|
|
arg_os,
|
|
&*arg_os.as_bytes());
|
|
|
|
self.unset(AS::ValidNegNumFound);
|
|
// Is this a new argument, or values from a previous option?
|
|
let starts_new_arg = self.is_new_arg(&arg_os, needs_val_of);
|
|
if arg_os.starts_with(b"--") && arg_os.len_() == 2 && starts_new_arg {
|
|
debugln!("Parser::get_matches_with: setting TrailingVals=true");
|
|
self.set(AS::TrailingValues);
|
|
continue;
|
|
}
|
|
|
|
// Has the user already passed '--'? Meaning only positional args follow
|
|
if !self.is_set(AS::TrailingValues) {
|
|
// Does the arg match a subcommand name, or any of it's aliases (if defined)
|
|
{
|
|
let (is_match, sc_name) = self.possible_subcommand(&arg_os);
|
|
debugln!("Parser::get_matches_with: possible_sc={:?}, sc={:?}",
|
|
is_match,
|
|
sc_name);
|
|
if is_match {
|
|
let sc_name = sc_name.expect(INTERNAL_ERROR_MSG);
|
|
if sc_name == "help" && self.is_set(AS::NeedsSubcommandHelp) {
|
|
try!(self.parse_help_subcommand(it));
|
|
}
|
|
subcmd_name = Some(sc_name.to_owned());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if !starts_new_arg {
|
|
match needs_val_of {
|
|
ParseResult::Opt(name) => {
|
|
// Check to see if parsing a value from a previous arg
|
|
let arg = self.opts
|
|
.iter()
|
|
.find(|o| o.b.name == name)
|
|
.expect(INTERNAL_ERROR_MSG);
|
|
// get the OptBuilder so we can check the settings
|
|
needs_val_of = try!(self.add_val_to_arg(arg, &arg_os, matcher));
|
|
// get the next value from the iterator
|
|
continue;
|
|
}
|
|
_ => (),
|
|
}
|
|
} else if arg_os.starts_with(b"--") {
|
|
needs_val_of = try!(self.parse_long_arg(matcher, &arg_os));
|
|
debugln!("Parser:get_matches_with: After parse_long_arg {:?}",
|
|
needs_val_of);
|
|
match needs_val_of {
|
|
ParseResult::Flag |
|
|
ParseResult::Opt(..) |
|
|
ParseResult::ValuesDone => continue,
|
|
_ => (),
|
|
}
|
|
} else if arg_os.starts_with(b"-") && arg_os.len_() != 1 {
|
|
// Try to parse short args like normal, if AllowLeadingHyphen or
|
|
// AllowNegativeNumbers is set, parse_short_arg will *not* throw
|
|
// an error, and instead return Ok(None)
|
|
needs_val_of = try!(self.parse_short_arg(matcher, &arg_os));
|
|
// If it's None, we then check if one of those two AppSettings was set
|
|
debugln!("Parser:get_matches_with: After parse_short_arg {:?}",
|
|
needs_val_of);
|
|
match needs_val_of {
|
|
ParseResult::MaybeNegNum => {
|
|
if !(arg_os.to_string_lossy().parse::<i64>().is_ok() ||
|
|
arg_os.to_string_lossy().parse::<f64>().is_ok()) {
|
|
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
|
|
"",
|
|
&*usage::create_error_usage(self, matcher, None),
|
|
self.color()));
|
|
}
|
|
}
|
|
ParseResult::Opt(..) |
|
|
ParseResult::Flag |
|
|
ParseResult::ValuesDone => continue,
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
if !(self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound)) &&
|
|
!self.is_set(AS::InferSubcommands) {
|
|
if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(),
|
|
sc_names!(self)) {
|
|
return Err(Error::invalid_subcommand(arg_os
|
|
.to_string_lossy()
|
|
.into_owned(),
|
|
cdate,
|
|
self.meta
|
|
.bin_name
|
|
.as_ref()
|
|
.unwrap_or(&self.meta.name),
|
|
&*usage::create_error_usage(self,
|
|
matcher,
|
|
None),
|
|
self.color()));
|
|
}
|
|
}
|
|
}
|
|
|
|
let low_index_mults = self.is_set(AS::LowIndexMultiplePositional) &&
|
|
pos_counter == (self.positionals.len() - 1);
|
|
let missing_pos = self.is_set(AS::AllowMissingPositional) &&
|
|
pos_counter == (self.positionals.len() - 1);
|
|
debugln!("Parser::get_matches_with: Positional counter...{}",
|
|
pos_counter);
|
|
debugln!("Parser::get_matches_with: Low index multiples...{:?}",
|
|
low_index_mults);
|
|
if low_index_mults || missing_pos {
|
|
if let Some(na) = it.peek() {
|
|
let n = (*na).clone().into();
|
|
needs_val_of = if needs_val_of != ParseResult::ValuesDone {
|
|
if let Some(p) = self.positionals.get(pos_counter) {
|
|
ParseResult::Pos(p.b.name)
|
|
} else {
|
|
ParseResult::ValuesDone
|
|
}
|
|
} else {
|
|
ParseResult::ValuesDone
|
|
};
|
|
let sc_match = {
|
|
self.possible_subcommand(&n).0
|
|
};
|
|
if self.is_new_arg(&n, needs_val_of) || sc_match ||
|
|
suggestions::did_you_mean(&n.to_string_lossy(), sc_names!(self)).is_some() {
|
|
debugln!("Parser::get_matches_with: Bumping the positional counter...");
|
|
pos_counter += 1;
|
|
}
|
|
} else {
|
|
debugln!("Parser::get_matches_with: Bumping the positional counter...");
|
|
pos_counter += 1;
|
|
}
|
|
} else if self.is_set(AS::ContainsLast) && self.is_set(AS::TrailingValues) {
|
|
// Came to -- and one postional has .last(true) set, so we go immediately
|
|
// to the last (highest index) positional
|
|
debugln!("Parser::get_matches_with: .last(true) and --, setting last pos");
|
|
pos_counter = self.positionals.len();
|
|
}
|
|
if let Some(p) = self.positionals.get(pos_counter) {
|
|
if p.is_set(ArgSettings::Last) && !self.is_set(AS::TrailingValues) {
|
|
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
|
|
"",
|
|
&*usage::create_error_usage(self,
|
|
matcher,
|
|
None),
|
|
self.color()));
|
|
}
|
|
parse_positional!(self, p, arg_os, pos_counter, matcher);
|
|
self.settings.set(AS::ValidArgFound);
|
|
} else if self.is_set(AS::AllowExternalSubcommands) {
|
|
// Get external subcommand name
|
|
let sc_name = match arg_os.to_str() {
|
|
Some(s) => s.to_string(),
|
|
None => {
|
|
if !self.is_set(AS::StrictUtf8) {
|
|
return Err(Error::invalid_utf8(&*usage::create_error_usage(self,
|
|
matcher,
|
|
None),
|
|
self.color()));
|
|
}
|
|
arg_os.to_string_lossy().into_owned()
|
|
}
|
|
};
|
|
|
|
// Collect the external subcommand args
|
|
let mut sc_m = ArgMatcher::new();
|
|
while let Some(v) = it.next() {
|
|
let a = v.into();
|
|
if a.to_str().is_none() && !self.is_set(AS::StrictUtf8) {
|
|
return Err(Error::invalid_utf8(&*usage::create_error_usage(self,
|
|
matcher,
|
|
None),
|
|
self.color()));
|
|
}
|
|
sc_m.add_val_to("", &a);
|
|
}
|
|
|
|
matcher.subcommand(SubCommand {
|
|
name: sc_name,
|
|
matches: sc_m.into(),
|
|
});
|
|
} else if !(self.is_set(AS::AllowLeadingHyphen) ||
|
|
self.is_set(AS::AllowNegativeNumbers)) &&
|
|
!self.is_set(AS::InferSubcommands) {
|
|
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
|
|
"",
|
|
&*usage::create_error_usage(self,
|
|
matcher,
|
|
None),
|
|
self.color()));
|
|
} else if !has_args || self.is_set(AS::InferSubcommands) && self.has_subcommands() {
|
|
if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(),
|
|
sc_names!(self)) {
|
|
return Err(Error::invalid_subcommand(arg_os.to_string_lossy().into_owned(),
|
|
cdate,
|
|
self.meta
|
|
.bin_name
|
|
.as_ref()
|
|
.unwrap_or(&self.meta.name),
|
|
&*usage::create_error_usage(self,
|
|
matcher,
|
|
None),
|
|
self.color()));
|
|
} else {
|
|
return Err(Error::unrecognized_subcommand(arg_os
|
|
.to_string_lossy()
|
|
.into_owned(),
|
|
self.meta
|
|
.bin_name
|
|
.as_ref()
|
|
.unwrap_or(&self.meta.name),
|
|
self.color()));
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(ref pos_sc_name) = subcmd_name {
|
|
let sc_name = {
|
|
find_subcmd!(self, pos_sc_name)
|
|
.expect(INTERNAL_ERROR_MSG)
|
|
.p
|
|
.meta
|
|
.name
|
|
.clone()
|
|
};
|
|
try!(self.parse_subcommand(&*sc_name, matcher, it));
|
|
} else if self.is_set(AS::SubcommandRequired) {
|
|
let bn = self.meta.bin_name.as_ref().unwrap_or(&self.meta.name);
|
|
return Err(Error::missing_subcommand(bn,
|
|
&usage::create_error_usage(self, matcher, None),
|
|
self.color()));
|
|
} else if self.is_set(AS::SubcommandRequiredElseHelp) {
|
|
debugln!("Parser::get_matches_with: SubcommandRequiredElseHelp=true");
|
|
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,
|
|
});
|
|
}
|
|
|
|
Validator::new(self).validate(needs_val_of, subcmd_name, matcher)
|
|
}
|
|
|
|
|
|
fn propogate_help_version(&mut self) {
|
|
debugln!("Parser::propogate_help_version;");
|
|
self.create_help_and_version();
|
|
for sc in &mut self.subcommands {
|
|
sc.p.propogate_help_version();
|
|
}
|
|
}
|
|
|
|
fn build_bin_names(&mut self) {
|
|
debugln!("Parser::build_bin_names;");
|
|
for sc in &mut self.subcommands {
|
|
debug!("Parser::build_bin_names:iter: 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!("Parser::build_bin_names:iter: 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!("Parser::build_bin_names:iter: Calling build_bin_names from...{}",
|
|
sc.p.meta.name);
|
|
sc.p.build_bin_names();
|
|
}
|
|
}
|
|
|
|
fn parse_subcommand<I, T>(&mut self,
|
|
sc_name: &str,
|
|
matcher: &mut ArgMatcher<'a>,
|
|
it: &mut Peekable<I>)
|
|
-> ClapResult<()>
|
|
where I: Iterator<Item = T>,
|
|
T: Into<OsString> + Clone
|
|
{
|
|
use std::fmt::Write;
|
|
debugln!("Parser::parse_subcommand;");
|
|
let mut mid_string = String::new();
|
|
if !self.is_set(AS::SubcommandsNegateReqs) {
|
|
let mut hs: Vec<&str> = self.required.iter().map(|n| &**n).collect();
|
|
for k in matcher.arg_names() {
|
|
hs.push(k);
|
|
}
|
|
let reqs = usage::get_required_usage_from(self, &hs, Some(matcher), None, false);
|
|
|
|
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()
|
|
.find(|s| s.p.meta.name == sc_name) {
|
|
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!("{}{}{}",
|
|
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!("{}{}{}",
|
|
self.meta.bin_name.as_ref().unwrap_or(&String::new()),
|
|
if self.meta.bin_name.is_some() {
|
|
" "
|
|
} else {
|
|
""
|
|
},
|
|
&*sc.p.meta.name));
|
|
debugln!("Parser::parse_subcommand: About to parse sc={}",
|
|
sc.p.meta.name);
|
|
debugln!("Parser::parse_subcommand: sc settings={:#?}", sc.p.settings);
|
|
try!(sc.p.get_matches_with(&mut sc_matcher, it));
|
|
matcher.subcommand(SubCommand {
|
|
name: sc.p.meta.name.clone(),
|
|
matches: sc_matcher.into(),
|
|
});
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn groups_for_arg(&self, name: &str) -> Option<Vec<&'a str>> {
|
|
debugln!("Parser::groups_for_arg: name={}", name);
|
|
|
|
if self.groups.is_empty() {
|
|
debugln!("Parser::groups_for_arg: No groups defined");
|
|
return None;
|
|
}
|
|
let mut res = vec![];
|
|
debugln!("Parser::groups_for_arg: Searching through groups...");
|
|
for grp in &self.groups {
|
|
for a in &grp.args {
|
|
if a == &name {
|
|
sdebugln!("\tFound '{}'", grp.name);
|
|
res.push(&*grp.name);
|
|
}
|
|
}
|
|
}
|
|
if res.is_empty() {
|
|
return None;
|
|
}
|
|
|
|
Some(res)
|
|
}
|
|
|
|
pub fn args_in_group(&self, group: &str) -> Vec<String> {
|
|
let mut g_vec = vec![];
|
|
let mut args = vec![];
|
|
|
|
for n in &self.groups
|
|
.iter()
|
|
.find(|g| g.name == group)
|
|
.expect(INTERNAL_ERROR_MSG)
|
|
.args {
|
|
if let Some(f) = self.flags.iter().find(|f| &f.b.name == n) {
|
|
args.push(f.to_string());
|
|
} else if let Some(f) = self.opts.iter().find(|o| &o.b.name == n) {
|
|
args.push(f.to_string());
|
|
} else if let Some(p) = self.positionals.values().find(|p| &p.b.name == n) {
|
|
args.push(p.b.name.to_owned());
|
|
} else {
|
|
g_vec.push(*n);
|
|
}
|
|
}
|
|
|
|
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()
|
|
}
|
|
|
|
pub 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
|
|
.iter()
|
|
.find(|g| g.name == group)
|
|
.expect(INTERNAL_ERROR_MSG)
|
|
.args {
|
|
if self.groups.iter().any(|g| g.name == *n) {
|
|
args.extend(self.arg_names_in_group(n));
|
|
g_vec.push(*n);
|
|
} else if !args.contains(n) {
|
|
args.push(*n);
|
|
}
|
|
}
|
|
|
|
args.iter().map(|s| *s).collect()
|
|
}
|
|
|
|
pub fn create_help_and_version(&mut self) {
|
|
debugln!("Parser::create_help_and_version;");
|
|
// name is "hclap_help" because flags are sorted by name
|
|
if !self.contains_long("help") {
|
|
debugln!("Parser::create_help_and_version: Building --help");
|
|
if self.help_short.is_none() && !self.contains_short('h') {
|
|
self.help_short = Some('h');
|
|
}
|
|
let arg = FlagBuilder {
|
|
b: Base {
|
|
name: "hclap_help",
|
|
help: self.help_message.or(Some("Prints help information")),
|
|
..Default::default()
|
|
},
|
|
s: Switched {
|
|
short: self.help_short,
|
|
long: Some("help"),
|
|
..Default::default()
|
|
},
|
|
};
|
|
self.flags.push(arg);
|
|
}
|
|
if !self.is_set(AS::DisableVersion) && !self.contains_long("version") {
|
|
debugln!("Parser::create_help_and_version: Building --version");
|
|
if self.version_short.is_none() && !self.contains_short('V') {
|
|
self.version_short = Some('V');
|
|
}
|
|
// name is "vclap_version" because flags are sorted by name
|
|
let arg = FlagBuilder {
|
|
b: Base {
|
|
name: "vclap_version",
|
|
help: self.version_message.or(Some("Prints version information")),
|
|
..Default::default()
|
|
},
|
|
s: Switched {
|
|
short: self.version_short,
|
|
long: Some("version"),
|
|
..Default::default()
|
|
},
|
|
};
|
|
self.flags.push(arg);
|
|
}
|
|
if !self.subcommands.is_empty() && !self.is_set(AS::DisableHelpSubcommand) &&
|
|
self.is_set(AS::NeedsSubcommandHelp) {
|
|
debugln!("Parser::create_help_and_version: Building help");
|
|
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
|
|
fn check_for_help_and_version_str(&self, arg: &OsStr) -> ClapResult<()> {
|
|
debugln!("Parser::check_for_help_and_version_str;");
|
|
debug!("Parser::check_for_help_and_version_str: Checking if --{} is help or version...",
|
|
arg.to_str().unwrap());
|
|
if arg == "help" && self.is_set(AS::NeedsLongHelp) {
|
|
sdebugln!("Help");
|
|
return Err(self._help(true));
|
|
}
|
|
if arg == "version" && self.is_set(AS::NeedsLongVersion) {
|
|
sdebugln!("Version");
|
|
return Err(self._version(true));
|
|
}
|
|
sdebugln!("Neither");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn check_for_help_and_version_char(&self, arg: char) -> ClapResult<()> {
|
|
debugln!("Parser::check_for_help_and_version_char;");
|
|
debug!("Parser::check_for_help_and_version_char: Checking if -{} is help or version...",
|
|
arg);
|
|
if let Some(h) = self.help_short {
|
|
if arg == h && self.is_set(AS::NeedsLongHelp) {
|
|
sdebugln!("Help");
|
|
return Err(self._help(false));
|
|
}
|
|
}
|
|
if let Some(v) = self.version_short {
|
|
if arg == v && self.is_set(AS::NeedsLongVersion) {
|
|
sdebugln!("Version");
|
|
return Err(self._version(false));
|
|
}
|
|
}
|
|
sdebugln!("Neither");
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg_attr(feature = "cargo-clippy", allow(let_and_return))]
|
|
fn use_long_help(&self) -> bool {
|
|
let ul = self.flags.iter().any(|f| f.b.long_help.is_some()) ||
|
|
self.opts.iter().any(|o| o.b.long_help.is_some()) ||
|
|
self.positionals.values().any(|p| p.b.long_help.is_some()) ||
|
|
self.subcommands
|
|
.iter()
|
|
.any(|s| s.p.meta.long_about.is_some());
|
|
debugln!("Parser::use_long_help: ret={:?}", ul);
|
|
ul
|
|
}
|
|
|
|
fn _help(&self, mut use_long: bool) -> Error {
|
|
debugln!("Parser::_help: use_long={:?}", use_long);
|
|
use_long = use_long && self.use_long_help();
|
|
let mut buf = vec![];
|
|
match Help::write_parser_help(&mut buf, self, use_long) {
|
|
Err(e) => return e,
|
|
_ => (),
|
|
}
|
|
Error {
|
|
message: unsafe { String::from_utf8_unchecked(buf) },
|
|
kind: ErrorKind::HelpDisplayed,
|
|
info: None,
|
|
}
|
|
}
|
|
|
|
fn _version(&self, use_long: bool) -> Error {
|
|
debugln!("Parser::_version: ");
|
|
let out = io::stdout();
|
|
let mut buf_w = BufWriter::new(out.lock());
|
|
match self.print_version(&mut buf_w, use_long) {
|
|
Err(e) => return e,
|
|
_ => (),
|
|
}
|
|
Error {
|
|
message: String::new(),
|
|
kind: ErrorKind::VersionDisplayed,
|
|
info: None,
|
|
}
|
|
}
|
|
|
|
fn parse_long_arg(&mut self,
|
|
matcher: &mut ArgMatcher<'a>,
|
|
full_arg: &OsStr)
|
|
-> ClapResult<ParseResult<'a>> {
|
|
// maybe here lifetime should be 'a
|
|
debugln!("Parser::parse_long_arg;");
|
|
let mut val = None;
|
|
debug!("Parser::parse_long_arg: 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) = find_opt_by_long!(@os self, arg) {
|
|
debugln!("Parser::parse_long_arg: Found valid opt '{}'",
|
|
opt.to_string());
|
|
self.settings.set(AS::ValidArgFound);
|
|
let ret = try!(self.parse_opt(val, opt, val.is_some(), matcher));
|
|
if self.cache.map_or(true, |name| name != opt.b.name) {
|
|
arg_post_processing!(self, opt, matcher);
|
|
self.cache = Some(opt.b.name);
|
|
}
|
|
|
|
return Ok(ret);
|
|
} else if let Some(flag) = find_flag_by_long!(@os self, arg) {
|
|
debugln!("Parser::parse_long_arg: Found valid flag '{}'",
|
|
flag.to_string());
|
|
self.settings.set(AS::ValidArgFound);
|
|
// 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.
|
|
// if self.cache.map_or(true, |name| name != flag.b.name) {
|
|
arg_post_processing!(self, flag, matcher);
|
|
// self.cache = Some(flag.b.name);
|
|
// }
|
|
|
|
return Ok(ParseResult::Flag);
|
|
} else if self.is_set(AS::AllowLeadingHyphen) {
|
|
return Ok(ParseResult::MaybeHyphenValue);
|
|
} else if self.is_set(AS::ValidNegNumFound) {
|
|
return Ok(ParseResult::MaybeNegNum);
|
|
}
|
|
|
|
debugln!("Parser::parse_long_arg: Didn't match anything");
|
|
self.did_you_mean_error(arg.to_str().expect(INVALID_UTF8), matcher)
|
|
.map(|_| ParseResult::NotFound)
|
|
}
|
|
|
|
#[cfg_attr(feature = "lints", allow(len_zero))]
|
|
fn parse_short_arg(&mut self,
|
|
matcher: &mut ArgMatcher<'a>,
|
|
full_arg: &OsStr)
|
|
-> ClapResult<ParseResult<'a>> {
|
|
debugln!("Parser::parse_short_arg: full_arg={:?}", full_arg);
|
|
let arg_os = full_arg.trim_left_matches(b'-');
|
|
let arg = arg_os.to_string_lossy();
|
|
|
|
// If AllowLeadingHyphen is set, we want to ensure `-val` gets parsed as `-val` and not
|
|
// `-v` `-a` `-l` assuming `v` `a` and `l` are all, or mostly, valid shorts.
|
|
if self.is_set(AS::AllowLeadingHyphen) {
|
|
if arg.chars().any(|c| !self.contains_short(c)) {
|
|
debugln!("Parser::parse_short_arg: LeadingHyphenAllowed yet -{} isn't valid",
|
|
arg);
|
|
return Ok(ParseResult::MaybeHyphenValue);
|
|
}
|
|
} else if self.is_set(AS::ValidNegNumFound) {
|
|
// TODO: Add docs about having AllowNegativeNumbers and `-2` as a valid short
|
|
// May be better to move this to *after* not finding a valid flag/opt?
|
|
debugln!("Parser::parse_short_arg: Valid negative num...");
|
|
return Ok(ParseResult::MaybeNegNum);
|
|
}
|
|
|
|
let mut ret = ParseResult::NotFound;
|
|
for c in arg.chars() {
|
|
debugln!("Parser::parse_short_arg:iter:{}", c);
|
|
// 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) = find_opt_by_short!(self, c) {
|
|
debugln!("Parser::parse_short_arg:iter:{}: Found valid opt", c);
|
|
self.settings.set(AS::ValidArgFound);
|
|
// Check for trailing concatenated value
|
|
let p: Vec<_> = arg.splitn(2, c).collect();
|
|
debugln!("Parser::parse_short_arg:iter:{}: p[0]={:?}, p[1]={:?}",
|
|
c,
|
|
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 {
|
|
debugln!("Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii)",
|
|
c,
|
|
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, false, matcher));
|
|
|
|
if self.cache.map_or(true, |name| name != opt.b.name) {
|
|
arg_post_processing!(self, opt, matcher);
|
|
self.cache = Some(opt.b.name);
|
|
}
|
|
|
|
return Ok(ret);
|
|
} else if let Some(flag) = find_flag_by_short!(self, c) {
|
|
debugln!("Parser::parse_short_arg:iter:{}: Found valid flag", c);
|
|
self.settings.set(AS::ValidArgFound);
|
|
// Only flags can be help or version
|
|
try!(self.check_for_help_and_version_char(c));
|
|
ret = try!(self.parse_flag(flag, matcher));
|
|
|
|
// Handle conflicts, requirements, overrides, etc.
|
|
// Must be called here due to mutablilty
|
|
if self.cache.map_or(true, |name| name != flag.b.name) {
|
|
arg_post_processing!(self, flag, matcher);
|
|
self.cache = Some(flag.b.name);
|
|
}
|
|
} else {
|
|
let arg = format!("-{}", c);
|
|
return Err(Error::unknown_argument(&*arg,
|
|
"",
|
|
&*usage::create_error_usage(self,
|
|
matcher,
|
|
None),
|
|
self.color()));
|
|
}
|
|
}
|
|
Ok(ret)
|
|
}
|
|
|
|
fn parse_opt(&self,
|
|
val: Option<&OsStr>,
|
|
opt: &OptBuilder<'a, 'b>,
|
|
had_eq: bool,
|
|
matcher: &mut ArgMatcher<'a>)
|
|
-> ClapResult<ParseResult<'a>> {
|
|
debugln!("Parser::parse_opt; opt={}, val={:?}", opt.b.name, val);
|
|
debugln!("Parser::parse_opt; opt.settings={:?}", opt.b.settings);
|
|
let mut has_eq = false;
|
|
|
|
debug!("Parser::parse_opt; Checking for val...");
|
|
if let Some(fv) = val {
|
|
has_eq = fv.starts_with(&[b'=']) || had_eq;
|
|
let v = fv.trim_left_matches(b'=');
|
|
if !opt.is_set(ArgSettings::EmptyValues) &&
|
|
(v.len_() == 0 || (opt.is_set(ArgSettings::RequireEquals) && !has_eq)) {
|
|
sdebugln!("Found Empty - Error");
|
|
return Err(Error::empty_value(opt,
|
|
&*usage::create_error_usage(self, matcher, None),
|
|
self.color()));
|
|
}
|
|
sdebugln!("Found - {:?}, len: {}", v, v.len_());
|
|
debugln!("Parser::parse_opt: {:?} contains '='...{:?}",
|
|
fv,
|
|
fv.starts_with(&[b'=']));
|
|
try!(self.add_val_to_arg(opt, v, matcher));
|
|
} else if opt.is_set(ArgSettings::RequireEquals) && !opt.is_set(ArgSettings::EmptyValues) {
|
|
sdebugln!("None, but requires equals...Error");
|
|
return Err(Error::empty_value(opt,
|
|
&*usage::create_error_usage(self, matcher, None),
|
|
self.color()));
|
|
|
|
} else {
|
|
sdebugln!("None");
|
|
}
|
|
|
|
matcher.inc_occurrence_of(opt.b.name);
|
|
// Increment or create the group "args"
|
|
self.groups_for_arg(opt.b.name)
|
|
.and_then(|vec| Some(matcher.inc_occurrences_of(&*vec)));
|
|
|
|
if val.is_none() ||
|
|
!has_eq &&
|
|
(opt.is_set(ArgSettings::Multiple) && !opt.is_set(ArgSettings::RequireDelimiter) &&
|
|
matcher.needs_more_vals(opt)) {
|
|
debugln!("Parser::parse_opt: More arg vals required...");
|
|
return Ok(ParseResult::Opt(opt.b.name));
|
|
}
|
|
debugln!("Parser::parse_opt: More arg vals not required...");
|
|
Ok(ParseResult::ValuesDone)
|
|
}
|
|
|
|
fn add_val_to_arg<A>(&self,
|
|
arg: &A,
|
|
val: &OsStr,
|
|
matcher: &mut ArgMatcher<'a>)
|
|
-> ClapResult<ParseResult<'a>>
|
|
where A: AnyArg<'a, 'b> + Display
|
|
{
|
|
debugln!("Parser::add_val_to_arg; arg={}, val={:?}", arg.name(), val);
|
|
debugln!("Parser::add_val_to_arg; trailing_vals={:?}, DontDelimTrailingVals={:?}",
|
|
self.is_set(AS::TrailingValues),
|
|
self.is_set(AS::DontDelimitTrailingValues));
|
|
if !(self.is_set(AS::TrailingValues) && self.is_set(AS::DontDelimitTrailingValues)) {
|
|
if let Some(delim) = arg.val_delim() {
|
|
if val.is_empty_() {
|
|
Ok(try!(self.add_single_val_to_arg(arg, val, matcher)))
|
|
} else {
|
|
let mut iret = ParseResult::ValuesDone;
|
|
for v in val.split(delim as u32 as u8) {
|
|
iret = try!(self.add_single_val_to_arg(arg, v, matcher));
|
|
}
|
|
// If there was a delimiter used, we're not looking for more values
|
|
if val.contains_byte(delim as u32 as u8) ||
|
|
arg.is_set(ArgSettings::RequireDelimiter) {
|
|
iret = ParseResult::ValuesDone;
|
|
}
|
|
Ok(iret)
|
|
}
|
|
} else {
|
|
self.add_single_val_to_arg(arg, val, matcher)
|
|
}
|
|
} else {
|
|
self.add_single_val_to_arg(arg, val, matcher)
|
|
}
|
|
}
|
|
|
|
fn add_single_val_to_arg<A>(&self,
|
|
arg: &A,
|
|
v: &OsStr,
|
|
matcher: &mut ArgMatcher<'a>)
|
|
-> ClapResult<ParseResult<'a>>
|
|
where A: AnyArg<'a, 'b> + Display
|
|
{
|
|
debugln!("Parser::add_single_val_to_arg;");
|
|
debugln!("Parser::add_single_val_to_arg: adding val...{:?}", v);
|
|
if let Some(t) = arg.val_terminator() {
|
|
if t == v {
|
|
return Ok(ParseResult::ValuesDone);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
if matcher.needs_more_vals(arg) {
|
|
return Ok(ParseResult::Opt(arg.name()));
|
|
}
|
|
Ok(ParseResult::ValuesDone)
|
|
}
|
|
|
|
|
|
fn parse_flag(&self,
|
|
flag: &FlagBuilder<'a, 'b>,
|
|
matcher: &mut ArgMatcher<'a>)
|
|
-> ClapResult<ParseResult<'a>> {
|
|
debugln!("Parser::parse_flag;");
|
|
|
|
matcher.inc_occurrence_of(flag.b.name);
|
|
// Increment or create the group "args"
|
|
self.groups_for_arg(flag.b.name)
|
|
.and_then(|vec| Some(matcher.inc_occurrences_of(&*vec)));
|
|
|
|
Ok(ParseResult::Flag)
|
|
}
|
|
|
|
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
|
|
let suffix =
|
|
suggestions::did_you_mean_suffix(arg,
|
|
longs!(self),
|
|
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) = find_opt_by_long!(self, name) {
|
|
self.groups_for_arg(&*opt.b.name)
|
|
.and_then(|grps| Some(matcher.inc_occurrences_of(&*grps)));
|
|
matcher.insert(&*opt.b.name);
|
|
} else if let Some(flg) = find_flag_by_long!(self, name) {
|
|
self.groups_for_arg(&*flg.b.name)
|
|
.and_then(|grps| Some(matcher.inc_occurrences_of(&*grps)));
|
|
matcher.insert(&*flg.b.name);
|
|
}
|
|
}
|
|
|
|
let used_arg = format!("--{}", arg);
|
|
Err(Error::unknown_argument(&*used_arg,
|
|
&*suffix.0,
|
|
&*usage::create_error_usage(self, matcher, None),
|
|
self.color()))
|
|
}
|
|
|
|
// Prints the version to the user and exits if quit=true
|
|
fn print_version<W: Write>(&self, w: &mut W, use_long: bool) -> ClapResult<()> {
|
|
try!(self.write_version(w, use_long));
|
|
w.flush().map_err(Error::from)
|
|
}
|
|
|
|
pub fn write_version<W: Write>(&self, w: &mut W, use_long: bool) -> io::Result<()> {
|
|
let ver = if use_long {
|
|
self.meta
|
|
.long_version
|
|
.unwrap_or_else(|| self.meta.version.unwrap_or(""))
|
|
} else {
|
|
self.meta
|
|
.version
|
|
.unwrap_or_else(|| self.meta.long_version.unwrap_or(""))
|
|
};
|
|
if let Some(bn) = self.meta.bin_name.as_ref() {
|
|
if bn.contains(' ') {
|
|
// Incase we're dealing with subcommands i.e. git mv is translated to git-mv
|
|
write!(w, "{} {}", bn.replace(" ", "-"), ver)
|
|
} else {
|
|
write!(w, "{} {}", &self.meta.name[..], ver)
|
|
}
|
|
} else {
|
|
write!(w, "{} {}", &self.meta.name[..], ver)
|
|
}
|
|
}
|
|
|
|
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<()> {
|
|
Help::write_parser_help(w, self, false)
|
|
}
|
|
|
|
pub fn write_long_help<W: Write>(&self, w: &mut W) -> ClapResult<()> {
|
|
Help::write_parser_help(w, self, true)
|
|
}
|
|
|
|
pub fn write_help_err<W: Write>(&self, w: &mut W) -> ClapResult<()> {
|
|
Help::write_parser_help_to_stderr(w, self)
|
|
}
|
|
|
|
pub fn add_defaults(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
|
|
macro_rules! add_val {
|
|
(@default $_self:ident, $a:ident, $m:ident) => {
|
|
if let Some(ref val) = $a.v.default_val {
|
|
if $m.get($a.b.name).is_none() {
|
|
try!($_self.add_val_to_arg($a, OsStr::new(val), $m));
|
|
|
|
if $_self.cache.map_or(true, |name| name != $a.name()) {
|
|
arg_post_processing!($_self, $a, $m);
|
|
$_self.cache = Some($a.name());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
($_self:ident, $a:ident, $m:ident) => {
|
|
if let Some(ref vm) = $a.v.default_vals_ifs {
|
|
let mut done = false;
|
|
if $m.get($a.b.name).is_none() {
|
|
for &(arg, val, default) in vm.values() {
|
|
let add = if let Some(a) = $m.get(arg) {
|
|
if let Some(v) = val {
|
|
a.vals.iter().any(|value| v == value)
|
|
} else {
|
|
true
|
|
}
|
|
} else {
|
|
false
|
|
};
|
|
if add {
|
|
try!($_self.add_val_to_arg($a, OsStr::new(default), $m));
|
|
if $_self.cache.map_or(true, |name| name != $a.name()) {
|
|
arg_post_processing!($_self, $a, $m);
|
|
$_self.cache = Some($a.name());
|
|
}
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if done {
|
|
continue; // outer loop (outside macro)
|
|
}
|
|
}
|
|
add_val!(@default $_self, $a, $m)
|
|
};
|
|
}
|
|
|
|
for o in &self.opts {
|
|
add_val!(self, o, matcher);
|
|
}
|
|
for p in self.positionals.values() {
|
|
add_val!(self, p, matcher);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn flags(&self) -> Iter<FlagBuilder<'a, 'b>> { self.flags.iter() }
|
|
|
|
pub fn opts(&self) -> Iter<OptBuilder<'a, 'b>> { self.opts.iter() }
|
|
|
|
pub fn positionals(&self) -> vec_map::Values<PosBuilder<'a, 'b>> { self.positionals.values() }
|
|
|
|
pub fn subcommands(&self) -> Iter<App> { self.subcommands.iter() }
|
|
|
|
// Should we color the output? None=determined by output location, true=yes, false=no
|
|
#[doc(hidden)]
|
|
pub fn color(&self) -> ColorWhen {
|
|
debugln!("Parser::color;");
|
|
debug!("Parser::color: Color setting...");
|
|
if self.is_set(AS::ColorNever) {
|
|
sdebugln!("Never");
|
|
ColorWhen::Never
|
|
} else if self.is_set(AS::ColorAlways) {
|
|
sdebugln!("Always");
|
|
ColorWhen::Always
|
|
} else {
|
|
sdebugln!("Auto");
|
|
ColorWhen::Auto
|
|
}
|
|
}
|
|
|
|
pub fn find_any_arg(&self, name: &str) -> Option<&AnyArg> {
|
|
if let Some(f) = find_by_name!(self, name, flags, iter) {
|
|
return Some(f);
|
|
}
|
|
if let Some(o) = find_by_name!(self, name, opts, iter) {
|
|
return Some(o);
|
|
}
|
|
if let Some(p) = find_by_name!(self, name, positionals, values) {
|
|
return Some(p);
|
|
}
|
|
None
|
|
}
|
|
|
|
// Only used for completion scripts due to bin_name messiness
|
|
#[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))]
|
|
pub fn find_subcommand(&'b self, sc: &str) -> Option<&'b App<'a, 'b>> {
|
|
debugln!("Parser::find_subcommand: sc={}", sc);
|
|
debugln!("Parser::find_subcommand: Currently in Parser...{}",
|
|
self.meta.bin_name.as_ref().unwrap());
|
|
for s in self.subcommands.iter() {
|
|
if s.p.meta.bin_name.as_ref().unwrap_or(&String::new()) == sc ||
|
|
(s.p.meta.aliases.is_some() &&
|
|
s.p
|
|
.meta
|
|
.aliases
|
|
.as_ref()
|
|
.unwrap()
|
|
.iter()
|
|
.any(|&(s, _)| {
|
|
s == sc.split(' ').rev().next().expect(INTERNAL_ERROR_MSG)
|
|
})) {
|
|
return Some(s);
|
|
}
|
|
if let Some(app) = s.p.find_subcommand(sc) {
|
|
return Some(app);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
#[inline]
|
|
fn contains_long(&self, l: &str) -> bool { longs!(self).any(|al| al == &l) }
|
|
|
|
#[inline]
|
|
fn contains_short(&self, s: char) -> bool { shorts!(self).any(|arg_s| arg_s == &s) }
|
|
}
|