From 20431706a863f92cb37dc512fef6e48d192aaf2c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:11:38 +0200 Subject: Merging upstream version 1.66.0+dfsg1. Signed-off-by: Daniel Baumann --- vendor/clap-3.2.20/src/parser/parser.rs | 1729 +++++++++++++++++++++++++++++++ 1 file changed, 1729 insertions(+) create mode 100644 vendor/clap-3.2.20/src/parser/parser.rs (limited to 'vendor/clap-3.2.20/src/parser/parser.rs') diff --git a/vendor/clap-3.2.20/src/parser/parser.rs b/vendor/clap-3.2.20/src/parser/parser.rs new file mode 100644 index 000000000..ad2bc6e9c --- /dev/null +++ b/vendor/clap-3.2.20/src/parser/parser.rs @@ -0,0 +1,1729 @@ +// Std +use std::{ + cell::Cell, + ffi::{OsStr, OsString}, +}; + +// Third Party +use clap_lex::RawOsStr; + +// Internal +use crate::builder::AppSettings as AS; +use crate::builder::{Arg, Command}; +use crate::error::Error as ClapError; +use crate::error::Result as ClapResult; +use crate::mkeymap::KeyType; +use crate::output::fmt::Stream; +use crate::output::{fmt::Colorizer, Usage}; +use crate::parser::features::suggestions; +use crate::parser::{ArgMatcher, SubCommand}; +use crate::parser::{Validator, ValueSource}; +use crate::util::Id; +use crate::ArgAction; +use crate::{INTERNAL_ERROR_MSG, INVALID_UTF8}; + +pub(crate) struct Parser<'help, 'cmd> { + cmd: &'cmd mut Command<'help>, + cur_idx: Cell, + /// Index of the previous flag subcommand in a group of flags. + flag_subcmd_at: Option, + /// Counter indicating the number of items to skip + /// when revisiting the group of flags which includes the flag subcommand. + flag_subcmd_skip: usize, +} + +// Initializing Methods +impl<'help, 'cmd> Parser<'help, 'cmd> { + pub(crate) fn new(cmd: &'cmd mut Command<'help>) -> Self { + Parser { + cmd, + cur_idx: Cell::new(0), + flag_subcmd_at: None, + flag_subcmd_skip: 0, + } + } +} + +// Parsing Methods +impl<'help, 'cmd> Parser<'help, 'cmd> { + // The actual parsing function + #[allow(clippy::cognitive_complexity)] + pub(crate) fn get_matches_with( + &mut self, + matcher: &mut ArgMatcher, + raw_args: &mut clap_lex::RawArgs, + mut args_cursor: clap_lex::ArgCursor, + ) -> ClapResult<()> { + debug!("Parser::get_matches_with"); + // Verify all positional assertions pass + + let mut subcmd_name: Option = None; + let mut keep_state = false; + let mut parse_state = ParseState::ValuesDone; + let mut pos_counter = 1; + + // Already met any valid arg(then we shouldn't expect subcommands after it). + let mut valid_arg_found = false; + // If the user already passed '--'. Meaning only positional args follow. + let mut trailing_values = false; + + // Count of positional args + let positional_count = self + .cmd + .get_keymap() + .keys() + .filter(|x| x.is_position()) + .count(); + // If any arg sets .last(true) + let contains_last = self.cmd.get_arguments().any(|x| x.is_last_set()); + + while let Some(arg_os) = raw_args.next(&mut args_cursor) { + // Recover the replaced items if any. + if let Some(replaced_items) = arg_os + .to_value() + .ok() + .and_then(|a| self.cmd.get_replacement(a)) + { + debug!( + "Parser::get_matches_with: found replacer: {:?}, target: {:?}", + arg_os, replaced_items + ); + raw_args.insert(&args_cursor, replaced_items); + continue; + } + + debug!( + "Parser::get_matches_with: Begin parsing '{:?}' ({:?})", + arg_os.to_value_os(), + arg_os.to_value_os().as_raw_bytes() + ); + + // Has the user already passed '--'? Meaning only positional args follow + if !trailing_values { + if self.cmd.is_subcommand_precedence_over_arg_set() + || !matches!(parse_state, ParseState::Opt(_) | ParseState::Pos(_)) + { + // Does the arg match a subcommand name, or any of its aliases (if defined) + let sc_name = self.possible_subcommand(arg_os.to_value(), valid_arg_found); + debug!("Parser::get_matches_with: sc={:?}", sc_name); + if let Some(sc_name) = sc_name { + #[allow(deprecated)] + if sc_name == "help" + && !self.is_set(AS::NoAutoHelp) + && !self.cmd.is_disable_help_subcommand_set() + { + self.parse_help_subcommand(raw_args.remaining(&mut args_cursor))?; + unreachable!("`parse_help_subcommand` always errors"); + } else { + subcmd_name = Some(sc_name.to_owned()); + } + break; + } + } + + if arg_os.is_escape() { + if matches!(&parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) if + self.cmd[opt].is_allow_hyphen_values_set()) + { + // ParseResult::MaybeHyphenValue, do nothing + } else { + debug!("Parser::get_matches_with: setting TrailingVals=true"); + trailing_values = true; + continue; + } + } else if let Some((long_arg, long_value)) = arg_os.to_long() { + let parse_result = self.parse_long_arg( + matcher, + long_arg, + long_value, + &parse_state, + &mut valid_arg_found, + trailing_values, + )?; + debug!( + "Parser::get_matches_with: After parse_long_arg {:?}", + parse_result + ); + match parse_result { + ParseResult::NoArg => { + unreachable!("`to_long` always has the flag specified") + } + ParseResult::ValuesDone => { + parse_state = ParseState::ValuesDone; + continue; + } + ParseResult::Opt(id) => { + parse_state = ParseState::Opt(id); + continue; + } + ParseResult::FlagSubCommand(name) => { + debug!( + "Parser::get_matches_with: FlagSubCommand found in long arg {:?}", + &name + ); + subcmd_name = Some(name); + break; + } + ParseResult::EqualsNotProvided { arg } => { + let _ = self.resolve_pending(matcher); + return Err(ClapError::no_equals( + self.cmd, + arg, + Usage::new(self.cmd).create_usage_with_title(&[]), + )); + } + ParseResult::NoMatchingArg { arg } => { + let _ = self.resolve_pending(matcher); + let remaining_args: Vec<_> = raw_args + .remaining(&mut args_cursor) + .map(|x| x.to_str().expect(INVALID_UTF8)) + .collect(); + return Err(self.did_you_mean_error(&arg, matcher, &remaining_args)); + } + ParseResult::UnneededAttachedValue { rest, used, arg } => { + let _ = self.resolve_pending(matcher); + return Err(ClapError::too_many_values( + self.cmd, + rest, + arg, + Usage::new(self.cmd).create_usage_no_title(&used), + )); + } + ParseResult::MaybeHyphenValue => { + // Maybe a hyphen value, do nothing. + } + ParseResult::AttachedValueNotConsumed => { + unreachable!() + } + } + } else if let Some(short_arg) = arg_os.to_short() { + // Arg looks like a short flag, and not a possible number + + // Try to parse short args like normal, if allow_hyphen_values or + // AllowNegativeNumbers is set, parse_short_arg will *not* throw + // an error, and instead return Ok(None) + let parse_result = self.parse_short_arg( + matcher, + short_arg, + &parse_state, + pos_counter, + &mut valid_arg_found, + trailing_values, + )?; + // If it's None, we then check if one of those two AppSettings was set + debug!( + "Parser::get_matches_with: After parse_short_arg {:?}", + parse_result + ); + match parse_result { + ParseResult::NoArg => { + // Is a single dash `-`, try positional. + } + ParseResult::ValuesDone => { + parse_state = ParseState::ValuesDone; + continue; + } + ParseResult::Opt(id) => { + parse_state = ParseState::Opt(id); + continue; + } + ParseResult::FlagSubCommand(name) => { + // If there are more short flags to be processed, we should keep the state, and later + // revisit the current group of short flags skipping the subcommand. + keep_state = self + .flag_subcmd_at + .map(|at| { + raw_args + .seek(&mut args_cursor, clap_lex::SeekFrom::Current(-1)); + // Since we are now saving the current state, the number of flags to skip during state recovery should + // be the current index (`cur_idx`) minus ONE UNIT TO THE LEFT of the starting position. + 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); + break; + } + ParseResult::EqualsNotProvided { arg } => { + let _ = self.resolve_pending(matcher); + return Err(ClapError::no_equals( + self.cmd, + arg, + Usage::new(self.cmd).create_usage_with_title(&[]), + )); + } + ParseResult::NoMatchingArg { arg } => { + let _ = self.resolve_pending(matcher); + return Err(ClapError::unknown_argument( + self.cmd, + arg, + None, + Usage::new(self.cmd).create_usage_with_title(&[]), + )); + } + ParseResult::MaybeHyphenValue => { + // Maybe a hyphen value, do nothing. + } + ParseResult::UnneededAttachedValue { .. } + | ParseResult::AttachedValueNotConsumed => unreachable!(), + } + } + + if let ParseState::Opt(id) = &parse_state { + // Assume this is a value of a previous arg. + + // get the option so we can check the settings + let arg_values = matcher.pending_values_mut(id, None); + let arg = &self.cmd[id]; + let parse_result = self.split_arg_values( + arg, + arg_os.to_value_os(), + trailing_values, + arg_values, + ); + let parse_result = parse_result.unwrap_or_else(|| { + if matcher.needs_more_vals(arg) { + ParseResult::Opt(arg.id.clone()) + } else { + ParseResult::ValuesDone + } + }); + parse_state = match parse_result { + ParseResult::Opt(id) => ParseState::Opt(id), + ParseResult::ValuesDone => ParseState::ValuesDone, + _ => unreachable!(), + }; + // get the next value from the iterator + continue; + } + } + + // Correct pos_counter. + pos_counter = { + let is_second_to_last = pos_counter + 1 == positional_count; + + // The last positional argument, or second to last positional + // argument may be set to .multiple_values(true) or `.multiple_occurrences(true)` + let low_index_mults = is_second_to_last + && self + .cmd + .get_positionals() + .any(|a| a.is_multiple() && (positional_count != a.index.unwrap_or(0))) + && self + .cmd + .get_positionals() + .last() + .map_or(false, |p_name| !p_name.is_last_set()); + + let missing_pos = self.cmd.is_allow_missing_positional_set() + && is_second_to_last + && !trailing_values; + + debug!( + "Parser::get_matches_with: Positional counter...{}", + pos_counter + ); + debug!( + "Parser::get_matches_with: Low index multiples...{:?}", + low_index_mults + ); + + if low_index_mults || missing_pos { + let skip_current = if let Some(n) = raw_args.peek(&args_cursor) { + if let Some(arg) = self + .cmd + .get_positionals() + .find(|a| a.index == Some(pos_counter)) + { + // If next value looks like a new_arg or it's a + // subcommand, skip positional argument under current + // pos_counter(which means current value cannot be a + // positional argument with a value next to it), assume + // current value matches the next arg. + self.is_new_arg(&n, arg) + || self + .possible_subcommand(n.to_value(), valid_arg_found) + .is_some() + } else { + true + } + } else { + true + }; + + if skip_current { + debug!("Parser::get_matches_with: Bumping the positional counter..."); + pos_counter + 1 + } else { + pos_counter + } + } else if trailing_values + && (self.cmd.is_allow_missing_positional_set() || contains_last) + { + // Came to -- and one positional has .last(true) set, so we go immediately + // to the last (highest index) positional + debug!("Parser::get_matches_with: .last(true) and --, setting last pos"); + positional_count + } else { + pos_counter + } + }; + + if let Some(arg) = self.cmd.get_keymap().get(&pos_counter) { + if arg.is_last_set() && !trailing_values { + let _ = self.resolve_pending(matcher); + return Err(ClapError::unknown_argument( + self.cmd, + arg_os.display().to_string(), + None, + Usage::new(self.cmd).create_usage_with_title(&[]), + )); + } + + if self.cmd.is_trailing_var_arg_set() && pos_counter == positional_count { + trailing_values = true; + } + + if matcher.pending_arg_id() != Some(&arg.id) || !arg.is_multiple_values_set() { + self.resolve_pending(matcher)?; + } + let arg_values = matcher.pending_values_mut(&arg.id, Some(Identifier::Index)); + let _parse_result = + self.split_arg_values(arg, arg_os.to_value_os(), trailing_values, arg_values); + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!( + "Parser::get_matches_with: Ignoring state {:?}; positionals do their own thing", + _parse_result + ); + } + } + + // Only increment the positional counter if it doesn't allow multiples + if !arg.is_multiple() { + pos_counter += 1; + parse_state = ParseState::ValuesDone; + } else { + parse_state = ParseState::Pos(arg.id.clone()); + } + valid_arg_found = true; + } else if let Some(external_parser) = + self.cmd.get_external_subcommand_value_parser().cloned() + { + // Get external subcommand name + let sc_name = match arg_os.to_value() { + Ok(s) => s.to_string(), + Err(_) => { + let _ = self.resolve_pending(matcher); + return Err(ClapError::invalid_utf8( + self.cmd, + Usage::new(self.cmd).create_usage_with_title(&[]), + )); + } + }; + + // Collect the external subcommand args + let mut sc_m = ArgMatcher::new(self.cmd); + if cfg!(feature = "unstable-v4") || !raw_args.is_end(&args_cursor) { + sc_m.start_occurrence_of_external(self.cmd); + } + + for raw_val in raw_args.remaining(&mut args_cursor) { + let val = external_parser.parse_ref(self.cmd, None, raw_val)?; + let external_id = &Id::empty_hash(); + sc_m.add_val_to(external_id, val, raw_val.to_os_string()); + } + + matcher.subcommand(SubCommand { + id: Id::from(&*sc_name), + name: sc_name, + matches: sc_m.into_inner(), + }); + + self.resolve_pending(matcher)?; + #[cfg(feature = "env")] + self.add_env(matcher)?; + self.add_defaults(matcher)?; + return Validator::new(self.cmd).validate(parse_state, matcher); + } else { + // Start error processing + let _ = self.resolve_pending(matcher); + return Err(self.match_arg_error(&arg_os, valid_arg_found, trailing_values)); + } + } + + if let Some(ref pos_sc_name) = subcmd_name { + let sc_name = self + .cmd + .find_subcommand(pos_sc_name) + .expect(INTERNAL_ERROR_MSG) + .get_name() + .to_owned(); + self.parse_subcommand(&sc_name, matcher, raw_args, args_cursor, keep_state)?; + } + + self.resolve_pending(matcher)?; + #[cfg(feature = "env")] + self.add_env(matcher)?; + self.add_defaults(matcher)?; + Validator::new(self.cmd).validate(parse_state, matcher) + } + + fn match_arg_error( + &self, + arg_os: &clap_lex::ParsedArg<'_>, + valid_arg_found: bool, + trailing_values: bool, + ) -> ClapError { + // If argument follows a `--` + if trailing_values { + // If the arg matches a subcommand name, or any of its aliases (if defined) + if self + .possible_subcommand(arg_os.to_value(), valid_arg_found) + .is_some() + { + return ClapError::unnecessary_double_dash( + self.cmd, + arg_os.display().to_string(), + Usage::new(self.cmd).create_usage_with_title(&[]), + ); + } + } + let candidates = suggestions::did_you_mean( + &arg_os.display().to_string(), + self.cmd.all_subcommand_names(), + ); + // If the argument looks like a subcommand. + if !candidates.is_empty() { + let candidates: Vec<_> = candidates + .iter() + .map(|candidate| format!("'{}'", candidate)) + .collect(); + return ClapError::invalid_subcommand( + self.cmd, + arg_os.display().to_string(), + candidates.join(" or "), + self.cmd + .get_bin_name() + .unwrap_or_else(|| self.cmd.get_name()) + .to_owned(), + Usage::new(self.cmd).create_usage_with_title(&[]), + ); + } + // If the argument must be a subcommand. + if !self.cmd.has_args() || self.cmd.is_infer_subcommands_set() && self.cmd.has_subcommands() + { + return ClapError::unrecognized_subcommand( + self.cmd, + arg_os.display().to_string(), + Usage::new(self.cmd).create_usage_with_title(&[]), + ); + } + ClapError::unknown_argument( + self.cmd, + arg_os.display().to_string(), + None, + Usage::new(self.cmd).create_usage_with_title(&[]), + ) + } + + // Checks if the arg matches a subcommand name, or any of its aliases (if defined) + fn possible_subcommand( + &self, + arg: Result<&str, &RawOsStr>, + valid_arg_found: bool, + ) -> Option<&str> { + debug!("Parser::possible_subcommand: arg={:?}", arg); + let arg = arg.ok()?; + + if !(self.cmd.is_args_conflicts_with_subcommands_set() && valid_arg_found) { + if self.cmd.is_infer_subcommands_set() { + // For subcommand `test`, we accepts it's prefix: `t`, `te`, + // `tes` and `test`. + let v = self + .cmd + .all_subcommand_names() + .filter(|s| s.starts_with(arg)) + .collect::>(); + + if v.len() == 1 { + return Some(v[0]); + } + + // If there is any ambiguity, fallback to non-infer subcommand + // search. + } + if let Some(sc) = self.cmd.find_subcommand(arg) { + return Some(sc.get_name()); + } + } + None + } + + // Checks if the arg matches a long flag subcommand name, or any of its aliases (if defined) + fn possible_long_flag_subcommand(&self, arg: &str) -> Option<&str> { + debug!("Parser::possible_long_flag_subcommand: arg={:?}", arg); + if self.cmd.is_infer_subcommands_set() { + let options = self + .cmd + .get_subcommands() + .fold(Vec::new(), |mut options, sc| { + if let Some(long) = sc.get_long_flag() { + if long.starts_with(arg) { + options.push(long); + } + options.extend(sc.get_all_aliases().filter(|alias| alias.starts_with(arg))) + } + options + }); + if options.len() == 1 { + return Some(options[0]); + } + + for sc in options { + if sc == arg { + return Some(sc); + } + } + } else if let Some(sc_name) = self.cmd.find_long_subcmd(arg) { + return Some(sc_name); + } + None + } + + fn parse_help_subcommand( + &self, + cmds: impl Iterator, + ) -> ClapResult { + debug!("Parser::parse_help_subcommand"); + + let mut cmd = self.cmd.clone(); + let sc = { + let mut sc = &mut cmd; + + for cmd in cmds { + sc = if let Some(sc_name) = + sc.find_subcommand(cmd).map(|sc| sc.get_name().to_owned()) + { + sc._build_subcommand(&sc_name).unwrap() + } else { + return Err(ClapError::unrecognized_subcommand( + sc, + cmd.to_string_lossy().into_owned(), + Usage::new(sc).create_usage_with_title(&[]), + )); + }; + } + + sc + }; + let parser = Parser::new(sc); + + Err(parser.help_err(true, Stream::Stdout)) + } + + fn is_new_arg(&self, next: &clap_lex::ParsedArg<'_>, current_positional: &Arg) -> bool { + #![allow(clippy::needless_bool)] // Prefer consistent if/else-if ladder + + debug!( + "Parser::is_new_arg: {:?}:{:?}", + next.to_value_os(), + current_positional.name + ); + + if self.cmd.is_allow_hyphen_values_set() + || self.cmd[¤t_positional.id].is_allow_hyphen_values_set() + || (self.cmd.is_allow_negative_numbers_set() && next.is_number()) + { + // If allow hyphen, this isn't a new arg. + debug!("Parser::is_new_arg: Allow hyphen"); + false + } else if next.is_long() { + // If this is a long flag, this is a new arg. + debug!("Parser::is_new_arg: -- found"); + true + } else if next.is_short() { + // If this is a short flag, this is a new arg. But a singe '-' by + // itself is a value and typically means "stdin" on unix systems. + debug!("Parser::is_new_arg: - found"); + true + } else { + // Nothing special, this is a value. + debug!("Parser::is_new_arg: value"); + false + } + } + + fn parse_subcommand( + &mut self, + sc_name: &str, + matcher: &mut ArgMatcher, + raw_args: &mut clap_lex::RawArgs, + args_cursor: clap_lex::ArgCursor, + keep_state: bool, + ) -> ClapResult<()> { + debug!("Parser::parse_subcommand"); + + let partial_parsing_enabled = self.cmd.is_ignore_errors_set(); + + if let Some(sc) = self.cmd._build_subcommand(sc_name) { + let mut sc_matcher = ArgMatcher::new(sc); + + debug!( + "Parser::parse_subcommand: About to parse sc={}", + sc.get_name() + ); + + { + let mut p = Parser::new(sc); + // HACK: maintain indexes between parsers + // FlagSubCommand short arg needs to revisit the current short args, but skip the subcommand itself + 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, raw_args, args_cursor) { + if partial_parsing_enabled { + debug!( + "Parser::parse_subcommand: ignored error in subcommand {}: {:?}", + sc_name, error + ); + } else { + return Err(error); + } + } + } + matcher.subcommand(SubCommand { + id: sc.get_id(), + name: sc.get_name().to_owned(), + matches: sc_matcher.into_inner(), + }); + } + Ok(()) + } + + fn parse_long_arg( + &mut self, + matcher: &mut ArgMatcher, + long_arg: Result<&str, &RawOsStr>, + long_value: Option<&RawOsStr>, + parse_state: &ParseState, + valid_arg_found: &mut bool, + trailing_values: bool, + ) -> ClapResult { + // maybe here lifetime should be 'a + debug!("Parser::parse_long_arg"); + + if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) if + self.cmd[opt].is_allow_hyphen_values_set()) + { + return Ok(ParseResult::MaybeHyphenValue); + } + + debug!("Parser::parse_long_arg: Does it contain '='..."); + let long_arg = match long_arg { + Ok(long_arg) => long_arg, + Err(long_arg) => { + return Ok(ParseResult::NoMatchingArg { + arg: long_arg.to_str_lossy().into_owned(), + }); + } + }; + if long_arg.is_empty() { + debug_assert!( + long_value.is_some(), + "`--` should be filtered out before this point" + ); + } + + let arg = if let Some(arg) = self.cmd.get_keymap().get(long_arg) { + debug!( + "Parser::parse_long_arg: Found valid arg or flag '{}'", + arg.to_string() + ); + Some((long_arg, arg)) + } else if self.cmd.is_infer_long_args_set() { + self.cmd.get_arguments().find_map(|a| { + if let Some(long) = a.long { + if long.starts_with(long_arg) { + return Some((long, a)); + } + } + a.aliases + .iter() + .find_map(|(alias, _)| alias.starts_with(long_arg).then(|| (*alias, a))) + }) + } else { + None + }; + + if let Some((_long_arg, arg)) = arg { + let ident = Identifier::Long; + *valid_arg_found = true; + if arg.is_takes_value_set() { + debug!( + "Parser::parse_long_arg({:?}): Found an arg with value '{:?}'", + long_arg, &long_value + ); + let has_eq = long_value.is_some(); + self.parse_opt_value(ident, long_value, arg, matcher, trailing_values, has_eq) + } else if let Some(rest) = long_value { + let required = self.cmd.required_graph(); + debug!( + "Parser::parse_long_arg({:?}): Got invalid literal `{:?}`", + long_arg, rest + ); + let used: Vec = matcher + .arg_ids() + .filter(|arg_id| { + matcher.check_explicit(arg_id, crate::builder::ArgPredicate::IsPresent) + }) + .filter(|&n| { + self.cmd + .find(n) + .map_or(true, |a| !(a.is_hide_set() || required.contains(&a.id))) + }) + .cloned() + .collect(); + + Ok(ParseResult::UnneededAttachedValue { + rest: rest.to_str_lossy().into_owned(), + used, + arg: arg.to_string(), + }) + } else { + debug!("Parser::parse_long_arg({:?}): Presence validated", long_arg); + self.react(Some(ident), ValueSource::CommandLine, arg, vec![], matcher) + } + } else if let Some(sc_name) = self.possible_long_flag_subcommand(long_arg) { + Ok(ParseResult::FlagSubCommand(sc_name.to_string())) + } else if self.cmd.is_allow_hyphen_values_set() { + Ok(ParseResult::MaybeHyphenValue) + } else { + Ok(ParseResult::NoMatchingArg { + arg: long_arg.to_owned(), + }) + } + } + + fn parse_short_arg( + &mut self, + matcher: &mut ArgMatcher, + mut short_arg: clap_lex::ShortFlags<'_>, + parse_state: &ParseState, + // change this to possible pos_arg when removing the usage of &mut Parser. + pos_counter: usize, + valid_arg_found: &mut bool, + trailing_values: bool, + ) -> ClapResult { + debug!("Parser::parse_short_arg: short_arg={:?}", short_arg); + + #[allow(clippy::blocks_in_if_conditions)] + if self.cmd.is_allow_negative_numbers_set() && short_arg.is_number() { + debug!("Parser::parse_short_arg: negative number"); + return Ok(ParseResult::MaybeHyphenValue); + } else if self.cmd.is_allow_hyphen_values_set() + && short_arg + .clone() + .any(|c| !c.map(|c| self.cmd.contains_short(c)).unwrap_or_default()) + { + debug!("Parser::parse_short_args: contains non-short flag"); + return Ok(ParseResult::MaybeHyphenValue); + } else if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) + if self.cmd[opt].is_allow_hyphen_values_set()) + { + debug!("Parser::parse_short_args: prior arg accepts hyphenated values",); + return Ok(ParseResult::MaybeHyphenValue); + } else if self + .cmd + .get_keymap() + .get(&pos_counter) + .map_or(false, |arg| { + arg.is_allow_hyphen_values_set() && !arg.is_last_set() + }) + { + debug!( + "Parser::parse_short_args: positional at {} allows hyphens", + pos_counter + ); + return Ok(ParseResult::MaybeHyphenValue); + } + + let mut ret = ParseResult::NoArg; + + let skip = self.flag_subcmd_skip; + self.flag_subcmd_skip = 0; + let res = short_arg.advance_by(skip); + debug_assert_eq!( + res, + Ok(()), + "tracking of `flag_subcmd_skip` is off for `{:?}`", + short_arg + ); + while let Some(c) = short_arg.next_flag() { + let c = match c { + Ok(c) => c, + Err(rest) => { + return Ok(ParseResult::NoMatchingArg { + arg: format!("-{}", rest.to_str_lossy()), + }); + } + }; + debug!("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(arg) = self.cmd.get_keymap().get(&c) { + let ident = Identifier::Short; + debug!( + "Parser::parse_short_arg:iter:{}: Found valid opt or flag", + c + ); + *valid_arg_found = true; + if !arg.is_takes_value_set() { + ret = + self.react(Some(ident), ValueSource::CommandLine, arg, vec![], matcher)?; + continue; + } + + // Check for trailing concatenated value + // + // Cloning the iterator, so we rollback if it isn't there. + let val = short_arg.clone().next_value_os().unwrap_or_default(); + debug!( + "Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii), short_arg={:?}", + c, val, val.as_raw_bytes(), short_arg + ); + let val = Some(val).filter(|v| !v.is_empty()); + + // Default to "we're expecting a value later". + // + // If attached value is not consumed, we may have more short + // flags to parse, continue. + // + // e.g. `-xvf`, when require_equals && x.min_vals == 0, we don't + // consume the `vf`, even if it's provided as value. + let (val, has_eq) = if let Some(val) = val.and_then(|v| v.strip_prefix('=')) { + (Some(val), true) + } else { + (val, false) + }; + match self.parse_opt_value(ident, val, arg, matcher, trailing_values, has_eq)? { + ParseResult::AttachedValueNotConsumed => continue, + x => return Ok(x), + } + } + + return if let Some(sc_name) = self.cmd.find_short_subcmd(c) { + debug!("Parser::parse_short_arg:iter:{}: subcommand={}", c, sc_name); + // Make sure indices get updated before reading `self.cur_idx` + self.resolve_pending(matcher)?; + self.cur_idx.set(self.cur_idx.get() + 1); + debug!("Parser::parse_short_arg: cur_idx:={}", self.cur_idx.get()); + + let name = sc_name.to_string(); + // Get the index of the previously saved flag subcommand in the group of flags (if exists). + // If it is a new flag subcommand, then the formentioned index should be the current one + // (ie. `cur_idx`), and should be registered. + let cur_idx = self.cur_idx.get(); + self.flag_subcmd_at.get_or_insert(cur_idx); + let done_short_args = short_arg.is_empty(); + if done_short_args { + self.flag_subcmd_at = None; + } + Ok(ParseResult::FlagSubCommand(name)) + } else { + Ok(ParseResult::NoMatchingArg { + arg: format!("-{}", c), + }) + }; + } + Ok(ret) + } + + fn parse_opt_value( + &self, + ident: Identifier, + attached_value: Option<&RawOsStr>, + arg: &Arg<'help>, + matcher: &mut ArgMatcher, + trailing_values: bool, + has_eq: bool, + ) -> ClapResult { + debug!( + "Parser::parse_opt_value; arg={}, val={:?}, has_eq={:?}", + arg.name, attached_value, has_eq + ); + debug!("Parser::parse_opt_value; arg.settings={:?}", arg.settings); + + debug!("Parser::parse_opt_value; Checking for val..."); + // require_equals is set, but no '=' is provided, try throwing error. + if arg.is_require_equals_set() && !has_eq { + if arg.min_vals == Some(0) { + debug!("Requires equals, but min_vals == 0"); + let mut arg_values = Vec::new(); + // We assume this case is valid: require equals, but min_vals == 0. + if !arg.default_missing_vals.is_empty() { + debug!("Parser::parse_opt_value: has default_missing_vals"); + for v in arg.default_missing_vals.iter() { + let trailing_values = false; // CLI should not be affecting default_missing_values + let _parse_result = self.split_arg_values( + arg, + &RawOsStr::new(v), + trailing_values, + &mut arg_values, + ); + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::parse_opt_value: Ignoring state {:?}; no values accepted after default_missing_values", _parse_result); + } + } + } + }; + let react_result = self.react( + Some(ident), + ValueSource::CommandLine, + arg, + arg_values, + matcher, + )?; + debug_assert_eq!(react_result, ParseResult::ValuesDone); + if attached_value.is_some() { + Ok(ParseResult::AttachedValueNotConsumed) + } else { + Ok(ParseResult::ValuesDone) + } + } else { + debug!("Requires equals but not provided. Error."); + Ok(ParseResult::EqualsNotProvided { + arg: arg.to_string(), + }) + } + } else if let Some(v) = attached_value { + let mut arg_values = Vec::new(); + let parse_result = self.split_arg_values(arg, v, trailing_values, &mut arg_values); + let react_result = self.react( + Some(ident), + ValueSource::CommandLine, + arg, + arg_values, + matcher, + )?; + debug_assert_eq!(react_result, ParseResult::ValuesDone); + let mut parse_result = parse_result.unwrap_or_else(|| { + if matcher.needs_more_vals(arg) { + ParseResult::Opt(arg.id.clone()) + } else { + ParseResult::ValuesDone + } + }); + if parse_result != ParseResult::ValuesDone { + debug!("Parser::parse_opt_value: Overriding state {:?}; no values accepted after attached", parse_result); + parse_result = ParseResult::ValuesDone; + } + Ok(parse_result) + } else { + debug!("Parser::parse_opt_value: More arg vals required..."); + self.resolve_pending(matcher)?; + matcher.pending_values_mut(&arg.id, Some(ident)); + Ok(ParseResult::Opt(arg.id.clone())) + } + } + + fn split_arg_values( + &self, + arg: &Arg<'help>, + val: &RawOsStr, + trailing_values: bool, + output: &mut Vec, + ) -> Option { + debug!("Parser::split_arg_values; arg={}, val={:?}", arg.name, val); + debug!( + "Parser::split_arg_values; trailing_values={:?}, DontDelimTrailingVals={:?}", + trailing_values, + self.cmd.is_dont_delimit_trailing_values_set() + ); + + let mut delim = arg.val_delim; + if trailing_values && self.cmd.is_dont_delimit_trailing_values_set() { + delim = None; + } + match delim { + Some(delim) if val.contains(delim) => { + let vals = val.split(delim).map(|x| x.to_os_str().into_owned()); + for raw_val in vals { + if Some(raw_val.as_os_str()) == arg.terminator.map(OsStr::new) { + return Some(ParseResult::ValuesDone); + } + output.push(raw_val); + } + // Delimited values are always considered the final value + Some(ParseResult::ValuesDone) + } + _ if Some(val) == arg.terminator.map(RawOsStr::from_str) => { + Some(ParseResult::ValuesDone) + } + _ => { + output.push(val.to_os_str().into_owned()); + if arg.is_require_value_delimiter_set() { + Some(ParseResult::ValuesDone) + } else { + None + } + } + } + } + + fn push_arg_values( + &self, + arg: &Arg<'help>, + raw_vals: Vec, + matcher: &mut ArgMatcher, + ) -> ClapResult<()> { + debug!("Parser::push_arg_values: {:?}", raw_vals); + + for raw_val in raw_vals { + // update the current index because each value is a distinct index to clap + self.cur_idx.set(self.cur_idx.get() + 1); + debug!( + "Parser::add_single_val_to_arg: cur_idx:={}", + self.cur_idx.get() + ); + let value_parser = arg.get_value_parser(); + let val = value_parser.parse_ref(self.cmd, Some(arg), &raw_val)?; + + // Increment or create the group "args" + for group in self.cmd.groups_for_arg(&arg.id) { + matcher.add_val_to(&group, val.clone(), raw_val.clone()); + } + + matcher.add_val_to(&arg.id, val, raw_val); + matcher.add_index_to(&arg.id, self.cur_idx.get()); + } + + Ok(()) + } + + fn resolve_pending(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { + let pending = match matcher.take_pending() { + Some(pending) => pending, + None => { + return Ok(()); + } + }; + + debug!("Parser::resolve_pending: id={:?}", pending.id); + let arg = self.cmd.find(&pending.id).expect(INTERNAL_ERROR_MSG); + let _ = self.react( + pending.ident, + ValueSource::CommandLine, + arg, + pending.raw_vals, + matcher, + )?; + + Ok(()) + } + + fn react( + &self, + ident: Option, + source: ValueSource, + arg: &Arg<'help>, + raw_vals: Vec, + matcher: &mut ArgMatcher, + ) -> ClapResult { + self.resolve_pending(matcher)?; + + debug!( + "Parser::react action={:?}, identifier={:?}, source={:?}", + arg.get_action(), + ident, + source + ); + match arg.get_action() { + ArgAction::Set => { + if source == ValueSource::CommandLine + && matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) + { + // Record flag's index + self.cur_idx.set(self.cur_idx.get() + 1); + debug!("Parser::react: cur_idx:={}", self.cur_idx.get()); + } + matcher.remove(&arg.id); + self.start_custom_arg(matcher, arg, source); + self.push_arg_values(arg, raw_vals, matcher)?; + if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { + debug!( + "Parser::react not enough values passed in, leaving it to the validator to complain", + ); + } + Ok(ParseResult::ValuesDone) + } + ArgAction::Append => { + if source == ValueSource::CommandLine + && matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) + { + // Record flag's index + self.cur_idx.set(self.cur_idx.get() + 1); + debug!("Parser::react: cur_idx:={}", self.cur_idx.get()); + } + self.start_custom_arg(matcher, arg, source); + self.push_arg_values(arg, raw_vals, matcher)?; + if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { + debug!( + "Parser::react not enough values passed in, leaving it to the validator to complain", + ); + } + Ok(ParseResult::ValuesDone) + } + #[allow(deprecated)] + ArgAction::StoreValue => { + if ident == Some(Identifier::Index) + && arg.is_multiple_values_set() + && matcher.contains(&arg.id) + { + // HACK: Reuse existing occurrence + } else if source == ValueSource::CommandLine { + if matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) { + // Record flag's index + self.cur_idx.set(self.cur_idx.get() + 1); + debug!("Parser::react: cur_idx:={}", self.cur_idx.get()); + } + self.start_occurrence_of_arg(matcher, arg); + } else { + self.start_custom_arg(matcher, arg, source); + } + self.push_arg_values(arg, raw_vals, matcher)?; + if ident == Some(Identifier::Index) && arg.is_multiple_values_set() { + // HACK: Maintain existing occurrence behavior + let matched = matcher.get_mut(&arg.id).unwrap(); + #[allow(deprecated)] + matched.set_occurrences(matched.num_vals() as u64); + } + if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { + debug!( + "Parser::react not enough values passed in, leaving it to the validator to complain", + ); + } + Ok(ParseResult::ValuesDone) + } + #[allow(deprecated)] + ArgAction::IncOccurrence => { + debug_assert_eq!(raw_vals, Vec::::new()); + if source == ValueSource::CommandLine { + if matches!(ident, Some(Identifier::Short) | Some(Identifier::Long)) { + // Record flag's index + self.cur_idx.set(self.cur_idx.get() + 1); + debug!("Parser::react: cur_idx:={}", self.cur_idx.get()); + } + self.start_occurrence_of_arg(matcher, arg); + } else { + self.start_custom_arg(matcher, arg, source); + } + matcher.add_index_to(&arg.id, self.cur_idx.get()); + Ok(ParseResult::ValuesDone) + } + ArgAction::SetTrue => { + let raw_vals = match raw_vals.len() { + 0 => { + vec![OsString::from("true")] + } + 1 => raw_vals, + _ => { + debug!("Parser::react ignoring trailing values: {:?}", raw_vals); + let mut raw_vals = raw_vals; + raw_vals.resize(1, Default::default()); + raw_vals + } + }; + + matcher.remove(&arg.id); + self.start_custom_arg(matcher, arg, source); + self.push_arg_values(arg, raw_vals, matcher)?; + Ok(ParseResult::ValuesDone) + } + ArgAction::SetFalse => { + let raw_vals = match raw_vals.len() { + 0 => { + vec![OsString::from("false")] + } + 1 => raw_vals, + _ => { + debug!("Parser::react ignoring trailing values: {:?}", raw_vals); + let mut raw_vals = raw_vals; + raw_vals.resize(1, Default::default()); + raw_vals + } + }; + + matcher.remove(&arg.id); + self.start_custom_arg(matcher, arg, source); + self.push_arg_values(arg, raw_vals, matcher)?; + Ok(ParseResult::ValuesDone) + } + ArgAction::Count => { + let raw_vals = match raw_vals.len() { + 0 => { + let existing_value = *matcher + .get_one::(arg.get_id()) + .unwrap_or(&0); + let next_value = existing_value.saturating_add(1); + vec![OsString::from(next_value.to_string())] + } + 1 => raw_vals, + _ => { + debug!("Parser::react ignoring trailing values: {:?}", raw_vals); + let mut raw_vals = raw_vals; + raw_vals.resize(1, Default::default()); + raw_vals + } + }; + + matcher.remove(&arg.id); + self.start_custom_arg(matcher, arg, source); + self.push_arg_values(arg, raw_vals, matcher)?; + Ok(ParseResult::ValuesDone) + } + ArgAction::Help => { + debug_assert_eq!(raw_vals, Vec::::new()); + let use_long = match ident { + Some(Identifier::Long) => true, + Some(Identifier::Short) => false, + Some(Identifier::Index) => true, + None => true, + }; + debug!("Help: use_long={}", use_long); + Err(self.help_err(use_long, Stream::Stdout)) + } + ArgAction::Version => { + debug_assert_eq!(raw_vals, Vec::::new()); + let use_long = match ident { + Some(Identifier::Long) => true, + Some(Identifier::Short) => false, + Some(Identifier::Index) => true, + None => true, + }; + debug!("Version: use_long={}", use_long); + Err(self.version_err(use_long)) + } + } + } + + fn remove_overrides(&self, arg: &Arg<'help>, matcher: &mut ArgMatcher) { + debug!("Parser::remove_overrides: id={:?}", arg.id); + for override_id in &arg.overrides { + debug!("Parser::remove_overrides:iter:{:?}: removing", override_id); + matcher.remove(override_id); + } + + // Override anything that can override us + let mut transitive = Vec::new(); + for arg_id in matcher.arg_ids() { + if let Some(overrider) = self.cmd.find(arg_id) { + if overrider.overrides.contains(&arg.id) { + transitive.push(&overrider.id); + } + } + } + for overrider_id in transitive { + debug!("Parser::remove_overrides:iter:{:?}: removing", overrider_id); + matcher.remove(overrider_id); + } + } + + #[cfg(feature = "env")] + fn add_env(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> { + debug!("Parser::add_env"); + use crate::util::str_to_bool; + + let trailing_values = false; // defaults are independent of the commandline + for arg in self.cmd.get_arguments() { + // Use env only if the arg was absent among command line args, + // early return if this is not the case. + if matcher.contains(&arg.id) { + debug!("Parser::add_env: Skipping existing arg `{}`", arg); + continue; + } + + debug!("Parser::add_env: Checking arg `{}`", arg); + if let Some((_, Some(ref val))) = arg.env { + let val = RawOsStr::new(val); + + if arg.is_takes_value_set() { + debug!( + "Parser::add_env: Found an opt with value={:?}, trailing={:?}", + val, trailing_values + ); + let mut arg_values = Vec::new(); + let _parse_result = + self.split_arg_values(arg, &val, trailing_values, &mut arg_values); + let _ = self.react(None, ValueSource::EnvVariable, arg, arg_values, matcher)?; + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::add_env: Ignoring state {:?}; env variables are outside of the parse loop", _parse_result); + } + } + } else { + match arg.get_action() { + #[allow(deprecated)] + ArgAction::StoreValue => unreachable!("{:?} is not a flag", arg.get_id()), + #[allow(deprecated)] + ArgAction::IncOccurrence => { + debug!("Parser::add_env: Found a flag with value `{:?}`", val); + let predicate = str_to_bool(val.to_str_lossy()); + debug!("Parser::add_env: Found boolean literal `{:?}`", predicate); + if predicate.unwrap_or(true) { + let _ = self.react( + None, + ValueSource::EnvVariable, + arg, + vec![], + matcher, + )?; + } + } + ArgAction::Set + | ArgAction::Append + | ArgAction::SetTrue + | ArgAction::SetFalse + | ArgAction::Count => { + let mut arg_values = Vec::new(); + let _parse_result = + self.split_arg_values(arg, &val, trailing_values, &mut arg_values); + let _ = self.react( + None, + ValueSource::EnvVariable, + arg, + arg_values, + matcher, + )?; + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::add_env: Ignoring state {:?}; env variables are outside of the parse loop", _parse_result); + } + } + } + // Early return on `Help` or `Version`. + ArgAction::Help | ArgAction::Version => { + let _ = + self.react(None, ValueSource::EnvVariable, arg, vec![], matcher)?; + } + } + } + } + } + + Ok(()) + } + + fn add_defaults(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { + debug!("Parser::add_defaults"); + + for arg in self.cmd.get_arguments() { + debug!("Parser::add_defaults:iter:{}:", arg.name); + self.add_default_value(arg, matcher)?; + } + + Ok(()) + } + + fn add_default_value(&self, arg: &Arg<'help>, matcher: &mut ArgMatcher) -> ClapResult<()> { + let trailing_values = false; // defaults are independent of the commandline + + if !arg.default_missing_vals.is_empty() { + debug!( + "Parser::add_default_value:iter:{}: has default missing vals", + arg.name + ); + match matcher.get(&arg.id) { + Some(ma) if ma.all_val_groups_empty() => { + debug!( + "Parser::add_default_value:iter:{}: has no user defined vals", + arg.name + ); + // The flag occurred, we just want to add the val groups + let mut arg_values = Vec::new(); + for v in arg.default_missing_vals.iter() { + let _parse_result = self.split_arg_values( + arg, + &RawOsStr::new(v), + trailing_values, + &mut arg_values, + ); + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::add_default_value: Ignoring state {:?}; defaults are outside of the parse loop", _parse_result); + } + } + } + self.start_custom_arg(matcher, arg, ValueSource::CommandLine); + self.push_arg_values(arg, arg_values, matcher)?; + } + None => { + debug!("Parser::add_default_value:iter:{}: wasn't used", arg.name); + // do nothing + } + _ => { + debug!( + "Parser::add_default_value:iter:{}: has user defined vals", + arg.name + ); + // do nothing + } + } + } else { + debug!( + "Parser::add_default_value:iter:{}: doesn't have default missing vals", + arg.name + ); + // do nothing + } + + if !arg.default_vals_ifs.is_empty() { + debug!("Parser::add_default_value: has conditional defaults"); + if !matcher.contains(&arg.id) { + for (id, val, default) in arg.default_vals_ifs.iter() { + let add = if let Some(a) = matcher.get(id) { + match val { + crate::builder::ArgPredicate::Equals(v) => { + a.raw_vals_flatten().any(|value| v == value) + } + crate::builder::ArgPredicate::IsPresent => true, + } + } else { + false + }; + + if add { + if let Some(default) = default { + let mut arg_values = Vec::new(); + let _parse_result = self.split_arg_values( + arg, + &RawOsStr::new(default), + trailing_values, + &mut arg_values, + ); + let _ = self.react( + None, + ValueSource::DefaultValue, + arg, + arg_values, + matcher, + )?; + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::add_default_value: Ignoring state {:?}; defaults are outside of the parse loop", _parse_result); + } + } + } + return Ok(()); + } + } + } + } else { + debug!("Parser::add_default_value: doesn't have conditional defaults"); + } + + if !arg.default_vals.is_empty() { + debug!( + "Parser::add_default_value:iter:{}: has default vals", + arg.name + ); + if matcher.contains(&arg.id) { + debug!("Parser::add_default_value:iter:{}: was used", arg.name); + // do nothing + } else { + debug!("Parser::add_default_value:iter:{}: wasn't used", arg.name); + let mut arg_values = Vec::new(); + for v in arg.default_vals.iter() { + let _parse_result = self.split_arg_values( + arg, + &RawOsStr::new(v), + trailing_values, + &mut arg_values, + ); + if let Some(_parse_result) = _parse_result { + if _parse_result != ParseResult::ValuesDone { + debug!("Parser::add_default_value: Ignoring state {:?}; defaults are outside of the parse loop", _parse_result); + } + } + } + let _ = self.react(None, ValueSource::DefaultValue, arg, arg_values, matcher)?; + } + } else { + debug!( + "Parser::add_default_value:iter:{}: doesn't have default vals", + arg.name + ); + + // do nothing + } + + Ok(()) + } + + fn start_custom_arg(&self, matcher: &mut ArgMatcher, arg: &Arg<'help>, source: ValueSource) { + if source == ValueSource::CommandLine { + // With each new occurrence, remove overrides from prior occurrences + self.remove_overrides(arg, matcher); + } + matcher.start_custom_arg(arg, source); + for group in self.cmd.groups_for_arg(&arg.id) { + matcher.start_custom_group(&group, source); + } + } + + /// Increase occurrence of specific argument and the grouped arg it's in. + fn start_occurrence_of_arg(&self, matcher: &mut ArgMatcher, arg: &Arg<'help>) { + // With each new occurrence, remove overrides from prior occurrences + self.remove_overrides(arg, matcher); + + matcher.start_occurrence_of_arg(arg); + // Increment or create the group "args" + for group in self.cmd.groups_for_arg(&arg.id) { + matcher.start_occurrence_of_group(&group); + } + } +} + +// Error, Help, and Version Methods +impl<'help, 'cmd> Parser<'help, 'cmd> { + /// Is only used for the long flag(which is the only one needs fuzzy searching) + fn did_you_mean_error( + &mut self, + arg: &str, + matcher: &mut ArgMatcher, + remaining_args: &[&str], + ) -> ClapError { + debug!("Parser::did_you_mean_error: arg={}", arg); + // Didn't match a flag or option + let longs = self + .cmd + .get_keymap() + .keys() + .filter_map(|x| match x { + KeyType::Long(l) => Some(l.to_string_lossy().into_owned()), + _ => None, + }) + .collect::>(); + 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.cmd.get_subcommands_mut(), + ); + + // Add the arg to the matches to build a proper usage string + if let Some((name, _)) = did_you_mean.as_ref() { + if let Some(arg) = self.cmd.get_keymap().get(&name.as_ref()) { + self.start_occurrence_of_arg(matcher, arg); + } + } + + let required = self.cmd.required_graph(); + let used: Vec = matcher + .arg_ids() + .filter(|arg_id| { + matcher.check_explicit(arg_id, crate::builder::ArgPredicate::IsPresent) + }) + .filter(|n| self.cmd.find(n).map_or(true, |a| !a.is_hide_set())) + .cloned() + .collect(); + + ClapError::unknown_argument( + self.cmd, + format!("--{}", arg), + did_you_mean, + Usage::new(self.cmd) + .required(&required) + .create_usage_with_title(&*used), + ) + } + + fn help_err(&self, use_long: bool, stream: Stream) -> ClapError { + match self.cmd.write_help_err(use_long, stream) { + Ok(c) => ClapError::display_help(self.cmd, c), + Err(e) => e, + } + } + + fn version_err(&self, use_long: bool) -> ClapError { + debug!("Parser::version_err"); + + let msg = self.cmd._render_version(use_long); + let mut c = Colorizer::new(Stream::Stdout, self.cmd.color_help()); + c.none(msg); + ClapError::display_version(self.cmd, c) + } +} + +// Query Methods +impl<'help, 'cmd> Parser<'help, 'cmd> { + pub(crate) fn is_set(&self, s: AS) -> bool { + self.cmd.is_set(s) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum ParseState { + ValuesDone, + Opt(Id), + Pos(Id), +} + +/// Recoverable Parsing results. +#[derive(Debug, PartialEq, Clone)] +#[must_use] +enum ParseResult { + FlagSubCommand(String), + Opt(Id), + ValuesDone, + /// Value attached to the short flag is not consumed(e.g. 'u' for `-cu` is + /// not consumed). + AttachedValueNotConsumed, + /// This long flag doesn't need a value but is provided one. + UnneededAttachedValue { + rest: String, + used: Vec, + arg: String, + }, + /// This flag might be an hyphen Value. + MaybeHyphenValue, + /// Equals required but not provided. + EqualsNotProvided { + arg: String, + }, + /// Failed to match a Arg. + NoMatchingArg { + arg: String, + }, + /// No argument found e.g. parser is given `-` when parsing a flag. + NoArg, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) struct PendingArg { + pub(crate) id: Id, + pub(crate) ident: Option, + pub(crate) raw_vals: Vec, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum Identifier { + Short, + Long, + Index, +} -- cgit v1.2.3