From b88d933d9635c55a62ec24270c31e11efdc35101 Mon Sep 17 00:00:00 2001 From: liudingming Date: Mon, 2 Aug 2021 00:10:56 +0800 Subject: [PATCH] Extract erroring from parsing. Rename needs_valueof to parse_result Use ParseResult more Less predicting, more fallback Remove non-sense ParsingResult::NotFound Merge FlagSubCommand and FlagSubCommandShort Merge NoMatchingLongArg and NoMatchingShortArg Better documentation for pos_counter bumping Denoise of pos_counter Split ParseState from ParseResult Remove ParseResult::Flag small cleanup --- src/parse/errors.rs | 9 +- src/parse/mod.rs | 2 +- src/parse/parser.rs | 493 +++++++++++++++++++++++++---------------- src/parse/validator.rs | 8 +- 4 files changed, 309 insertions(+), 203 deletions(-) diff --git a/src/parse/errors.rs b/src/parse/errors.rs index da657a8a..ccdb9fd0 100644 --- a/src/parse/errors.rs +++ b/src/parse/errors.rs @@ -564,9 +564,8 @@ impl Error { } } - pub(crate) fn no_equals(arg: &Arg, usage: String, color: ColorChoice) -> Self { + pub(crate) fn no_equals(arg: String, usage: String, color: ColorChoice) -> Self { let mut c = Colorizer::new(true, color); - let arg = arg.to_string(); start_error(&mut c, "Equal sign is needed when assigning values to '"); c.warning(&arg); @@ -798,7 +797,7 @@ impl Error { pub(crate) fn too_many_values( val: String, - arg: &Arg, + arg: String, usage: String, color: ColorChoice, ) -> Self { @@ -807,7 +806,7 @@ impl Error { start_error(&mut c, "The value '"); c.warning(val.clone()); c.none("' was provided to '"); - c.warning(arg.to_string()); + c.warning(&arg); c.none("' but it wasn't expecting any more values"); put_usage(&mut c, usage); try_help(&mut c); @@ -815,7 +814,7 @@ impl Error { Error { message: c, kind: ErrorKind::TooManyValues, - info: vec![arg.to_string(), val], + info: vec![arg, val], source: None, } } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 05344938..f2f0d1fd 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -9,7 +9,7 @@ mod validator; pub(crate) use self::{ arg_matcher::ArgMatcher, matches::{MatchedArg, SubCommand, ValueType}, - parser::{Input, ParseResult, Parser}, + parser::{Input, ParseState, Parser}, validator::Validator, }; diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 5684427d..fe4224af 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -20,17 +20,43 @@ use crate::{ INTERNAL_ERROR_MSG, INVALID_UTF8, }; -#[derive(Debug, PartialEq, Clone)] -pub(crate) enum ParseResult { - Flag, - FlagSubCommand(String), - FlagSubCommandShort(String), +pub(crate) enum ParseState { + ValuesDone, Opt(Id), Pos(Id), - MaybeHyphenValue, - NotFound, - AttachedValueNotConsumed, +} + +/// Recoverable Parsing results. +#[derive(Debug, PartialEq, Clone)] +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, + /// This is a Help flag. + HelpFlag, + /// This is a version flag. + VersionFlag, } #[derive(Debug)] @@ -319,7 +345,7 @@ impl<'help, 'app> Parser<'help, 'app> { let mut subcmd_name: Option = None; let mut keep_state = false; - let mut needs_val_of = ParseResult::NotFound; + let mut parse_state = ParseState::ValuesDone; let mut pos_counter = 1; // Count of positional args let positional_count = self.app.args.keys().filter(|x| x.is_position()).count(); @@ -351,10 +377,9 @@ impl<'help, 'app> Parser<'help, 'app> { // Has the user already passed '--'? Meaning only positional args follow 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 { + if self.is_set(AS::SubcommandPrecedenceOverArg) + || !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); debug!("Parser::get_matches_with: sc={:?}", sc_name); @@ -367,92 +392,161 @@ impl<'help, 'app> Parser<'help, 'app> { } } - // Is this a new argument, or a value for previous option? - 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; - } - _ => (), + if arg_os.starts_with("--") { + let parse_result = self.parse_long_arg(matcher, &arg_os, &parse_state); + debug!( + "Parser::get_matches_with: After parse_long_arg {:?}", + parse_result + ); + match parse_result { + ParseResult::NoArg => { + debug!("Parser::get_matches_with: setting TrailingVals=true"); + self.app.set(AS::TrailingValues); + continue; } - } else if arg_os.starts_with("-") - && arg_os.len() != 1 - && !(self.is_set(AS::AllowNegativeNumbers) - && arg_os.to_string_lossy().parse::().is_ok()) - && !(self.is_set(AS::AllowLeadingHyphen) - && arg_os - .trim_start_matches(b'-') - .to_string_lossy() - .chars() - .any(|c| !self.app.contains_short(c))) - { - // Arg looks like a short flag, and not a possible number - - // Try to parse short args like normal, if AllowLeadingHyphen or - // AllowNegativeNumbers is set, parse_short_arg will *not* throw - // an error, and instead return Ok(None) - needs_val_of = self.parse_short_arg(matcher, &arg_os)?; - // If it's None, we then check if one of those two AppSettings was set - 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) => { - // 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| { - it.cursor -= 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.to_owned()); - break; - } - _ => (), + 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 } => { + return Err(ClapError::no_equals( + arg, + Usage::new(self).create_usage_with_title(&[]), + self.app.color(), + )); + } + ParseResult::NoMatchingArg { arg } => { + let remaining_args: Vec<_> = remaining_args + .iter() + .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 } => { + return Err(ClapError::too_many_values( + rest, + arg, + Usage::new(self).create_usage_no_title(&used), + self.app.color(), + )) + } + ParseResult::HelpFlag => { + return Err(self.help_err(true)); + } + ParseResult::VersionFlag => { + return Err(self.version_err(true)); + } + ParseResult::MaybeHyphenValue => { + // Maybe a hyphen value, do nothing. + } + ParseResult::AttachedValueNotConsumed => { + unreachable!() } } - } else if let ParseResult::Opt(id) = needs_val_of { - // Check to see if parsing a value from a previous arg + } else if arg_os.starts_with("-") { + // Arg looks like a short flag, and not a possible number + + // Try to parse short args like normal, if AllowLeadingHyphen or + // AllowNegativeNumbers is set, parse_short_arg will *not* throw + // an error, and instead return Ok(None) + let parse_result = self.parse_short_arg(matcher, &arg_os, &parse_state); + // 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| { + it.cursor -= 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 } => { + return Err(ClapError::no_equals( + arg, + Usage::new(self).create_usage_with_title(&[]), + self.app.color(), + )) + } + ParseResult::NoMatchingArg { arg } => { + return Err(ClapError::unknown_argument( + arg, + None, + Usage::new(self).create_usage_with_title(&[]), + self.app.color(), + )); + } + ParseResult::HelpFlag => { + return Err(self.help_err(false)); + } + ParseResult::VersionFlag => { + return Err(self.version_err(false)); + } + 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 - needs_val_of = self.add_val_to_arg( - &self.app[&id], + let parse_result = self.add_val_to_arg( + &self.app[id], arg_os, matcher, ValueType::CommandLine, true, ); + parse_state = match parse_result { + ParseResult::Opt(id) => ParseState::Opt(id), + ParseResult::ValuesDone => ParseState::ValuesDone, + _ => unreachable!(), + }; // get the next value from the iterator continue; } @@ -487,26 +581,27 @@ impl<'help, 'app> Parser<'help, 'app> { ); 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 + let skip_current = if let Some(n) = remaining_args.get(0) { + if let Some(p) = self .app .get_positionals() .find(|p| p.index == Some(pos_counter)) { - ParseResult::Pos(p.id.clone()) + // 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. + let n = ArgStr::new(n); + self.is_new_arg(&n, p) || self.possible_subcommand(&n).is_some() } else { - ParseResult::ValuesDone - }; - - // If the arg doesn't looks like a new_arg and it's not a - // subcommand, don't bump the position counter. - let n = ArgStr::new(n); - self.is_new_arg(&n, &needs_val_of) || self.possible_subcommand(&n).is_some() + true + } } else { true }; - if bump { + if skip_current { debug!("Parser::get_matches_with: Bumping the positional counter..."); pos_counter + 1 } else { @@ -552,6 +647,8 @@ impl<'help, 'app> Parser<'help, 'app> { // Only increment the positional counter if it doesn't allow multiples if !p.settings.is_set(ArgSettings::MultipleValues) { pos_counter += 1; + } else { + parse_state = ParseState::Pos(p.id.clone()); } self.app.settings.set(AS::ValidArgFound); } else if self.is_set(AS::AllowExternalSubcommands) { @@ -595,7 +692,7 @@ impl<'help, 'app> Parser<'help, 'app> { self.remove_overrides(matcher); - return Validator::new(self).validate(needs_val_of, subcmd_name.is_some(), matcher); + return Validator::new(self).validate(parse_state, subcmd_name.is_some(), matcher); } else { // Start error processing return Err(self.match_arg_error(&arg_os)); @@ -630,7 +727,7 @@ impl<'help, 'app> Parser<'help, 'app> { self.remove_overrides(matcher); - Validator::new(self).validate(needs_val_of, subcmd_name.is_some(), matcher) + Validator::new(self).validate(parse_state, subcmd_name.is_some(), matcher) } fn match_arg_error(&self, arg_os: &ArgStr) -> ClapError { @@ -830,34 +927,32 @@ impl<'help, 'app> Parser<'help, 'app> { 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); + fn is_new_arg(&self, next: &ArgStr, current_positional: &Arg) -> bool { + debug!( + "Parser::is_new_arg: {:?}:{:?}", + next, current_positional.name + ); - 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::().is_ok()) - } - ParseResult::ValuesDone => return true, - _ => false, - }; - - debug!("Parser::is_new_arg: want_value={:?}", want_value); - - // Is this a new argument, or values from a previous option? - if want_value { + if self.is_set(AS::AllowLeadingHyphen) + || self.app[¤t_positional.id].is_set(ArgSettings::AllowHyphenValues) + || (self.is_set(AS::AllowNegativeNumbers) + && next.to_string_lossy().parse::().is_ok()) + { + // If allow hyphen, this isn't a new arg. + debug!("Parser::is_new_arg: Allow hyphen"); false - } else if arg_os.starts_with("--") { + } else if next.starts_with("--") { + // If this is a long flag, this is a new arg. debug!("Parser::is_new_arg: -- found"); true - } else if arg_os.starts_with("-") { + } else if next.starts_with("-") { debug!("Parser::is_new_arg: - found"); - // a singe '-' by itself is a value and typically means "stdin" on unix systems - arg_os.len() != 1 + // 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. + next.len() != 1 } else { debug!("Parser::is_new_arg: value"); + // Nothing special, this is a value. false } } @@ -956,7 +1051,7 @@ impl<'help, 'app> Parser<'help, 'app> { // Retrieves the names of all args the user has supplied thus far, except required ones // because those will be listed in self.required - fn check_for_help_and_version_str(&self, arg: &ArgStr) -> ClapResult<()> { + fn check_for_help_and_version_str(&self, arg: &ArgStr) -> Option { debug!("Parser::check_for_help_and_version_str"); debug!( "Parser::check_for_help_and_version_str: Checking if --{:?} is help or version...", @@ -967,7 +1062,7 @@ impl<'help, 'app> Parser<'help, 'app> { if let Some(h) = help.long { if arg == h && !self.is_set(AS::NoAutoHelp) { debug!("Help"); - return Err(self.help_err(true)); + return Some(ParseResult::HelpFlag); } } } @@ -976,16 +1071,16 @@ impl<'help, 'app> Parser<'help, 'app> { if let Some(v) = version.long { if arg == v && !self.is_set(AS::NoAutoVersion) { debug!("Version"); - return Err(self.version_err(true)); + return Some(ParseResult::VersionFlag); } } } debug!("Neither"); - Ok(()) + None } - fn check_for_help_and_version_char(&self, arg: char) -> ClapResult<()> { + fn check_for_help_and_version_char(&self, arg: char) -> Option { debug!("Parser::check_for_help_and_version_char"); debug!( "Parser::check_for_help_and_version_char: Checking if -{} is help or version...", @@ -996,7 +1091,7 @@ impl<'help, 'app> Parser<'help, 'app> { if let Some(h) = help.short { if arg == h && !self.is_set(AS::NoAutoHelp) { debug!("Help"); - return Err(self.help_err(false)); + return Some(ParseResult::HelpFlag); } } } @@ -1005,13 +1100,13 @@ impl<'help, 'app> Parser<'help, 'app> { if let Some(v) = version.short { if arg == v && !self.is_set(AS::NoAutoVersion) { debug!("Version"); - return Err(self.version_err(false)); + return Some(ParseResult::VersionFlag); } } } debug!("Neither"); - Ok(()) + None } fn use_long_help(&self) -> bool { @@ -1037,17 +1132,26 @@ impl<'help, 'app> Parser<'help, 'app> { &mut self, matcher: &mut ArgMatcher, full_arg: &ArgStr, - remaining_args: &[OsString], - ) -> ClapResult { + parse_state: &ParseState, + ) -> ParseResult { // maybe here lifetime should be 'a debug!("Parser::parse_long_arg"); + if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) if + self.app[opt].is_set(ArgSettings::AllowHyphenValues)) + { + return ParseResult::MaybeHyphenValue; + } + // Update the current index 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'-'); + if long_arg.is_empty() { + return ParseResult::NoArg; + } let (arg, val) = if full_arg.contains_byte(b'=') { let (p0, p1) = long_arg.split_at_byte(b'='); debug!("Yes '{:?}'", p1); @@ -1084,10 +1188,8 @@ impl<'help, 'app> Parser<'help, 'app> { "Parser::parse_long_arg: Found an opt with value '{:?}'", &val ); - return self.parse_opt(&val, opt, matcher); - } - debug!("Parser::parse_long_arg: Found a flag"); - if let Some(rest) = val { + self.parse_opt(&val, opt, matcher) + } else if let Some(rest) = val { debug!( "Parser::parse_long_arg: Got invalid literal `{:?}`", rest.to_os_string() @@ -1102,33 +1204,25 @@ impl<'help, 'app> Parser<'help, 'app> { .cloned() .collect(); - return Err(ClapError::too_many_values( - rest.to_string_lossy().into(), - opt, - Usage::new(self).create_usage_no_title(&used), - self.app.color(), - )); + ParseResult::UnneededAttachedValue { + rest: rest.to_string_lossy().to_string(), + used, + arg: opt.to_string(), + } + } else if let Some(parse_result) = self.check_for_help_and_version_str(&arg) { + parse_result + } else { + debug!("Parser::parse_long_arg: Presence validated"); + self.parse_flag(opt, matcher) } - self.check_for_help_and_version_str(&arg)?; - debug!("Parser::parse_long_arg: Presence validated"); - 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 let Some(sc_name) = self.possible_long_flag_subcommand(&arg) { + ParseResult::FlagSubCommand(sc_name.to_string()) } else if self.is_set(AS::AllowLeadingHyphen) { - Ok(ParseResult::MaybeHyphenValue) + 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, - )) + ParseResult::NoMatchingArg { + arg: arg.to_string_lossy().to_string(), + } } } @@ -1136,12 +1230,22 @@ impl<'help, 'app> Parser<'help, 'app> { &mut self, matcher: &mut ArgMatcher, full_arg: &ArgStr, - ) -> ClapResult { + parse_state: &ParseState, + ) -> 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(); - let mut ret = ParseResult::NotFound; + if (self.is_set(AS::AllowNegativeNumbers) && arg.parse::().is_ok()) + || (self.is_set(AS::AllowLeadingHyphen) + && arg.chars().any(|c| !self.app.contains_short(c))) + || matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) + if self.app[opt].is_set(ArgSettings::AllowHyphenValues)) + { + return ParseResult::MaybeHyphenValue; + } + + let mut ret = ParseResult::NoArg; let skip = self.flag_subcmd_skip; self.flag_subcmd_skip = 0; @@ -1168,7 +1272,9 @@ impl<'help, 'app> Parser<'help, 'app> { 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)?; + if let Some(parse_result) = self.check_for_help_and_version_char(c) { + return parse_result; + } ret = self.parse_flag(opt, matcher); continue; } @@ -1200,11 +1306,13 @@ impl<'help, 'app> Parser<'help, 'app> { // // e.g. `-xvf`, when RequireEquals && x.min_vals == 0, we don't // consume the `vf`, even if it's provided as value. - match self.parse_opt(&val, opt, matcher)? { + match self.parse_opt(&val, opt, matcher) { ParseResult::AttachedValueNotConsumed => continue, - x => return Ok(x), + x => return x, } - } else if let Some(sc_name) = self.app.find_short_subcmd(c) { + } + + return 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 = { @@ -1221,19 +1329,14 @@ impl<'help, 'app> Parser<'help, 'app> { if done_short_args { self.flag_subcmd_at = None; } - return Ok(ParseResult::FlagSubCommandShort(name)); + ParseResult::FlagSubCommand(name) } else { - let arg = format!("-{}", c); - - return Err(ClapError::unknown_argument( - arg, - None, - Usage::new(self).create_usage_with_title(&[]), - self.app.color(), - )); - } + ParseResult::NoMatchingArg { + arg: format!("-{}", c), + } + }; } - Ok(ret) + ret } fn parse_opt( @@ -1241,7 +1344,7 @@ impl<'help, 'app> Parser<'help, 'app> { attached_value: &Option, opt: &Arg<'help>, matcher: &mut ArgMatcher, - ) -> ClapResult { + ) -> ParseResult { debug!( "Parser::parse_opt; opt={}, val={:?}", opt.name, attached_value @@ -1268,21 +1371,17 @@ impl<'help, 'app> Parser<'help, 'app> { }; self.inc_occurrence_of_arg(matcher, opt); if attached_value.is_some() { - return Ok(ParseResult::AttachedValueNotConsumed); + ParseResult::AttachedValueNotConsumed } else { - return Ok(ParseResult::ValuesDone); + ParseResult::ValuesDone } } else { debug!("Requires equals but not provided. Error."); - return Err(ClapError::no_equals( - opt, - Usage::new(self).create_usage_with_title(&[]), - self.app.color(), - )); + ParseResult::EqualsNotProvided { + arg: opt.to_string(), + } } - } - - if let Some(fv) = attached_value { + } else if let Some(fv) = attached_value { let v = fv.trim_start_n_matches(1, b'='); debug!("Found - {:?}, len: {}", v, v.len()); debug!( @@ -1292,7 +1391,7 @@ impl<'help, 'app> Parser<'help, 'app> { ); self.add_val_to_arg(opt, v, matcher, ValueType::CommandLine, false); self.inc_occurrence_of_arg(matcher, opt); - Ok(ParseResult::ValuesDone) + ParseResult::ValuesDone } else { debug!("Parser::parse_opt: More arg vals required..."); self.inc_occurrence_of_arg(matcher, opt); @@ -1300,7 +1399,7 @@ impl<'help, 'app> Parser<'help, 'app> { for group in self.app.groups_for_arg(&opt.id) { matcher.new_val_group(&group); } - Ok(ParseResult::Opt(opt.id.clone())) + ParseResult::Opt(opt.id.clone()) } } @@ -1422,7 +1521,7 @@ impl<'help, 'app> Parser<'help, 'app> { matcher.add_index_to(&flag.id, self.cur_idx.get(), ValueType::CommandLine); self.inc_occurrence_of_arg(matcher, flag); - ParseResult::Flag + ParseResult::ValuesDone } fn remove_overrides(&mut self, matcher: &mut ArgMatcher) { @@ -1606,7 +1705,15 @@ impl<'help, 'app> Parser<'help, 'app> { 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)?; + match self.check_for_help_and_version_str(&val) { + Some(ParseResult::HelpFlag) => { + return Err(self.help_err(true)); + } + Some(ParseResult::VersionFlag) => { + return Err(self.version_err(true)); + } + _ => (), + } matcher.add_index_to(&a.id, self.cur_idx.get(), ValueType::EnvVariable); } } diff --git a/src/parse/validator.rs b/src/parse/validator.rs index 09b91094..9bb30678 100644 --- a/src/parse/validator.rs +++ b/src/parse/validator.rs @@ -4,7 +4,7 @@ use crate::{ output::Usage, parse::{ errors::{Error, ErrorKind, Result as ClapResult}, - ArgMatcher, MatchedArg, ParseResult, Parser, ValueType, + ArgMatcher, MatchedArg, ParseState, Parser, ValueType, }, util::{ChildGraph, Id}, INTERNAL_ERROR_MSG, INVALID_UTF8, @@ -25,7 +25,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { pub(crate) fn validate( &mut self, - needs_val_of: ParseResult, + parse_state: ParseState, is_subcmd: bool, matcher: &mut ArgMatcher, ) -> ClapResult<()> { @@ -33,7 +33,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { let mut reqs_validated = false; self.p.add_env(matcher)?; self.p.add_defaults(matcher); - if let ParseResult::Opt(a) = needs_val_of { + if let ParseState::Opt(a) = parse_state { debug!("Validator::validate: needs_val_of={:?}", a); self.validate_required(matcher)?; @@ -506,7 +506,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { .to_str() .expect(INVALID_UTF8) .to_string(), - a, + a.to_string(), Usage::new(self.p).create_usage_with_title(&[]), self.p.app.color(), ));