use std::cell::Cell;
use std::ffi::{OsStr, OsString};
use std::io::Write;
use std::mem;
#[cfg(not(any(target_os = "windows", target_arch = "wasm32")))]
use std::os::unix::ffi::OsStrExt;
use crate::build::app::Propagation;
use crate::build::AppSettings as AS;
use crate::build::{App, Arg, ArgSettings};
use crate::mkeymap::KeyType;
use crate::output::{Help, Usage};
use crate::parse::errors::Error as ClapError;
use crate::parse::errors::ErrorKind;
use crate::parse::errors::Result as ClapResult;
use crate::parse::features::suggestions;
use crate::parse::Validator;
use crate::parse::{ArgMatcher, SubCommand};
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
use crate::util::OsStrExt3;
use crate::util::{self, ChildGraph, Key, OsStrExt2, EMPTY_HASH};
use crate::INTERNAL_ERROR_MSG;
use crate::INVALID_UTF8;
type Id = u64;
#[derive(Debug, PartialEq, Copy, Clone)]
#[doc(hidden)]
pub enum ParseResult {
Flag,
Opt(Id),
Pos(Id),
MaybeHyphenValue,
MaybeNegNum,
NotFound,
ValuesDone,
}
#[derive(Debug)]
#[doc(hidden)]
pub struct Input {
items: Vec<OsString>,
cursor: usize,
}
impl<I, T> From<I> for Input
where
I: Iterator<Item = T>,
T: Into<OsString> + Clone,
{
fn from(val: I) -> Self {
Self {
items: val.map(|x| x.into()).collect(),
cursor: 0,
}
}
}
impl Input {
pub fn next(&mut self, new: Option<&[&str]>) -> Option<(&OsStr, Option<&OsStr>)> {
if new.is_some() {
let mut new_items: Vec<OsString> = new
.expect(INTERNAL_ERROR_MSG)
.iter()
.map(OsString::from)
.collect();
for i in self.cursor..self.items.len() {
new_items.push(self.items[i].clone());
}
self.items = new_items;
self.cursor = 0;
}
if self.cursor >= self.items.len() {
None
} else {
self.cursor += 1;
if self.cursor >= self.items.len() {
Some((&self.items[self.cursor - 1], None))
} else {
Some((&self.items[self.cursor - 1], Some(&self.items[self.cursor])))
}
}
}
pub fn remaining(&self) -> &[OsString] {
&self.items[self.cursor..]
}
}
#[doc(hidden)]
pub struct Parser<'b, 'c>
where
'b: 'c,
{
pub app: &'c mut App<'b>,
pub required: ChildGraph<Id>,
pub overriden: Vec<Id>,
seen: Vec<Id>,
cur_idx: Cell<usize>,
}
impl<'b, 'c> Parser<'b, 'c>
where
'b: 'c,
{
pub fn new(app: &'c mut App<'b>) -> Self {
let mut reqs = ChildGraph::with_capacity(5);
for a in app
.args
.args
.iter()
.filter(|a| a.settings.is_set(ArgSettings::Required))
.map(|a| a.id)
{
reqs.insert(a);
}
Parser {
app,
required: reqs,
overriden: Vec::new(),
seen: Vec::new(),
cur_idx: Cell::new(0),
}
}
fn _verify_positionals(&self) -> bool {
debugln!("Parser::_verify_positionals;");
let highest_idx = *self
.app
.args
.keys
.iter()
.map(|x| &x.key)
.filter_map(|x| {
if let KeyType::Position(n) = x {
Some(n)
} else {
None
}
})
.max()
.unwrap_or(&0);
let num_p = self
.app
.args
.keys
.iter()
.map(|x| &x.key)
.filter(|x| {
if let KeyType::Position(_) = x {
true
} else {
false
}
})
.count();
assert!(
highest_idx == num_p as u64,
"Found positional argument whose index is {} but there \
are only {} positional arguments defined",
highest_idx,
num_p
);
let only_highest = |a: &Arg| {
a.is_set(ArgSettings::MultipleValues) && (a.index.unwrap_or(0) != highest_idx)
};
if positionals!(self.app).any(only_highest) {
let last = self
.app
.args
.get(&KeyType::Position(highest_idx))
.expect(INTERNAL_ERROR_MSG);
let second_to_last = self
.app
.args
.get(&KeyType::Position(highest_idx - 1))
.expect(INTERNAL_ERROR_MSG);
let ok = last.is_set(ArgSettings::Required)
|| (second_to_last.terminator.is_some()
|| second_to_last.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::MultipleValues)
|| 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 = positionals!(self.app).fold(0, |acc, p| {
if p.settings.is_set(ArgSettings::MultipleValues) && p.num_vals.is_none() {
acc + 1
} else {
acc
}
});
let ok = count <= 1
|| (last.is_set(ArgSettings::Last)
&& last.is_set(ArgSettings::MultipleValues)
&& second_to_last.is_set(ArgSettings::MultipleValues)
&& 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) {
let mut found = false;
let mut foundx2 = false;
for p in positionals!(self.app) {
if foundx2 && !p.is_set(ArgSettings::Required) {
assert!(
p.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.name,
p.index
);
} else if p.is_set(ArgSettings::Required) && !p.is_set(ArgSettings::Last) {
if found {
foundx2 = true;
continue;
}
found = true;
continue;
} else {
found = false;
}
}
} else {
let mut found = false;
for p in (1..=num_p)
.rev()
.filter_map(|n| self.app.args.get(&KeyType::Position(n as u64)))
{
if found {
assert!(
p.is_set(ArgSettings::Required),
"Found positional argument which is not required with a lower \
index than a required positional argument: {:?} index {:?}",
p.name,
p.index
);
} else if p.is_set(ArgSettings::Required) && !p.is_set(ArgSettings::Last) {
found = true;
continue;
}
}
}
assert!(
positionals!(self.app).fold(0, |acc, p| if p.is_set(ArgSettings::Last) {
acc + 1
} else {
acc
}) < 2,
"Only one positional argument may have last(true) set. Found two."
);
if positionals!(self.app)
.any(|p| p.is_set(ArgSettings::Last) && p.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
}
#[allow(clippy::block_in_if_condition_stmt)]
pub(crate) fn _build(&mut self) {
debugln!("Parser::_build;");
let mut key: Vec<(KeyType, usize)> = Vec::new();
let mut counter = 0;
for (i, a) in self.app.args.args.iter_mut().enumerate() {
if a.index == None && a.short == None && a.long == None {
counter += 1;
a.index = Some(counter);
key.push((KeyType::Position(counter), i));
}
if a.is_set(ArgSettings::Required) {
debugln!("Parser::_build: adding {} to default requires", a.name);
let idx = self.required.insert(a.id);
if let Some(ref areqs) = a.requires {
for name in areqs
.iter()
.filter(|&&(val, _)| val.is_none())
.map(|&(_, name)| name)
{
self.required.insert_child(idx, name);
}
}
}
}
for (k, i) in key.into_iter() {
self.app.args.insert_key(k, i);
}
debug_assert!(self._verify_positionals());
if positionals!(self.app).any(|a| {
a.is_set(ArgSettings::MultipleValues)
&& (a.index.unwrap_or(0) as usize
!= self
.app
.args
.keys
.iter()
.map(|x| &x.key)
.filter(|x| x.is_position())
.count())
}) && positionals!(self.app).last().map_or(false, |p_name| {
!self
.app
.find(p_name.id)
.expect(INTERNAL_ERROR_MSG)
.is_set(ArgSettings::Last)
}) {
self.app.settings.set(AS::LowIndexMultiplePositional);
}
for group in &self.app.groups {
if group.required {
let idx = self.required.insert(group.id);
if let Some(ref reqs) = group.requires {
for &a in reqs {
self.required.insert_child(idx, a);
}
}
}
}
}
}
impl<'b, 'c> Parser<'b, 'c>
where
'b: 'c,
{
#[allow(clippy::cognitive_complexity)]
pub fn get_matches_with(&mut self, matcher: &mut ArgMatcher, it: &mut Input) -> ClapResult<()> {
debugln!("Parser::get_matches_with;");
self._build();
let has_args = self.has_args();
let mut subcmd_name: Option<String> = None;
let mut needs_val_of: ParseResult = ParseResult::NotFound;
let mut pos_counter = 1;
let mut replace: Option<&[&str]> = None;
while let Some((arg_os, next_arg)) = it.next(replace) {
replace = None;
debugln!(
"Parser::get_matches_with: Begin parsing '{:?}' ({:?})",
arg_os,
&*arg_os.as_bytes()
);
for (key, val) in &self.app.replacers {
let key_bytes = OsStr::new(key).as_bytes();
if key_bytes == arg_os.as_bytes() {
debugln!("found replacer: {:?}, target: {:?}", key, val);
replace = Some(val);
}
}
if replace.is_some() {
continue;
}
self.unset(AS::ValidNegNumFound);
let starts_new_arg = self.is_new_arg(arg_os, needs_val_of);
if !self.is_set(AS::TrailingValues)
&& 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;
}
if !self.is_set(AS::TrailingValues) {
match needs_val_of {
ParseResult::Opt(_) | ParseResult::Pos(_) => (),
_ => {
let sc_name = self.possible_subcommand(arg_os);
debugln!(
"Parser::get_matches_with: possible_sc={:?}, sc={:?}",
sc_name.is_some(),
sc_name
);
if sc_name.is_some() {
let sc_name = sc_name.expect(INTERNAL_ERROR_MSG);
if sc_name == "help" && !self.is_set(AS::NoAutoHelp) {
self.parse_help_subcommand(it.remaining())?;
}
subcmd_name = Some(sc_name.to_owned());
break;
}
}
}
if starts_new_arg {
if arg_os.starts_with(b"--") {
needs_val_of = 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 {
needs_val_of = self.parse_short_arg(matcher, arg_os)?;
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(ClapError::unknown_argument(
&*arg_os.to_string_lossy(),
None,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
}
ParseResult::Opt(..) | ParseResult::Flag | ParseResult::ValuesDone => {
continue
}
_ => (),
}
}
} else if let ParseResult::Opt(name) = needs_val_of {
let arg = self.app.find(name).expect(INTERNAL_ERROR_MSG);
needs_val_of = self.add_val_to_arg(arg, arg_os, matcher)?;
continue;
}
if !(self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound)
|| self.is_set(AS::AllowExternalSubcommands)
|| self.is_set(AS::InferSubcommands))
{
let cands =
suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self.app));
if !cands.is_empty() {
let cands: Vec<_> =
cands.iter().map(|cand| format!("'{}'", cand)).collect();
return Err(ClapError::invalid_subcommand(
arg_os.to_string_lossy().into_owned(),
cands.join(" or "),
self.app.bin_name.as_ref().unwrap_or(&self.app.name),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
}
}
let positional_count = self
.app
.args
.keys
.iter()
.map(|x| &x.key)
.filter(|x| {
if let KeyType::Position(_) = x {
true
} else {
false
}
})
.count();
let is_second_to_last = positional_count > 1 && (pos_counter == (positional_count - 1));
let low_index_mults = self.is_set(AS::LowIndexMultiplePositional) && is_second_to_last;
let missing_pos = self.is_set(AS::AllowMissingPositional)
&& is_second_to_last
&& !self.is_set(AS::TrailingValues);
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(n) = next_arg {
needs_val_of = if needs_val_of != ParseResult::ValuesDone {
if let Some(p) =
positionals!(self.app).find(|p| p.index == Some(pos_counter as u64))
{
ParseResult::Pos(p.id)
} else {
ParseResult::ValuesDone
}
} else {
ParseResult::ValuesDone
};
let sc_match = { self.possible_subcommand(n).is_some() };
if self.is_new_arg(n, needs_val_of)
|| sc_match
|| !suggestions::did_you_mean(&n.to_string_lossy(), sc_names!(self.app))
.is_empty()
{
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::AllowMissingPositional) && self.is_set(AS::TrailingValues))
|| (self.is_set(AS::ContainsLast) && self.is_set(AS::TrailingValues))
{
debugln!("Parser::get_matches_with: .last(true) and --, setting last pos");
pos_counter = self
.app
.args
.keys
.iter()
.map(|x| &x.key)
.filter(|x| {
if let KeyType::Position(_) = x {
true
} else {
false
}
})
.count();
}
if let Some(p) = positionals!(self.app).find(|p| p.index == Some(pos_counter as u64)) {
if p.is_set(ArgSettings::Last) && !self.is_set(AS::TrailingValues) {
return Err(ClapError::unknown_argument(
&*arg_os.to_string_lossy(),
None,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
if !self.is_set(AS::TrailingValues)
&& (self.is_set(AS::TrailingVarArg)
&& pos_counter
== self
.app
.args
.keys
.iter()
.map(|x| &x.key)
.filter(|x| x.is_position())
.count())
{
self.app.settings.set(AS::TrailingValues);
}
self.seen.push(p.id);
let _ = self.add_val_to_arg(p, arg_os, matcher)?;
matcher.inc_occurrence_of(p.id);
for grp in groups_for_arg!(self.app, p.id) {
matcher.inc_occurrence_of(grp);
}
self.app.settings.set(AS::ValidArgFound);
if !p.settings.is_set(ArgSettings::MultipleValues) {
pos_counter += 1;
}
self.app.settings.set(AS::ValidArgFound);
} else if self.is_set(AS::AllowExternalSubcommands) {
let sc_name = match arg_os.to_str() {
Some(s) => s.to_string(),
None => {
if !self.is_set(AS::StrictUtf8) {
return Err(ClapError::invalid_utf8(
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
arg_os.to_string_lossy().into_owned()
}
};
let mut sc_m = ArgMatcher::default();
while let Some((v, _)) = it.next(None) {
if v.to_str().is_none() && !self.is_set(AS::StrictUtf8) {
return Err(ClapError::invalid_utf8(
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
sc_m.add_val_to(EMPTY_HASH, &v);
}
let id = sc_name.key();
matcher.subcommand(SubCommand {
name: sc_name,
id,
matches: sc_m.into_inner(),
});
break;
} else if !((self.is_set(AS::AllowLeadingHyphen)
|| self.is_set(AS::AllowNegativeNumbers))
&& arg_os.starts_with(b"-"))
&& !self.is_set(AS::InferSubcommands)
{
return Err(ClapError::unknown_argument(
&*arg_os.to_string_lossy(),
None,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
} else if !has_args || self.is_set(AS::InferSubcommands) && self.has_subcommands() {
let cands =
suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self.app));
if !cands.is_empty() {
let cands: Vec<_> = cands.iter().map(|cand| format!("'{}'", cand)).collect();
return Err(ClapError::invalid_subcommand(
arg_os.to_string_lossy().into_owned(),
cands.join(" or "),
self.app.bin_name.as_ref().unwrap_or(&self.app.name),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
} else {
return Err(ClapError::unrecognized_subcommand(
arg_os.to_string_lossy().into_owned(),
self.app.bin_name.as_ref().unwrap_or(&self.app.name),
self.app.color(),
));
}
} else {
return Err(ClapError::unknown_argument(
&*arg_os.to_string_lossy(),
None,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
}
if let Some(ref pos_sc_name) = subcmd_name {
let sc_name = {
find_subcmd!(self.app, *pos_sc_name)
.expect(INTERNAL_ERROR_MSG)
.name
.clone()
};
self.parse_subcommand(&*sc_name, matcher, it)?;
} else if self.is_set(AS::SubcommandRequired) {
let bn = self.app.bin_name.as_ref().unwrap_or(&self.app.name);
return Err(ClapError::missing_subcommand(
bn,
&Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
} else if self.is_set(AS::SubcommandRequiredElseHelp) {
debugln!("Parser::get_matches_with: SubcommandRequiredElseHelp=true");
let mut out = vec![];
self.write_help_err(&mut out)?;
return Err(ClapError {
cause: String::new(),
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
}
self.remove_overrides(matcher);
Validator::new(self).validate(needs_val_of, subcmd_name.is_some(), matcher)
}
fn possible_subcommand(&self, arg_os: &OsStr) -> Option<&str> {
debugln!("Parser::possible_subcommand: arg={:?}", arg_os);
fn starts(h: &str, n: &OsStr) -> bool {
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 None;
}
if !self.is_set(AS::InferSubcommands) {
if let Some(sc) = find_subcmd!(self.app, arg_os) {
return Some(&sc.name);
}
} else {
let v = sc_names!(self.app)
.filter(|s| starts(s, &*arg_os))
.collect::<Vec<_>>();
if v.len() == 1 {
return Some(v[0]);
}
for sc in &v {
if OsStr::new(sc) == arg_os {
return Some(sc);
}
}
}
None
}
fn parse_help_subcommand(&self, cmds: &[OsString]) -> ClapResult<ParseResult> {
debugln!("Parser::parse_help_subcommand;");
let mut help_help = false;
let mut bin_name = self.app.bin_name.as_ref().unwrap_or(&self.app.name).clone();
let mut sc = {
let mut sc = self.app.clone();
for (i, cmd) in cmds.iter().enumerate() {
if &*cmd.to_string_lossy() == "help" {
help_help = true;
break; }
if let Some(id) = find_subcmd!(sc, cmd).map(|x| x.id) {
sc._propagate(Propagation::To(id));
}
if let Some(mut c) = find_subcmd_cloned!(sc, cmd) {
c._build();
sc = c;
if i == cmds.len() - 1 {
break;
}
} else if let Some(mut c) = find_subcmd_cloned!(sc, &*cmd.to_string_lossy()) {
c._build();
sc = c;
if i == cmds.len() - 1 {
break;
}
} else {
return Err(ClapError::unrecognized_subcommand(
cmd.to_string_lossy().into_owned(),
self.app.bin_name.as_ref().unwrap_or(&self.app.name),
self.app.color(),
));
}
bin_name = format!("{} {}", bin_name, &*sc.name);
}
sc
};
let parser = Parser::new(&mut sc);
if help_help {
let mut pb = Arg::with_name("subcommand")
.index(1)
.setting(ArgSettings::MultipleValues)
.help("The subcommand whose help message to display");
pb._build();
parser.app.settings = parser.app.settings | self.app.g_settings;
parser.app.g_settings = self.app.g_settings;
}
if parser.app.bin_name != self.app.bin_name {
parser.app.bin_name = Some(format!("{} {}", bin_name, parser.app.name));
}
Err(parser.help_err(false))
}
#[allow(clippy::wrong_self_convention)]
fn is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: ParseResult) -> bool {
debugln!("Parser::is_new_arg:{:?}:{:?}", 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.app.find(name).expect(INTERNAL_ERROR_MSG);
o.is_set(ArgSettings::AllowHyphenValues) || app_wide_settings
}
ParseResult::Pos(name) => {
let p = self.app.find(name).expect(INTERNAL_ERROR_MSG);
p.is_set(ArgSettings::AllowHyphenValues) || app_wide_settings
}
ParseResult::ValuesDone => return true,
_ => false,
};
debugln!("Parser::is_new_arg: arg_allows_tac={:?}", arg_allows_tac);
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; } else if arg_allows_tac {
return false;
}
true
} else if arg_os.starts_with(b"-") {
debugln!("Parser::is_new_arg: - found");
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
}
fn parse_subcommand(
&mut self,
sc_name: &str,
matcher: &mut ArgMatcher,
it: &mut Input,
) -> ClapResult<()> {
use std::fmt::Write;
debugln!("Parser::parse_subcommand;");
let mut mid_string = String::new();
if !self.is_set(AS::SubcommandsNegateReqs) {
let reqs = Usage::new(self).get_required_usage_from(&[], None, true);
for s in &reqs {
write!(&mut mid_string, " {}", s).expect(INTERNAL_ERROR_MSG);
}
}
mid_string.push_str(" ");
if let Some(id) = find_subcmd!(self.app, sc_name).map(|x| x.id) {
self.app._propagate(Propagation::To(id));
}
if let Some(sc) = subcommands!(self.app, iter_mut).find(|s| s.name == sc_name) {
let mut sc_matcher = ArgMatcher::default();
sc.usage = Some(format!(
"{}{}{}",
self.app.bin_name.as_ref().unwrap_or(&String::new()),
if self.app.bin_name.is_some() {
&*mid_string
} else {
""
},
&*sc.name
));
sc.bin_name = Some(format!(
"{}{}{}",
self.app.bin_name.as_ref().unwrap_or(&String::new()),
if self.app.bin_name.is_some() { " " } else { "" },
&*sc.name
));
sc._build();
debugln!("Parser::parse_subcommand: About to parse sc={}", sc.name);
{
let mut p = Parser::new(sc);
p.get_matches_with(&mut sc_matcher, it)?;
}
let name = sc.name.clone();
let sc_id = name.key();
matcher.subcommand(SubCommand {
id: sc_id, name,
matches: sc_matcher.into_inner(),
});
}
Ok(())
}
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.app.settings.is_set(AS::NoAutoHelp) {
sdebugln!("Help");
return Err(self.help_err(true));
}
if arg == "version" && !self.app.settings.is_set(AS::NoAutoVersion) {
sdebugln!("Version");
return Err(self.version_err(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(help) = self.app.find(util::HELP_HASH) {
if let Some(h) = help.short {
if arg == h && !self.app.settings.is_set(AS::NoAutoHelp) {
sdebugln!("Help");
return Err(self.help_err(false));
}
}
}
if let Some(version) = self.app.find(util::VERSION_HASH) {
if let Some(v) = version.short {
if arg == v && !self.app.settings.is_set(AS::NoAutoVersion) {
sdebugln!("Version");
return Err(self.version_err(false));
}
}
}
sdebugln!("Neither");
Ok(())
}
fn use_long_help(&self) -> bool {
debugln!("Parser::use_long_help;");
let should_long = |v: &Arg| {
v.long_help.is_some()
|| v.is_set(ArgSettings::HiddenLongHelp)
|| v.is_set(ArgSettings::HiddenShortHelp)
};
self.app.long_about.is_some()
|| self.app.args.args.iter().any(|f| should_long(&f))
|| subcommands!(self.app).any(|s| s.long_about.is_some())
}
fn parse_long_arg(
&mut self,
matcher: &mut ArgMatcher,
full_arg: &OsStr,
) -> ClapResult<ParseResult> {
debugln!("Parser::parse_long_arg;");
self.cur_idx.set(self.cur_idx.get() + 1);
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_start_matches(b'-').split_at_byte(b'=');
sdebugln!("Yes '{:?}'", p1);
val = Some(p1);
p0
} else {
sdebugln!("No");
full_arg.trim_start_matches(b'-')
};
if let Some(opt) = self.app.args.get(&KeyType::Long(arg.into())) {
debugln!(
"Parser::parse_long_arg: Found valid opt or flag '{}'",
opt.to_string()
);
self.app.settings.set(AS::ValidArgFound);
self.seen.push(opt.id);
if opt.is_set(ArgSettings::TakesValue) {
return Ok(self.parse_opt(val, opt, val.is_some(), matcher)?);
}
self.check_for_help_and_version_str(arg)?;
self.parse_flag(opt, matcher)?;
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)
}
fn parse_short_arg(
&mut self,
matcher: &mut ArgMatcher,
full_arg: &OsStr,
) -> ClapResult<ParseResult> {
debugln!("Parser::parse_short_arg: full_arg={:?}", full_arg);
let arg_os = full_arg.trim_start_matches(b'-');
let arg = arg_os.to_string_lossy();
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) {
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);
self.cur_idx.set(self.cur_idx.get() + 1);
if let Some(opt) = self.app.args.get(&KeyType::Short(c)) {
debugln!(
"Parser::parse_short_arg:iter:{}: Found valid opt or flag",
c
);
self.app.settings.set(AS::ValidArgFound);
self.seen.push(opt.id);
if !opt.is_set(ArgSettings::TakesValue) {
self.check_for_help_and_version_char(c)?;
ret = self.parse_flag(opt, matcher)?;
continue;
}
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().is_empty() {
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
};
let ret = self.parse_opt(val, opt, false, matcher)?;
return Ok(ret);
} else {
let arg = format!("-{}", c);
return Err(ClapError::unknown_argument(
&*arg,
None,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
}
Ok(ret)
}
fn parse_opt(
&self,
val: Option<&OsStr>,
opt: &Arg<'b>,
had_eq: bool,
matcher: &mut ArgMatcher,
) -> ClapResult<ParseResult> {
debugln!("Parser::parse_opt; opt={}, val={:?}", opt.name, val);
debugln!("Parser::parse_opt; opt.settings={:?}", opt.settings);
let mut has_eq = false;
let no_val = val.is_none();
let empty_vals = opt.is_set(ArgSettings::AllowEmptyValues);
let min_vals_zero = opt.min_vals.unwrap_or(1) == 0;
let needs_eq = opt.is_set(ArgSettings::RequireEquals);
debug!("Parser::parse_opt; Checking for val...");
if let Some(fv) = val {
has_eq = fv.starts_with(&[b'=']) || had_eq;
let v = fv.trim_start_matches(b'=');
if !empty_vals && (v.is_empty() || (needs_eq && !has_eq)) {
sdebugln!("Found Empty - Error");
return Err(ClapError::empty_value(
opt,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
sdebugln!("Found - {:?}, len: {}", v, v.len());
debugln!(
"Parser::parse_opt: {:?} contains '='...{:?}",
fv,
fv.starts_with(&[b'='])
);
self.add_val_to_arg(opt, v, matcher)?;
} else if needs_eq && !(empty_vals || min_vals_zero) {
sdebugln!("None, but requires equals...Error");
return Err(ClapError::empty_value(
opt,
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
} else {
sdebugln!("None");
}
matcher.inc_occurrence_of(opt.id);
for grp in groups_for_arg!(self.app, opt.id) {
matcher.inc_occurrence_of(grp);
}
let needs_delim = opt.is_set(ArgSettings::RequireDelimiter);
let mult = opt.is_set(ArgSettings::MultipleValues);
if no_val && min_vals_zero && !has_eq && needs_eq {
debugln!("Parser::parse_opt: More arg vals not required...");
return Ok(ParseResult::ValuesDone);
} else if no_val || (mult && !needs_delim) && !has_eq && matcher.needs_more_vals(opt) {
debugln!("Parser::parse_opt: More arg vals required...");
return Ok(ParseResult::Opt(opt.id));
}
debugln!("Parser::parse_opt: More arg vals not required...");
Ok(ParseResult::ValuesDone)
}
fn add_val_to_arg(
&self,
arg: &Arg<'b>,
val: &OsStr,
matcher: &mut ArgMatcher,
) -> ClapResult<ParseResult> {
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(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 = self.add_single_val_to_arg(arg, v, matcher)?;
}
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(
&self,
arg: &Arg<'b>,
v: &OsStr,
matcher: &mut ArgMatcher,
) -> ClapResult<ParseResult> {
debugln!("Parser::add_single_val_to_arg;");
debugln!("Parser::add_single_val_to_arg: adding val...{:?}", v);
self.cur_idx.set(self.cur_idx.get() + 1);
if let Some(t) = arg.terminator {
if t == v {
return Ok(ParseResult::ValuesDone);
}
}
matcher.add_val_to(arg.id, v);
matcher.add_index_to(arg.id, self.cur_idx.get());
for grp in groups_for_arg!(self.app, arg.id) {
matcher.add_val_to(grp, v);
}
if matcher.needs_more_vals(arg) {
return Ok(ParseResult::Opt(arg.id));
}
Ok(ParseResult::ValuesDone)
}
fn parse_flag(&self, flag: &Arg<'b>, matcher: &mut ArgMatcher) -> ClapResult<ParseResult> {
debugln!("Parser::parse_flag;");
matcher.inc_occurrence_of(flag.id);
matcher.add_index_to(flag.id, self.cur_idx.get());
for grp in groups_for_arg!(self.app, flag.id) {
matcher.inc_occurrence_of(grp);
}
Ok(ParseResult::Flag)
}
fn remove_overrides(&mut self, matcher: &mut ArgMatcher) {
debugln!("Parser::remove_overrides;");
let mut to_rem: Vec<Id> = Vec::new();
let mut self_override: Vec<Id> = Vec::new();
let mut arg_overrides = Vec::new();
for &name in matcher.arg_names() {
debugln!("Parser::remove_overrides:iter:{};", name);
if let Some(arg) = self.app.find(name) {
let mut handle_self_override = |o| {
if (arg.is_set(ArgSettings::MultipleValues)
|| arg.is_set(ArgSettings::MultipleOccurrences))
|| !arg.has_switch()
{
return true;
}
debugln!(
"Parser::remove_overrides:iter:{}:iter:{}: self override;",
name,
o
);
self_override.push(o);
false
};
if let Some(ref overrides) = arg.overrides {
debugln!("Parser::remove_overrides:iter:{}:{:?};", name, overrides);
for &o in overrides {
if o == arg.id {
if handle_self_override(o) {
continue;
}
} else {
arg_overrides.push((arg.id, o));
arg_overrides.push((o, arg.id));
}
}
}
if self.is_set(AS::AllArgsOverrideSelf) {
let _ = handle_self_override(arg.id);
}
}
}
for &arg in self.seen.iter().rev() {
for &(a, overr) in arg_overrides.iter().filter(|&&(a, _)| a == arg) {
if !to_rem.contains(&a) {
to_rem.push(overr);
}
}
}
for &name in &self_override {
debugln!("Parser::remove_overrides:iter:self:{}: resetting;", name);
if let Some(ma) = matcher.get_mut(name) {
if ma.occurs < 2 {
continue;
}
ma.occurs = 1;
if !ma.vals.is_empty() {
let mut v = vec![ma.vals.pop().expect(INTERNAL_ERROR_MSG)];
mem::swap(&mut v, &mut ma.vals);
}
}
}
for &name in &to_rem {
debugln!("Parser::remove_overrides:iter:{}: removing;", name);
matcher.remove(name);
self.overriden.push(name);
}
}
#[allow(clippy::cognitive_complexity)]
pub(crate) fn add_defaults(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("Parser::add_defaults;");
macro_rules! add_val {
(@default $_self:ident, $a:ident, $m:ident) => {
if let Some(ref vals) = $a.default_vals {
debugln!("Parser::add_defaults:iter:{}: has default vals", $a.name);
if $m
.get($a.id)
.map(|ma| ma.vals.len())
.map(|len| len == 0)
.unwrap_or(false)
{
debugln!(
"Parser::add_defaults:iter:{}: has no user defined vals",
$a.name
);
for val in vals {
$_self.add_val_to_arg($a, val, $m)?;
}
} else if $m.get($a.id).is_some() {
debugln!(
"Parser::add_defaults:iter:{}: has user defined vals",
$a.name
);
} else {
debugln!("Parser::add_defaults:iter:{}: wasn't used", $a.name);
for val in vals {
$_self.add_val_to_arg($a, val, $m)?;
}
}
} else {
debugln!(
"Parser::add_defaults:iter:{}: doesn't have default vals",
$a.name
);
}
};
($_self:ident, $a:ident, $m:ident) => {
if let Some(ref vm) = $a.default_vals_ifs {
sdebugln!(" has conditional defaults");
let mut done = false;
if $m.get($a.id).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 {
$_self.add_val_to_arg($a, OsStr::new(default), $m)?;
done = true;
break;
}
}
}
if done {
continue; }
} else {
sdebugln!(" doesn't have conditional defaults");
}
add_val!(@default $_self, $a, $m)
};
}
for o in opts!(self.app) {
debug!("Parser::add_defaults:iter:{}:", o.name);
add_val!(self, o, matcher);
}
for p in positionals!(self.app) {
debug!("Parser::add_defaults:iter:{}:", p.name);
add_val!(self, p, matcher);
}
Ok(())
}
pub(crate) fn add_env(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> {
for a in self.app.args.args.iter() {
if let Some(ref val) = a.env {
if let Some(ref val) = val.1 {
self.add_val_to_arg(a, OsStr::new(val), matcher)?;
}
}
}
Ok(())
}
}
impl<'b, 'c> Parser<'b, 'c>
where
'b: 'c,
{
fn did_you_mean_error(&mut self, arg: &str, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("Parser::did_you_mean_error: arg={}", arg);
let longs = self
.app
.args
.keys
.iter()
.map(|x| &x.key)
.filter_map(|x| match x {
KeyType::Long(l) => Some(l.to_string_lossy().into_owned()),
_ => None,
})
.collect::<Vec<_>>();
debugln!("Parser::did_you_mean_error: longs={:?}", longs);
let suffix = suggestions::did_you_mean_flag_suffix(
arg,
longs.iter().map(|ref x| &x[..]),
self.app.subcommands.as_mut_slice(),
);
if let Some(ref name) = suffix.1 {
if let Some(opt) = self.app.args.get(&KeyType::Long(OsString::from(name))) {
for g in groups_for_arg!(self.app, opt.id) {
matcher.inc_occurrence_of(g);
}
matcher.insert(opt.id);
}
}
let used: Vec<Id> = matcher
.arg_names()
.filter(|n| {
if let Some(a) = self.app.find(**n) {
!(self.required.contains(a.id) || a.is_set(ArgSettings::Hidden))
} else {
true
}
})
.cloned()
.collect();
let did_you_mean_msg = if suffix.0.is_empty() {
None
} else {
Some(suffix.0)
};
Err(ClapError::unknown_argument(
&*format!("--{}", arg),
did_you_mean_msg,
&*Usage::new(self).create_usage_with_title(&*used),
self.app.color(),
))
}
fn print_version<W: Write>(&self, w: &mut W, use_long: bool) -> ClapResult<()> {
self.app._write_version(w, use_long)?;
w.flush().map_err(ClapError::from)
}
pub(crate) fn write_help_err<W: Write>(&self, w: &mut W) -> ClapResult<()> {
Help::new(w, self, false, true).write_help()
}
fn help_err(&self, mut use_long: bool) -> ClapError {
debugln!(
"Parser::help_err: use_long={:?}",
use_long && self.use_long_help()
);
use_long = use_long && self.use_long_help();
let mut buf = vec![];
match Help::new(&mut buf, self, use_long, false).write_help() {
Err(e) => e,
_ => ClapError {
cause: String::new(),
message: String::from_utf8(buf).unwrap_or_default(),
kind: ErrorKind::HelpDisplayed,
info: None,
},
}
}
fn version_err(&self, use_long: bool) -> ClapError {
debugln!("Parser::version_err: ");
let mut buf = vec![];
match self.print_version(&mut buf, use_long) {
Err(e) => e,
_ => ClapError {
cause: String::new(),
message: String::from_utf8(buf).unwrap_or_default(),
kind: ErrorKind::VersionDisplayed,
info: None,
},
}
}
}
impl<'b, 'c> Parser<'b, 'c>
where
'b: 'c,
{
fn contains_short(&self, s: char) -> bool {
self.app.contains_short(s)
}
pub(crate) fn has_args(&self) -> bool {
self.app.has_args()
}
pub(crate) fn has_opts(&self) -> bool {
self.app.has_opts()
}
pub(crate) fn has_flags(&self) -> bool {
self.app.has_flags()
}
pub(crate) fn has_positionals(&self) -> bool {
self.app.args.keys.iter().any(|x| x.key.is_position())
}
pub(crate) fn has_subcommands(&self) -> bool {
self.app.has_subcommands()
}
pub(crate) fn has_visible_subcommands(&self) -> bool {
self.app.has_visible_subcommands()
}
pub(crate) fn is_set(&self, s: AS) -> bool {
self.app.is_set(s)
}
pub(crate) fn set(&mut self, s: AS) {
self.app.set(s)
}
pub(crate) fn unset(&mut self, s: AS) {
self.app.unset(s)
}
}