Skip to content
Prev Previous commit
Next Next commit
uucore: show suggestion when parse errors occurs because of an ambigu…
…ous value
  • Loading branch information
matrixhead committed Aug 15, 2024
commit 6b1036f70e4da61e5ca91ed8783bf9aacad65ec2
33 changes: 29 additions & 4 deletions src/uucore/src/lib/parser/shortcut_value_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore abcdefgh abef
// spell-checker:ignore abcdefgh abef Strs

//! A parser that accepts shortcuts for values.
//! `ShortcutValueParser` is similar to clap's `PossibleValuesParser`
Expand Down Expand Up @@ -32,6 +32,7 @@ impl ShortcutValueParser {
cmd: &clap::Command,
arg: Option<&clap::Arg>,
value: &str,
possible_values: &[&PossibleValue],
) -> clap::Error {
let mut err = clap::Error::new(ErrorKind::InvalidValue).with_cmd(cmd);

Expand All @@ -52,6 +53,26 @@ impl ShortcutValueParser {
ContextValue::Strings(self.0.iter().map(|x| x.get_name().to_string()).collect()),
);

// if `possible_values` is not empty then that means this error is because of an ambiguous value.
if !possible_values.is_empty() {
let mut formatted_possible_values = String::new();
for (i, s) in possible_values.iter().enumerate() {
formatted_possible_values.push_str(&format!("'{}'", s.get_name()));
if i < possible_values.len() - 2 {
formatted_possible_values.push_str(", ");
} else if i < possible_values.len() - 1 {
formatted_possible_values.push_str(" or ");
}
}
err.insert(
ContextKind::Suggested,
ContextValue::StyledStrs(vec![format!(
"It looks like '{}' could match several values. Did you mean {}?",
value, formatted_possible_values
)
.into()]),
);
}
err
}
}
Expand All @@ -76,13 +97,13 @@ impl TypedValueParser for ShortcutValueParser {
.collect();

match matched_values.len() {
0 => Err(self.generate_clap_error(cmd, arg, value)),
0 => Err(self.generate_clap_error(cmd, arg, value, &[])),
1 => Ok(matched_values[0].get_name().to_string()),
_ => {
if let Some(direct_match) = matched_values.iter().find(|x| x.get_name() == value) {
Ok(direct_match.get_name().to_string())
} else {
Err(self.generate_clap_error(cmd, arg, value))
Err(self.generate_clap_error(cmd, arg, value, &matched_values))
}
}
}
Expand Down Expand Up @@ -143,7 +164,11 @@ mod tests {

for ambiguous_value in ambiguous_values {
let result = parser.parse_ref(&cmd, None, OsStr::new(ambiguous_value));
assert_eq!(ErrorKind::InvalidValue, result.unwrap_err().kind());
assert_eq!(ErrorKind::InvalidValue, result.as_ref().unwrap_err().kind());
assert!(result.unwrap_err().to_string().contains(&format!(
"It looks like '{}' could match several values. Did you mean 'abcd' or 'abef'?",
ambiguous_value
)));
}

let result = parser.parse_ref(&cmd, None, OsStr::new("abc"));
Expand Down