mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 14:52:33 +00:00
Merge pull request #4482 from epage/suggest
feat(parser): Show available subcommands when one is missing
This commit is contained in:
commit
8cefdf31cc
5 changed files with 52 additions and 5 deletions
|
@ -9,6 +9,8 @@ pub enum ContextKind {
|
|||
InvalidArg,
|
||||
/// Existing arguments
|
||||
PriorArg,
|
||||
/// Accepted subcommands
|
||||
ValidSubcommand,
|
||||
/// Accepted values
|
||||
ValidValue,
|
||||
/// Rejected values
|
||||
|
@ -44,6 +46,7 @@ impl ContextKind {
|
|||
Self::InvalidSubcommand => Some("Invalid Subcommand"),
|
||||
Self::InvalidArg => Some("Invalid Argument"),
|
||||
Self::PriorArg => Some("Prior Argument"),
|
||||
Self::ValidSubcommand => Some("Value Subcommand"),
|
||||
Self::ValidValue => Some("Value Value"),
|
||||
Self::InvalidValue => Some("Invalid Value"),
|
||||
Self::ActualNumValues => Some("Actual Number of Values"),
|
||||
|
|
|
@ -227,6 +227,24 @@ fn write_dynamic_context(error: &crate::error::Error, styled: &mut StyledStr) ->
|
|||
styled.none("'");
|
||||
styled.warning(invalid_sub);
|
||||
styled.none("' requires a subcommand but one was not provided");
|
||||
|
||||
let possible_values = error.get(ContextKind::ValidSubcommand);
|
||||
if let Some(ContextValue::Strings(possible_values)) = possible_values {
|
||||
if !possible_values.is_empty() {
|
||||
styled.none("\n");
|
||||
styled.none(TAB);
|
||||
styled.none("[subcommands: ");
|
||||
if let Some((last, elements)) = possible_values.split_last() {
|
||||
for v in elements {
|
||||
styled.good(escape(v));
|
||||
styled.none(", ");
|
||||
}
|
||||
styled.good(escape(last));
|
||||
}
|
||||
styled.none("]");
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -504,17 +504,21 @@ impl<F: ErrorFormatter> Error<F> {
|
|||
|
||||
pub(crate) fn missing_subcommand(
|
||||
cmd: &Command,
|
||||
name: String,
|
||||
parent: String,
|
||||
available: Vec<String>,
|
||||
usage: Option<StyledStr>,
|
||||
) -> Self {
|
||||
let mut err = Self::new(ErrorKind::MissingSubcommand).with_cmd(cmd);
|
||||
|
||||
#[cfg(feature = "error-context")]
|
||||
{
|
||||
err = err.extend_context_unchecked([(
|
||||
ContextKind::InvalidSubcommand,
|
||||
ContextValue::String(name),
|
||||
)]);
|
||||
err = err.extend_context_unchecked([
|
||||
(ContextKind::InvalidSubcommand, ContextValue::String(parent)),
|
||||
(
|
||||
ContextKind::ValidSubcommand,
|
||||
ContextValue::Strings(available),
|
||||
),
|
||||
]);
|
||||
if let Some(usage) = usage {
|
||||
err = err
|
||||
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
|
||||
|
|
|
@ -70,6 +70,10 @@ impl<'cmd> Validator<'cmd> {
|
|||
return Err(Error::missing_subcommand(
|
||||
self.cmd,
|
||||
bn.to_string(),
|
||||
self.cmd
|
||||
.all_subcommand_names()
|
||||
.map(|s| s.to_owned())
|
||||
.collect::<Vec<_>>(),
|
||||
Usage::new(self.cmd)
|
||||
.required(&self.required)
|
||||
.create_usage_with_title(&[]),
|
||||
|
|
|
@ -58,6 +58,24 @@ fn sub_command_required() {
|
|||
assert_eq!(err.kind(), ErrorKind::MissingSubcommand);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "error-context")]
|
||||
fn sub_command_required_error() {
|
||||
static ERROR: &str = "\
|
||||
error: 'sc_required' requires a subcommand but one was not provided
|
||||
[subcommands: sub1, help]
|
||||
|
||||
Usage: sc_required <COMMAND>
|
||||
|
||||
For more information try '--help'
|
||||
";
|
||||
|
||||
let cmd = Command::new("sc_required")
|
||||
.subcommand_required(true)
|
||||
.subcommand(Command::new("sub1"));
|
||||
utils::assert_output(cmd, "sc_required", ERROR, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arg_required_else_help() {
|
||||
let result = Command::new("arg_required")
|
||||
|
|
Loading…
Reference in a new issue