use std::{
cell::Cell,
ffi::{OsStr, OsString},
};
use crate::{
build::AppSettings as AS,
build::{App, Arg, ArgSettings},
mkeymap::KeyType,
output::{fmt::Colorizer, Help, HelpWriter, Usage},
parse::errors::Error as ClapError,
parse::errors::ErrorKind,
parse::errors::Result as ClapResult,
parse::features::suggestions,
parse::{ArgMatcher, SubCommand},
parse::{Validator, ValueType},
util::{termcolor::ColorChoice, ArgStr, ChildGraph, Id},
INTERNAL_ERROR_MSG, INVALID_UTF8,
};
#[derive(Debug, PartialEq, Clone)]
pub(crate) enum ParseResult {
Flag,
FlagSubCommand(String),
FlagSubCommandShort(String),
Opt(Id),
Pos(Id),
MaybeHyphenValue,
NotFound,
ValuesDone,
}
#[derive(Debug)]
pub(crate) 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(crate) fn next(&mut self) -> Option<(&OsStr, &[OsString])> {
if self.cursor >= self.items.len() {
None
} else {
let current = &self.items[self.cursor];
self.cursor += 1;
let remaining = &self.items[self.cursor..];
Some((current, remaining))
}
}
pub(crate) fn insert(&mut self, insert_items: &[&str]) {
self.items = insert_items
.iter()
.map(OsString::from)
.chain(self.items.drain(self.cursor..))
.collect();
self.cursor = 0;
}
}
pub(crate) struct Parser<'help, 'app> {
pub(crate) app: &'app mut App<'help>,
pub(crate) required: ChildGraph<Id>,
pub(crate) overridden: Vec<Id>,
pub(crate) seen: Vec<Id>,
pub(crate) cur_idx: Cell<usize>,
pub(crate) flag_subcmd_at: Option<usize>,
pub(crate) flag_subcmd_skip: usize,
}
impl<'help, 'app> Parser<'help, 'app> {
pub(crate) fn new(app: &'app mut App<'help>) -> Self {
let mut reqs = ChildGraph::with_capacity(5);
for a in app
.args
.args()
.filter(|a| a.settings.is_set(ArgSettings::Required))
{
reqs.insert(a.id.clone());
}
Parser {
app,
required: reqs,
overridden: Vec::new(),
seen: Vec::new(),
cur_idx: Cell::new(0),
flag_subcmd_at: None,
flag_subcmd_skip: 0,
}
}
#[cfg(debug_assertions)]
fn _verify_positionals(&self) -> bool {
debug!("Parser::_verify_positionals");
let highest_idx = self
.app
.args
.keys()
.filter_map(|x| {
if let KeyType::Position(n) = x {
Some(*n)
} else {
None
}
})
.max()
.unwrap_or(0);
let num_p = self.app.args.keys().filter(|x| x.is_position()).count();
assert!(
highest_idx == num_p,
"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 self.app.get_positionals().any(only_highest) {
let last = &self.app.args[&KeyType::Position(highest_idx)];
let second_to_last = &self.app.args[&KeyType::Position(highest_idx - 1)];
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_values(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_values(true)"
);
let count = self
.app
.get_positionals()
.filter(|p| p.settings.is_set(ArgSettings::MultipleValues) && p.num_vals.is_none())
.count();
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_values(true) set is allowed per \
command, unless the second one also has .last(true) set"
);
}
let mut found = false;
if self.is_set(AS::AllowMissingPositional) {
let mut foundx2 = false;
for p in self.app.get_positionals() {
if foundx2 && !p.is_set(ArgSettings::Required) {
assert!(
p.is_set(ArgSettings::Required),
"Found non-required positional argument 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 {
for p in (1..=num_p).rev().filter_map(|n| self.app.args.get(&n)) {
if found {
assert!(
p.is_set(ArgSettings::Required),
"Found non-required positional argument 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!(
self.app
.get_positionals()
.filter(|p| p.is_set(ArgSettings::Last))
.count()
< 2,
"Only one positional argument may have last(true) set. Found two."
);
if self
.app
.get_positionals()
.any(|p| p.is_set(ArgSettings::Last) && p.is_set(ArgSettings::Required))
&& self.app.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(crate) fn _build(&mut self) {
debug!("Parser::_build");
#[cfg(debug_assertions)]
self._verify_positionals();
for group in &self.app.groups {
if group.required {
let idx = self.required.insert(group.id.clone());
for a in &group.requires {
self.required.insert_child(idx, a.clone());
}
}
}
}
}
impl<'help, 'app> Parser<'help, 'app> {
#[allow(clippy::cognitive_complexity)]
pub(crate) fn get_matches_with(
&mut self,
matcher: &mut ArgMatcher,
it: &mut Input,
) -> ClapResult<()> {
debug!("Parser::get_matches_with");
self._build();
let mut subcmd_name: Option<String> = None;
let mut keep_state = false;
let mut needs_val_of = ParseResult::NotFound;
let mut pos_counter = 1;
let positional_count = self.app.args.keys().filter(|x| x.is_position()).count();
let contains_last = self.app.args.args().any(|x| x.is_set(ArgSettings::Last));
while let Some((arg_os, remaining_args)) = it.next() {
if let Some((_replacer, replaced_items)) = self
.app
.replacers
.iter()
.find(|(key, _)| OsStr::new(key) == arg_os)
{
it.insert(replaced_items);
debug!(
"Parser::get_matches_with: found replacer: {:?}, target: {:?}",
_replacer, replaced_items
);
continue;
}
let arg_os = ArgStr::new(arg_os);
debug!(
"Parser::get_matches_with: Begin parsing '{:?}' ({:?})",
arg_os,
arg_os.as_raw_bytes()
);
if !self.is_set(AS::TrailingValues) {
let can_be_subcommand = self.is_set(AS::SubcommandPrecedenceOverArg)
|| !matches!(needs_val_of, ParseResult::Opt(_) | ParseResult::Pos(_));
if can_be_subcommand {
let sc_name = self.possible_subcommand(&arg_os);
debug!("Parser::get_matches_with: sc={:?}", sc_name);
if let Some(sc_name) = sc_name {
if sc_name == "help" && !self.is_set(AS::NoAutoHelp) {
self.parse_help_subcommand(remaining_args)?;
}
subcmd_name = Some(sc_name.to_owned());
break;
}
}
if self.is_new_arg(&arg_os, &needs_val_of) {
if arg_os == "--" {
debug!("Parser::get_matches_with: setting TrailingVals=true");
self.app.set(AS::TrailingValues);
continue;
} else if arg_os.starts_with("--") {
needs_val_of = self.parse_long_arg(matcher, &arg_os, remaining_args)?;
debug!(
"Parser::get_matches_with: After parse_long_arg {:?}",
needs_val_of
);
match needs_val_of {
ParseResult::Flag | ParseResult::Opt(..) | ParseResult::ValuesDone => {
continue;
}
ParseResult::FlagSubCommand(ref name) => {
debug!("Parser::get_matches_with: FlagSubCommand found in long arg {:?}", name);
subcmd_name = Some(name.to_owned());
break;
}
ParseResult::FlagSubCommandShort(_) => unreachable!(),
_ => (),
}
} else if arg_os.starts_with("-")
&& arg_os.len() != 1
&& !(self.is_set(AS::AllowNegativeNumbers)
&& arg_os.to_string_lossy().parse::<f64>().is_ok())
{
needs_val_of = self.parse_short_arg(matcher, &arg_os)?;
debug!(
"Parser::get_matches_with: After parse_short_arg {:?}",
needs_val_of
);
match needs_val_of {
ParseResult::Opt(..) | ParseResult::Flag | ParseResult::ValuesDone => {
continue;
}
ParseResult::FlagSubCommandShort(ref name) => {
keep_state = self
.flag_subcmd_at
.map(|at| {
it.cursor -= 1;
self.flag_subcmd_skip = self.cur_idx.get() - at + 1;
})
.is_some();
debug!(
"Parser::get_matches_with:FlagSubCommandShort: subcmd_name={}, keep_state={}, flag_subcmd_skip={}",
name,
keep_state,
self.flag_subcmd_skip
);
subcmd_name = Some(name.to_owned());
break;
}
ParseResult::FlagSubCommand(_) => unreachable!(),
_ => (),
}
}
} else if let ParseResult::Opt(id) = needs_val_of {
needs_val_of = self.add_val_to_arg(
&self.app[&id],
arg_os,
matcher,
ValueType::CommandLine,
true,
);
continue;
}
}
let is_second_to_last = pos_counter + 1 == positional_count;
let low_index_mults = is_second_to_last
&& self.app.get_positionals().any(|a| {
a.is_set(ArgSettings::MultipleValues)
&& (positional_count != a.index.unwrap_or(0))
})
&& self
.app
.get_positionals()
.last()
.map_or(false, |p_name| !p_name.is_set(ArgSettings::Last));
let missing_pos = self.is_set(AS::AllowMissingPositional)
&& is_second_to_last
&& !self.is_set(AS::TrailingValues);
debug!(
"Parser::get_matches_with: Positional counter...{}",
pos_counter
);
debug!(
"Parser::get_matches_with: Low index multiples...{:?}",
low_index_mults
);
pos_counter = if low_index_mults || missing_pos {
let bump = if let Some(n) = remaining_args.get(0) {
needs_val_of = if let Some(p) = self
.app
.get_positionals()
.find(|p| p.index == Some(pos_counter))
{
ParseResult::Pos(p.id.clone())
} else {
ParseResult::ValuesDone
};
let n = ArgStr::new(n);
self.is_new_arg(&n, &needs_val_of) || self.possible_subcommand(&n).is_some()
} else {
true
};
if bump {
debug!("Parser::get_matches_with: Bumping the positional counter...");
pos_counter + 1
} else {
pos_counter
}
} else if self.is_set(AS::TrailingValues)
&& (self.is_set(AS::AllowMissingPositional) || contains_last)
{
debug!("Parser::get_matches_with: .last(true) and --, setting last pos");
positional_count
} else {
pos_counter
};
if let Some(p) = self.app.args.get(&pos_counter) {
if p.is_set(ArgSettings::Last) && !self.is_set(AS::TrailingValues) {
return Err(ClapError::unknown_argument(
arg_os.to_string_lossy().to_string(),
None,
Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
if self.is_set(AS::TrailingVarArg) && pos_counter == positional_count {
self.app.settings.set(AS::TrailingValues);
}
self.seen.push(p.id.clone());
let append = self.arg_have_val(matcher, p);
self.add_val_to_arg(p, arg_os, matcher, ValueType::CommandLine, append);
self.inc_occurrence_of_arg(matcher, p);
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() {
if self.is_set(AS::StrictUtf8) && v.to_str().is_none() {
return Err(ClapError::invalid_utf8(
Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
sc_m.add_val_to(
&Id::empty_hash(),
v.to_os_string(),
ValueType::CommandLine,
false,
);
}
matcher.subcommand(SubCommand {
name: sc_name.clone(),
id: sc_name.into(),
matches: sc_m.into_inner(),
});
self.remove_overrides(matcher);
return Validator::new(self).validate(needs_val_of, subcmd_name.is_some(), matcher);
} else {
return Err(self.match_arg_error(&arg_os));
}
}
if let Some(ref pos_sc_name) = subcmd_name {
let sc_name = self
.app
.find_subcommand(pos_sc_name)
.expect(INTERNAL_ERROR_MSG)
.name
.clone();
self.parse_subcommand(&sc_name, matcher, it, keep_state)?;
} 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.to_string(),
Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
} else if self.is_set(AS::SubcommandRequiredElseHelp) {
debug!("Parser::get_matches_with: SubcommandRequiredElseHelp=true");
let message = self.write_help_err()?;
return Err(ClapError {
message,
kind: ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
info: vec![],
source: None,
});
}
self.remove_overrides(matcher);
Validator::new(self).validate(needs_val_of, subcmd_name.is_some(), matcher)
}
fn match_arg_error(&self, arg_os: &ArgStr) -> ClapError {
if self.is_set(AS::TrailingValues) {
if self.possible_subcommand(arg_os).is_some() {
return ClapError::unnecessary_double_dash(
arg_os.to_string_lossy().to_string(),
Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
);
}
}
let candidates =
suggestions::did_you_mean(&arg_os.to_string_lossy(), self.app.all_subcommand_names());
if !candidates.is_empty() {
let candidates: Vec<_> = candidates
.iter()
.map(|candidate| format!("'{}'", candidate))
.collect();
return ClapError::invalid_subcommand(
arg_os.to_string_lossy().to_string(),
candidates.join(" or "),
self.app
.bin_name
.as_ref()
.unwrap_or(&self.app.name)
.to_string(),
Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
);
}
if !self.app.has_args() || self.is_set(AS::InferSubcommands) && self.app.has_subcommands() {
return ClapError::unrecognized_subcommand(
arg_os.to_string_lossy().to_string(),
self.app
.bin_name
.as_ref()
.unwrap_or(&self.app.name)
.to_string(),
self.app.color(),
);
}
ClapError::unknown_argument(
arg_os.to_string_lossy().to_string(),
None,
Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
)
}
pub(crate) fn color_help(&self) -> ColorChoice {
debug!("Parser::color_help");
if self.is_set(AS::ColoredHelp) {
self.app.color()
} else {
ColorChoice::Never
}
}
fn possible_subcommand(&self, arg_os: &ArgStr) -> Option<&str> {
debug!("Parser::possible_subcommand: arg={:?}", arg_os);
if !(self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound)) {
if self.is_set(AS::InferSubcommands) {
let v = self
.app
.all_subcommand_names()
.filter(|s| arg_os.is_prefix_of(s))
.collect::<Vec<_>>();
if v.len() == 1 {
return Some(v[0]);
}
}
if let Some(sc) = self.app.find_subcommand(arg_os) {
return Some(&sc.name);
}
}
None
}
fn possible_long_flag_subcommand(&self, arg_os: &ArgStr) -> Option<&str> {
debug!("Parser::possible_long_flag_subcommand: arg={:?}", arg_os);
if self.is_set(AS::InferSubcommands) {
let options = self
.app
.get_subcommands()
.fold(Vec::new(), |mut options, sc| {
if let Some(long) = sc.long_flag {
if arg_os.is_prefix_of(long) {
options.push(long);
}
options.extend(
sc.get_all_aliases()
.filter(|alias| arg_os.is_prefix_of(alias)),
)
}
options
});
if options.len() == 1 {
return Some(options[0]);
}
for sc in &options {
if sc == arg_os {
return Some(sc);
}
}
} else if let Some(sc_name) = self.app.find_long_subcmd(arg_os) {
return Some(sc_name);
}
None
}
fn parse_help_subcommand(&self, cmds: &[OsString]) -> ClapResult<ParseResult> {
debug!("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 == OsStr::new("help") {
help_help = true;
break; }
if let Some(mut c) = sc.find_subcommand(cmd).cloned() {
c._build();
sc = c;
if i == cmds.len() - 1 {
break;
}
} else if let Some(mut c) = sc.find_subcommand(&cmd.to_string_lossy()).cloned() {
c._build();
sc = c;
if i == cmds.len() - 1 {
break;
}
} else {
return Err(ClapError::unrecognized_subcommand(
cmd.to_string_lossy().to_string(),
self.app
.bin_name
.as_ref()
.unwrap_or(&self.app.name)
.to_string(),
self.app.color(),
));
}
bin_name = format!("{} {}", bin_name, &sc.name);
}
sc
};
let parser = Parser::new(&mut sc);
if help_help {
let mut pb = Arg::new("subcommand")
.index(1)
.setting(ArgSettings::TakesValue)
.setting(ArgSettings::MultipleValues)
.about("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(self.app.is_set(AS::UseLongFormatForHelpSubcommand)))
}
fn is_new_arg(&self, arg_os: &ArgStr, last_result: &ParseResult) -> bool {
debug!("Parser::is_new_arg: {:?}:{:?}", arg_os, last_result);
let want_value = match last_result {
ParseResult::Opt(name) | ParseResult::Pos(name) => {
self.is_set(AS::AllowLeadingHyphen)
|| self.app[name].is_set(ArgSettings::AllowHyphenValues)
|| (self.is_set(AS::AllowNegativeNumbers)
&& arg_os.to_string_lossy().parse::<f64>().is_ok())
}
ParseResult::ValuesDone => return true,
_ => false,
};
debug!("Parser::is_new_arg: want_value={:?}", want_value);
if want_value {
false
} else if arg_os.starts_with("--") {
debug!("Parser::is_new_arg: -- found");
true
} else if arg_os.starts_with("-") {
debug!("Parser::is_new_arg: - found");
arg_os.len() != 1
} else {
debug!("Parser::is_new_arg: value");
false
}
}
fn parse_subcommand(
&mut self,
sc_name: &str,
matcher: &mut ArgMatcher,
it: &mut Input,
keep_state: bool,
) -> ClapResult<()> {
use std::fmt::Write;
debug!("Parser::parse_subcommand");
let mut mid_string = String::from(" ");
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);
}
}
let partial_parsing_enabled = self.is_set(AS::IgnoreErrors);
if let Some(sc) = self.app.subcommands.iter_mut().find(|s| s.name == sc_name) {
let mut sc_matcher = ArgMatcher::default();
let mut sc_names = sc.name.clone();
let mut flag_subcmd = false;
if let Some(l) = sc.long_flag {
sc_names.push_str(&format!(", --{}", l));
flag_subcmd = true;
}
if let Some(s) = sc.short_flag {
sc_names.push_str(&format!(", -{}", s));
flag_subcmd = true;
}
if flag_subcmd {
sc_names = format!("{{{}}}", sc_names);
}
sc.usage = Some(
self.app
.bin_name
.as_ref()
.map(|bin_name| format!("{}{}{}", bin_name, mid_string, sc_names))
.unwrap_or(sc_names),
);
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();
debug!("Parser::parse_subcommand: About to parse sc={}", sc.name);
{
let mut p = Parser::new(sc);
if keep_state {
p.cur_idx.set(self.cur_idx.get());
p.flag_subcmd_at = self.flag_subcmd_at;
p.flag_subcmd_skip = self.flag_subcmd_skip;
}
if let Err(error) = p.get_matches_with(&mut sc_matcher, it) {
if partial_parsing_enabled {
debug!(
"Parser::parse_subcommand: ignored error in subcommand {}: {:?}",
sc_name, error
);
} else {
return Err(error);
}
}
}
let name = sc.name.clone();
matcher.subcommand(SubCommand {
id: Id::from_ref(&name), name,
matches: sc_matcher.into_inner(),
});
}
Ok(())
}
fn check_for_help_and_version_str(&self, arg: &ArgStr) -> ClapResult<()> {
debug!("Parser::check_for_help_and_version_str");
debug!(
"Parser::check_for_help_and_version_str: Checking if --{:?} is help or version...",
arg
);
if let Some(help) = self.app.find(&Id::help_hash()) {
if let Some(h) = help.long {
if arg == h && !self.is_set(AS::NoAutoHelp) {
debug!("Help");
return Err(self.help_err(true));
}
}
}
if let Some(version) = self.app.find(&Id::version_hash()) {
if let Some(v) = version.long {
if arg == v && !self.is_set(AS::NoAutoVersion) {
debug!("Version");
return Err(self.version_err(true));
}
}
}
debug!("Neither");
Ok(())
}
fn check_for_help_and_version_char(&self, arg: char) -> ClapResult<()> {
debug!("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(&Id::help_hash()) {
if let Some(h) = help.short {
if arg == h && !self.is_set(AS::NoAutoHelp) {
debug!("Help");
return Err(self.help_err(false));
}
}
}
if let Some(version) = self.app.find(&Id::version_hash()) {
if let Some(v) = version.short {
if arg == v && !self.is_set(AS::NoAutoVersion) {
debug!("Version");
return Err(self.version_err(false));
}
}
}
debug!("Neither");
Ok(())
}
fn use_long_help(&self) -> bool {
debug!("Parser::use_long_help");
let should_long = |v: &Arg| {
v.long_about.is_some()
|| v.is_set(ArgSettings::HiddenLongHelp)
|| v.is_set(ArgSettings::HiddenShortHelp)
};
self.app.long_about.is_some()
|| self.app.before_long_help.is_some()
|| self.app.after_long_help.is_some()
|| self.app.args.args().any(should_long)
|| self.app.subcommands.iter().any(|s| s.long_about.is_some())
}
fn parse_long_arg(
&mut self,
matcher: &mut ArgMatcher,
full_arg: &ArgStr,
remaining_args: &[OsString],
) -> ClapResult<ParseResult> {
debug!("Parser::parse_long_arg");
self.cur_idx.set(self.cur_idx.get() + 1);
debug!("Parser::parse_long_arg: cur_idx:={}", self.cur_idx.get());
debug!("Parser::parse_long_arg: Does it contain '='...");
let long_arg = full_arg.trim_start_n_matches(2, b'-');
let (arg, val) = if full_arg.contains_byte(b'=') {
let (p0, p1) = long_arg.split_at_byte(b'=');
debug!("Yes '{:?}'", p1);
(p0, Some(p1))
} else {
debug!("No");
(long_arg, None)
};
if let Some(opt) = self.app.args.get(&arg.to_os_string()) {
debug!(
"Parser::parse_long_arg: Found valid opt or flag '{}'",
opt.to_string()
);
self.app.settings.set(AS::ValidArgFound);
self.seen.push(opt.id.clone());
if opt.is_set(ArgSettings::TakesValue) {
return self.parse_opt(&val, opt, matcher);
} else {
self.check_for_help_and_version_str(&arg)?;
return Ok(self.parse_flag(opt, matcher));
}
}
if self.is_set(AS::InferLongArgs) {
let arg_str = arg.to_string_lossy();
let matches: Vec<_> = self
.app
.args
.args()
.filter(|a| {
a.long
.map_or(false, |long| long.starts_with(arg_str.as_ref()))
|| a.aliases
.iter()
.any(|(alias, _)| alias.starts_with(arg_str.as_ref()))
})
.collect();
if let [opt] = matches.as_slice() {
debug!(
"Parser::parse_long_arg: Found valid opt or flag '{}'",
opt.to_string()
);
self.app.settings.set(AS::ValidArgFound);
self.seen.push(opt.id.clone());
if opt.is_set(ArgSettings::TakesValue) {
return self.parse_opt(&val, opt, matcher);
} else {
self.check_for_help_and_version_str(&arg)?;
return Ok(self.parse_flag(opt, matcher));
}
}
}
if let Some(sc_name) = self.possible_long_flag_subcommand(&arg) {
Ok(ParseResult::FlagSubCommand(sc_name.to_string()))
} else if self.is_set(AS::AllowLeadingHyphen) {
Ok(ParseResult::MaybeHyphenValue)
} else {
debug!("Parser::parse_long_arg: Didn't match anything");
let remaining_args: Vec<_> = remaining_args
.iter()
.map(|x| x.to_str().expect(INVALID_UTF8))
.collect();
Err(self.did_you_mean_error(
arg.to_str().expect(INVALID_UTF8),
matcher,
&remaining_args,
))
}
}
fn parse_short_arg(
&mut self,
matcher: &mut ArgMatcher,
full_arg: &ArgStr,
) -> ClapResult<ParseResult> {
debug!("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) && arg.chars().any(|c| !self.app.contains_short(c)) {
debug!(
"Parser::parse_short_arg: LeadingHyphenAllowed yet -{} isn't valid",
arg
);
return Ok(ParseResult::MaybeHyphenValue);
}
let mut ret = ParseResult::NotFound;
let skip = self.flag_subcmd_skip;
self.flag_subcmd_skip = 0;
for c in arg.chars().skip(skip) {
debug!("Parser::parse_short_arg:iter:{}", c);
self.cur_idx.set(self.cur_idx.get() + 1);
debug!(
"Parser::parse_short_arg:iter:{}: cur_idx:={}",
c,
self.cur_idx.get()
);
if let Some(opt) = self.app.args.get(&c) {
debug!(
"Parser::parse_short_arg:iter:{}: Found valid opt or flag",
c
);
self.app.settings.set(AS::ValidArgFound);
self.seen.push(opt.id.clone());
if !opt.is_set(ArgSettings::TakesValue) {
self.check_for_help_and_version_char(c)?;
ret = self.parse_flag(opt, matcher);
continue;
}
let i = arg_os.split(c).next().expect(INTERNAL_ERROR_MSG).len() + c.len_utf8();
debug!(
"Parser::parse_short_arg:iter:{}: i={}, arg_os={:?}",
c, i, arg_os
);
let val = if i != arg_os.len() {
let val = arg_os.split_at_unchecked(i).1;
debug!(
"Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii)",
c,
val.as_raw_bytes(),
val
);
Some(val)
} else {
None
};
return self.parse_opt(&val, opt, matcher);
} else if let Some(sc_name) = self.app.find_short_subcmd(c) {
debug!("Parser::parse_short_arg:iter:{}: subcommand={}", c, sc_name);
let name = sc_name.to_string();
let done_short_args = {
let cur_idx = self.cur_idx.get();
let at = *self.flag_subcmd_at.get_or_insert(cur_idx);
cur_idx - at == arg.len() - 1
};
if done_short_args {
self.flag_subcmd_at = None;
}
return Ok(ParseResult::FlagSubCommandShort(name));
} 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<ArgStr>,
opt: &Arg<'help>,
matcher: &mut ArgMatcher,
) -> ClapResult<ParseResult> {
debug!("Parser::parse_opt; opt={}, val={:?}", opt.name, val);
debug!("Parser::parse_opt; opt.settings={:?}", opt.settings);
let mut has_eq = false;
let no_val = val.is_none();
let no_empty_vals = opt.is_set(ArgSettings::ForbidEmptyValues);
let min_vals_zero = opt.min_vals.unwrap_or(1) == 0;
let require_equals = opt.is_set(ArgSettings::RequireEquals);
debug!("Parser::parse_opt; Checking for val...");
if let Some(fv) = val {
has_eq = fv.starts_with("=");
let v = fv.trim_start_n_matches(1, b'=');
if no_empty_vals && (v.is_empty() || (require_equals && !has_eq)) {
debug!("Found Empty - Error");
return Err(ClapError::empty_value(
opt,
Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
debug!("Found - {:?}, len: {}", v, v.len());
debug!(
"Parser::parse_opt: {:?} contains '='...{:?}",
fv,
fv.starts_with("=")
);
self.add_val_to_arg(opt, v, matcher, ValueType::CommandLine, false);
} else if require_equals {
if min_vals_zero {
debug!("Requires equals, but min_vals == 0");
if !opt.default_missing_vals.is_empty() {
debug!("Parser::parse_opt: has default_missing_vals");
let vals = opt
.default_missing_vals
.iter()
.map(OsString::from)
.collect();
self.add_multiple_vals_to_arg(
opt,
vals,
matcher,
ValueType::CommandLine,
false,
);
};
} else {
debug!("Requires equals but not provided. Error.");
return Err(ClapError::no_equals(
opt,
Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
} else {
debug!("None");
}
self.inc_occurrence_of_arg(matcher, opt);
let needs_delimiter = opt.is_set(ArgSettings::RequireDelimiter);
let multiple = opt.is_set(ArgSettings::MultipleValues);
if no_val && min_vals_zero && require_equals {
debug!("Parser::parse_opt: More arg vals not required...");
Ok(ParseResult::ValuesDone)
} else if no_val
|| (multiple && !needs_delimiter) && !has_eq && matcher.needs_more_vals(opt)
{
debug!("Parser::parse_opt: More arg vals required...");
matcher.new_val_group(&opt.id);
for group in self.app.groups_for_arg(&opt.id) {
matcher.new_val_group(&group);
}
Ok(ParseResult::Opt(opt.id.clone()))
} else {
debug!("Parser::parse_opt: More arg vals not required...");
Ok(ParseResult::ValuesDone)
}
}
fn add_val_to_arg(
&self,
arg: &Arg<'help>,
val: ArgStr,
matcher: &mut ArgMatcher,
ty: ValueType,
append: bool,
) -> ParseResult {
debug!("Parser::add_val_to_arg; arg={}, val={:?}", arg.name, val);
debug!(
"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 {
let arg_split = val.split(delim);
let vals = if let Some(t) = arg.terminator {
let mut vals = vec![];
for val in arg_split {
if t == val {
break;
}
vals.push(val);
}
vals
} else {
arg_split.collect()
};
let vals = vals.into_iter().map(|x| x.into_os_string()).collect();
self.add_multiple_vals_to_arg(arg, vals, matcher, ty, append);
return if val.contains_char(delim)
|| arg.is_set(ArgSettings::RequireDelimiter)
|| !matcher.needs_more_vals(arg)
{
ParseResult::ValuesDone
} else {
ParseResult::Opt(arg.id.clone())
};
}
}
if let Some(t) = arg.terminator {
if t == val {
return ParseResult::ValuesDone;
}
}
self.add_single_val_to_arg(arg, val.to_os_string(), matcher, ty, append);
if matcher.needs_more_vals(arg) {
ParseResult::Opt(arg.id.clone())
} else {
ParseResult::ValuesDone
}
}
fn add_multiple_vals_to_arg(
&self,
arg: &Arg<'help>,
vals: Vec<OsString>,
matcher: &mut ArgMatcher,
ty: ValueType,
append: bool,
) {
if !append {
matcher.new_val_group(&arg.id);
for group in self.app.groups_for_arg(&arg.id) {
matcher.new_val_group(&group);
}
}
for val in vals {
self.add_single_val_to_arg(arg, val, matcher, ty, true);
}
}
fn add_single_val_to_arg(
&self,
arg: &Arg<'help>,
val: OsString,
matcher: &mut ArgMatcher,
ty: ValueType,
append: bool,
) {
debug!("Parser::add_single_val_to_arg: adding val...{:?}", val);
self.cur_idx.set(self.cur_idx.get() + 1);
debug!(
"Parser::add_single_val_to_arg: cur_idx:={}",
self.cur_idx.get()
);
for group in self.app.groups_for_arg(&arg.id) {
matcher.add_val_to(&group, val.clone(), ty, append);
}
matcher.add_val_to(&arg.id, val, ty, append);
matcher.add_index_to(&arg.id, self.cur_idx.get(), ty);
}
fn arg_have_val(&self, matcher: &mut ArgMatcher, arg: &Arg<'help>) -> bool {
matcher.arg_have_val(&arg.id)
}
fn parse_flag(&self, flag: &Arg<'help>, matcher: &mut ArgMatcher) -> ParseResult {
debug!("Parser::parse_flag");
matcher.add_index_to(&flag.id, self.cur_idx.get(), ValueType::CommandLine);
self.inc_occurrence_of_arg(matcher, flag);
ParseResult::Flag
}
fn remove_overrides(&mut self, matcher: &mut ArgMatcher) {
debug!("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() {
debug!("Parser::remove_overrides:iter:{:?}", name);
if let Some(overrider) = self.app.find(name) {
let mut override_self = false;
for overridee in &overrider.overrides {
debug!(
"Parser::remove_overrides:iter:{:?} => {:?}",
name, overrider
);
if *overridee == overrider.id {
override_self = true;
} else {
arg_overrides.push((overrider.id.clone(), overridee));
arg_overrides.push((overridee.clone(), &overrider.id));
}
}
if (self.is_set(AS::AllArgsOverrideSelf) || override_self)
&& !overrider.is_set(ArgSettings::MultipleValues)
&& !overrider.is_set(ArgSettings::MultipleOccurrences)
&& overrider.has_switch()
{
debug!(
"Parser::remove_overrides:iter:{:?}:iter:{:?}: self override",
name, overrider
);
self_override.push(overrider.id.clone());
}
}
}
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).clone());
}
}
}
for name in &self_override {
debug!("Parser::remove_overrides:iter:self:{:?}: resetting", name);
if let Some(ma) = matcher.get_mut(name) {
ma.occurs = 1;
ma.override_vals();
}
}
for name in &to_rem {
debug!("Parser::remove_overrides:iter:{:?}: removing", name);
matcher.remove(name);
self.overridden.push(name.clone());
}
}
pub(crate) fn add_defaults(&mut self, matcher: &mut ArgMatcher) {
debug!("Parser::add_defaults");
for o in self.app.get_opts() {
debug!("Parser::add_defaults:iter:{}:", o.name);
self.add_value(o, matcher, ValueType::DefaultValue);
}
for p in self.app.get_positionals() {
debug!("Parser::add_defaults:iter:{}:", p.name);
self.add_value(p, matcher, ValueType::DefaultValue);
}
}
fn add_value(&self, arg: &Arg<'help>, matcher: &mut ArgMatcher, ty: ValueType) {
if !arg.default_vals_ifs.is_empty() {
debug!("Parser::add_value: has conditional defaults");
if matcher.get(&arg.id).is_none() {
for (id, val, default) in arg.default_vals_ifs.values() {
let add = if let Some(a) = matcher.get(id) {
if let Some(v) = val {
a.vals_flatten().any(|value| v == value)
} else {
true
}
} else {
false
};
if add {
if let Some(default) = default {
self.add_val_to_arg(arg, ArgStr::new(default), matcher, ty, false);
}
return;
}
}
}
} else {
debug!("Parser::add_value: doesn't have conditional defaults");
}
if !arg.default_vals.is_empty() {
debug!("Parser::add_value:iter:{}: has default vals", arg.name);
if matcher.get(&arg.id).is_some() {
debug!("Parser::add_value:iter:{}: was used", arg.name);
} else {
debug!("Parser::add_value:iter:{}: wasn't used", arg.name);
let vals = arg.default_vals.iter().map(OsString::from).collect();
self.add_multiple_vals_to_arg(arg, vals, matcher, ty, false);
}
} else {
debug!(
"Parser::add_value:iter:{}: doesn't have default vals",
arg.name
);
}
if !arg.default_missing_vals.is_empty() {
debug!(
"Parser::add_value:iter:{}: has default missing vals",
arg.name
);
match matcher.get(&arg.id) {
Some(ma) if ma.is_vals_empty() => {
debug!(
"Parser::add_value:iter:{}: has no user defined vals",
arg.name
);
let vals = arg
.default_missing_vals
.iter()
.map(OsString::from)
.collect();
self.add_multiple_vals_to_arg(arg, vals, matcher, ty, false);
}
None => {
debug!("Parser::add_value:iter:{}: wasn't used", arg.name);
}
_ => {
debug!("Parser::add_value:iter:{}: has user defined vals", arg.name);
}
}
} else {
debug!(
"Parser::add_value:iter:{}: doesn't have default missing vals",
arg.name
);
}
}
pub(crate) fn add_env(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> {
if self.app.is_set(AS::DisableEnv) {
return Ok(());
}
for a in self.app.args.args() {
if matcher.get(&a.id).map_or(true, |a| a.occurs == 0) {
if let Some((_, Some(ref val))) = a.env {
let val = ArgStr::new(val);
if a.is_set(ArgSettings::TakesValue) {
self.add_val_to_arg(a, val, matcher, ValueType::EnvVariable, false);
} else {
self.check_for_help_and_version_str(&val)?;
matcher.add_index_to(&a.id, self.cur_idx.get(), ValueType::EnvVariable);
}
}
}
}
Ok(())
}
fn inc_occurrence_of_arg(&self, matcher: &mut ArgMatcher, arg: &Arg<'help>) {
matcher.inc_occurrence_of(&arg.id, arg.is_set(ArgSettings::IgnoreCase));
for group in self.app.groups_for_arg(&arg.id) {
matcher.inc_occurrence_of(&group, false);
}
}
}
impl<'help, 'app> Parser<'help, 'app> {
fn did_you_mean_error(
&mut self,
arg: &str,
matcher: &mut ArgMatcher,
remaining_args: &[&str],
) -> ClapError {
debug!("Parser::did_you_mean_error: arg={}", arg);
let longs = self
.app
.args
.keys()
.filter_map(|x| match x {
KeyType::Long(l) => Some(l.to_string_lossy().into_owned()),
_ => None,
})
.collect::<Vec<_>>();
debug!("Parser::did_you_mean_error: longs={:?}", longs);
let did_you_mean = suggestions::did_you_mean_flag(
arg,
remaining_args,
longs.iter().map(|x| &x[..]),
self.app.subcommands.as_mut_slice(),
);
if let Some((name, _)) = did_you_mean.as_ref() {
if let Some(opt) = self.app.args.get(&name.as_ref()) {
self.inc_occurrence_of_arg(matcher, opt);
}
}
let used: Vec<Id> = matcher
.arg_names()
.filter(|n| {
self.app.find(n).map_or(true, |a| {
!(self.required.contains(&a.id) || a.is_set(ArgSettings::Hidden))
})
})
.cloned()
.collect();
ClapError::unknown_argument(
format!("--{}", arg),
did_you_mean,
Usage::new(self).create_usage_with_title(&*used),
self.app.color(),
)
}
pub(crate) fn write_help_err(&self) -> ClapResult<Colorizer> {
let mut c = Colorizer::new(true, self.color_help());
Help::new(HelpWriter::Buffer(&mut c), self, false).write_help()?;
Ok(c)
}
fn help_err(&self, mut use_long: bool) -> ClapError {
debug!(
"Parser::help_err: use_long={:?}",
use_long && self.use_long_help()
);
use_long = use_long && self.use_long_help();
let mut c = Colorizer::new(false, self.color_help());
match Help::new(HelpWriter::Buffer(&mut c), self, use_long).write_help() {
Err(e) => e.into(),
_ => ClapError {
message: c,
kind: ErrorKind::DisplayHelp,
info: vec![],
source: None,
},
}
}
fn version_err(&self, use_long: bool) -> ClapError {
debug!("Parser::version_err");
let msg = self.app._render_version(use_long);
let mut c = Colorizer::new(false, self.color_help());
c.none(msg);
ClapError {
message: c,
kind: ErrorKind::DisplayVersion,
info: vec![],
source: None,
}
}
}
impl<'help, 'app> Parser<'help, 'app> {
pub(crate) fn is_set(&self, s: AS) -> bool {
self.app.is_set(s)
}
}