Skip to content

Commit

Permalink
refactor(error): Give caller control over suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
epage committed Aug 16, 2023
1 parent 8413c15 commit 9f65eb0
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 15 deletions.
46 changes: 39 additions & 7 deletions clap_builder/src/builder/value_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::convert::TryInto;
use std::ops::RangeBounds;

use crate::builder::Str;
use crate::builder::StyledStr;
use crate::util::AnyValue;
use crate::util::AnyValueId;

Expand Down Expand Up @@ -2104,7 +2105,7 @@ where
/// Arg::new("current-dir-unknown")
/// .long("cwd")
/// .aliases(["current-dir", "directory", "working-directory", "root"])
/// .value_parser(clap::builder::UnknownArgumentValueParser::suggest("-C"))
/// .value_parser(clap::builder::UnknownArgumentValueParser::suggest_arg("-C"))
/// .hide(true),
/// ]);
///
Expand All @@ -2122,13 +2123,31 @@ where
/// ```
#[derive(Clone, Debug)]
pub struct UnknownArgumentValueParser {
arg: Str,
arg: Option<Str>,
suggestions: Vec<StyledStr>,
}

impl UnknownArgumentValueParser {
/// Suggest an alternative argument
pub fn suggest(arg: impl Into<Str>) -> Self {
Self { arg: arg.into() }
pub fn suggest_arg(arg: impl Into<Str>) -> Self {
Self {
arg: Some(arg.into()),
suggestions: Default::default(),
}
}

/// Provide a general suggestion
pub fn suggest(text: impl Into<StyledStr>) -> Self {
Self {
arg: Default::default(),
suggestions: vec![text.into()],
}
}

/// Extend the suggestions
pub fn and_suggest(mut self, text: impl Into<StyledStr>) -> Self {
self.suggestions.push(text.into());
self
}
}

Expand All @@ -2145,13 +2164,26 @@ impl TypedValueParser for UnknownArgumentValueParser {
Some(arg) => arg.to_string(),
None => "..".to_owned(),
};
Err(crate::Error::unknown_argument(
let err = crate::Error::unknown_argument(
cmd,
arg,
Some((self.arg.as_str().to_owned(), None)),
self.arg.as_ref().map(|s| (s.as_str().to_owned(), None)),
false,
crate::output::Usage::new(cmd).create_usage_with_title(&[]),
))
);
#[cfg(feature = "error-context")]
let err = {
debug_assert_eq!(
err.get(crate::error::ContextKind::Suggested),
None,
"Assuming `Error::unknown_argument` doesn't apply any `Suggested` so we can without caution"
);
err.insert_context_unchecked(
crate::error::ContextKind::Suggested,
crate::error::ContextValue::StyledStrs(self.suggestions.clone()),
)
};
Err(err)
}
}

Expand Down
4 changes: 2 additions & 2 deletions clap_builder/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ impl<F: ErrorFormatter> Error<F> {
let mut styled_suggestion = StyledStr::new();
let _ = write!(
styled_suggestion,
"'{}{sub} --{flag}{}' exists",
"'{}{sub} {flag}{}' exists",
valid.render(),
valid.render_reset()
);
Expand All @@ -727,7 +727,7 @@ impl<F: ErrorFormatter> Error<F> {
Some((flag, None)) => {
err = err.insert_context_unchecked(
ContextKind::SuggestedArg,
ContextValue::String(format!("--{flag}")),
ContextValue::String(flag),
);
}
None => {}
Expand Down
1 change: 1 addition & 0 deletions clap_builder/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1521,6 +1521,7 @@ impl<'cmd> Parser<'cmd> {
self.start_custom_arg(matcher, arg, ValueSource::CommandLine);
}
}
let did_you_mean = did_you_mean.map(|(arg, cmd)| (format!("--{arg}"), cmd));

let required = self.cmd.required_graph();
let used: Vec<Id> = matcher
Expand Down
18 changes: 12 additions & 6 deletions tests/builder/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,10 @@ fn unknown_argument_option() {
Arg::new("current-dir-unknown")
.long("cwd")
.aliases(["current-dir", "directory", "working-directory", "root"])
.value_parser(clap::builder::UnknownArgumentValueParser::suggest("-C"))
.value_parser(
clap::builder::UnknownArgumentValueParser::suggest_arg("-C")
.and_suggest("not much else to say"),
)
.hide(true),
]);
let res = cmd.try_get_matches_from(["test", "--cwd", ".."]);
Expand All @@ -229,7 +232,8 @@ fn unknown_argument_option() {
static MESSAGE: &str = "\
error: unexpected argument '--cwd <current-dir-unknown>' found
tip: a similar argument exists: '---C'
tip: a similar argument exists: '-C'
tip: not much else to say
Usage: test [OPTIONS]
Expand All @@ -247,9 +251,10 @@ fn unknown_argument_flag() {
Arg::new("libtest-ignore")
.long("ignored")
.action(ArgAction::SetTrue)
.value_parser(clap::builder::UnknownArgumentValueParser::suggest(
"-- --ignored",
))
.value_parser(
clap::builder::UnknownArgumentValueParser::suggest_arg("-- --ignored")
.and_suggest("not much else to say"),
)
.hide(true),
]);
let res = cmd.try_get_matches_from(["test", "--ignored"]);
Expand All @@ -259,7 +264,8 @@ fn unknown_argument_flag() {
static MESSAGE: &str = "\
error: unexpected argument '--ignored' found
tip: a similar argument exists: '---- --ignored'
tip: a similar argument exists: '-- --ignored'
tip: not much else to say
Usage: test [OPTIONS]
Expand Down

0 comments on commit 9f65eb0

Please sign in to comment.