diff --git a/src/error/context.rs b/src/error/context.rs index e0055bb2ad0..5cc31097f12 100644 --- a/src/error/context.rs +++ b/src/error/context.rs @@ -30,7 +30,7 @@ pub enum ContextKind { /// Trailing argument TrailingArg, /// Potential fix for the user - SuggestedTrailingArg, + Suggested, /// A usage string Usage, /// An opaque message to the user @@ -53,8 +53,8 @@ impl ContextKind { Self::SuggestedSubcommand => Some("Suggested Subcommand"), Self::SuggestedArg => Some("Suggested Argument"), Self::SuggestedValue => Some("Suggested Value"), - Self::SuggestedTrailingArg => Some("Suggested Trailing Argument"), Self::TrailingArg => Some("Trailing Argument"), + Self::Suggested => Some("Suggested"), Self::Usage => None, Self::Custom => None, } @@ -82,6 +82,8 @@ pub enum ContextValue { Strings(Vec), /// A single value StyledStr(crate::builder::StyledStr), + /// many value + StyledStrs(Vec), /// A single value Number(isize), } @@ -94,6 +96,15 @@ impl std::fmt::Display for ContextValue { Self::String(v) => v.fmt(f), Self::Strings(v) => v.join(", ").fmt(f), Self::StyledStr(v) => v.fmt(f), + Self::StyledStrs(v) => { + for (i, v) in v.iter().enumerate() { + if i != 0 { + ", ".fmt(f)?; + } + v.fmt(f)?; + } + Ok(()) + } Self::Number(v) => v.fmt(f), } } diff --git a/src/error/format.rs b/src/error/format.rs index 4442a8e5386..2e0f8e9f824 100644 --- a/src/error/format.rs +++ b/src/error/format.rs @@ -64,6 +64,27 @@ impl ErrorFormatter for RichFormatter { } } + if let Some(valid) = error.get(ContextKind::SuggestedSubcommand) { + styled.none("\n\n"); + did_you_mean(&mut styled, valid); + } + if let Some(valid) = error.get(ContextKind::SuggestedArg) { + styled.none("\n\n"); + did_you_mean(&mut styled, valid); + } + if let Some(valid) = error.get(ContextKind::SuggestedValue) { + styled.none("\n\n"); + did_you_mean(&mut styled, valid); + } + let suggestions = error.get(ContextKind::Suggested); + if let Some(ContextValue::StyledStrs(suggestions)) = suggestions { + for suggestion in suggestions { + styled.none("\n\n"); + styled.none(TAB); + styled.extend(suggestion.iter()); + } + } + let usage = error.get(ContextKind::Usage); if let Some(ContextValue::StyledStr(usage)) = usage { put_usage(&mut styled, usage.clone()); @@ -170,11 +191,6 @@ fn write_dynamic_context(error: &crate::error::Error, styled: &mut StyledStr) -> styled.none("]"); } } - - if let Some(valid) = error.get(ContextKind::SuggestedValue) { - styled.none("\n\n"); - did_you_mean(styled, valid); - } true } else { false @@ -186,22 +202,6 @@ fn write_dynamic_context(error: &crate::error::Error, styled: &mut StyledStr) -> styled.none("The subcommand '"); styled.warning(invalid_sub); styled.none("' wasn't recognized"); - - if let Some(valid) = error.get(ContextKind::SuggestedSubcommand) { - styled.none("\n\n"); - did_you_mean(styled, valid); - } - - let suggestion = error.get(ContextKind::SuggestedCommand); - if let Some(ContextValue::String(suggestion)) = suggestion { - styled.none("\n\n"); - styled.none(TAB); - styled.none( - "If you believe you received this message in error, try re-running with '", - ); - styled.good(suggestion); - styled.none("'"); - } true } else { false @@ -326,54 +326,6 @@ fn write_dynamic_context(error: &crate::error::Error, styled: &mut StyledStr) -> styled.none("Found argument '"); styled.warning(invalid_arg.to_string()); styled.none("' which wasn't expected, or isn't valid in this context"); - - let valid_sub = error.get(ContextKind::SuggestedSubcommand); - let valid_arg = error.get(ContextKind::SuggestedArg); - match (valid_sub, valid_arg) { - ( - Some(ContextValue::String(valid_sub)), - Some(ContextValue::String(valid_arg)), - ) => { - styled.none("\n\n"); - styled.none(TAB); - styled.none("Did you mean to put '"); - styled.good(valid_arg); - styled.none("' after the subcommand '"); - styled.good(valid_sub); - styled.none("'?"); - } - (None, Some(valid)) => { - styled.none("\n\n"); - did_you_mean(styled, valid); - } - (_, _) => {} - } - - let invalid_arg = error.get(ContextKind::InvalidArg); - if let Some(ContextValue::String(invalid_arg)) = invalid_arg { - let suggested_trailing_arg = error.get(ContextKind::SuggestedTrailingArg); - if suggested_trailing_arg == Some(&ContextValue::Bool(true)) { - styled.none("\n\n"); - styled.none(TAB); - styled.none("If you tried to supply '"); - styled.warning(invalid_arg); - styled.none("' as a value rather than a flag, use '"); - styled.good("-- "); - styled.good(invalid_arg); - styled.none("'"); - } - - let trailing_arg = error.get(ContextKind::TrailingArg); - if trailing_arg == Some(&ContextValue::Bool(true)) { - styled.none("\n\n"); - styled.none(TAB); - styled.none("If you tried to supply '"); - styled.warning(invalid_arg); - styled.none("' as a subcommand, remove the '"); - styled.warning("--"); - styled.none("' before it."); - } - } true } else { false diff --git a/src/error/mod.rs b/src/error/mod.rs index 6d8502f56f6..af65529fce8 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -426,11 +426,18 @@ impl Error { name: String, usage: Option, ) -> Self { - let suggestion = format!("{} -- {}", name, subcmd); let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd); #[cfg(feature = "error-context")] { + let mut styled_suggestion = StyledStr::new(); + styled_suggestion + .none("If you believe you received this message in error, try re-running with '"); + styled_suggestion.good(name); + styled_suggestion.good(" -- "); + styled_suggestion.good(&subcmd); + styled_suggestion.none("'"); + err = err.extend_context_unchecked([ (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)), ( @@ -438,8 +445,8 @@ impl Error { ContextValue::Strings(did_you_mean), ), ( - ContextKind::SuggestedCommand, - ContextValue::String(suggestion), + ContextKind::Suggested, + ContextValue::StyledStrs(vec![styled_suggestion]), ), ]); if let Some(usage) = usage { @@ -645,28 +652,47 @@ impl Error { #[cfg(feature = "error-context")] { + let mut suggestions = vec![]; + if suggested_trailing_arg { + let mut styled_suggestion = StyledStr::new(); + styled_suggestion.none("If you tried to supply '"); + styled_suggestion.warning(&arg); + styled_suggestion.none("' as a value rather than a flag, use '"); + styled_suggestion.good("-- "); + styled_suggestion.good(&arg); + styled_suggestion.none("'"); + suggestions.push(styled_suggestion); + } + err = err .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]); if let Some(usage) = usage { err = err .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage)); } - if let Some((flag, sub)) = did_you_mean { - err = err.insert_context_unchecked( - ContextKind::SuggestedArg, - ContextValue::String(format!("--{}", flag)), - ); - if let Some(sub) = sub { + match did_you_mean { + Some((flag, Some(sub))) => { + let mut styled_suggestion = StyledStr::new(); + styled_suggestion.none("Did you mean to put '"); + styled_suggestion.good("--"); + styled_suggestion.good(flag); + styled_suggestion.none("' after the subcommand '"); + styled_suggestion.good(sub); + styled_suggestion.none("'?"); + suggestions.push(styled_suggestion); + } + Some((flag, None)) => { err = err.insert_context_unchecked( - ContextKind::SuggestedSubcommand, - ContextValue::String(sub), + ContextKind::SuggestedArg, + ContextValue::String(format!("--{}", flag)), ); } + None => {} } - if suggested_trailing_arg { + if !suggestions.is_empty() { err = err.insert_context_unchecked( - ContextKind::SuggestedTrailingArg, - ContextValue::Bool(true), + ContextKind::Suggested, + ContextValue::StyledStrs(suggestions), ); } } @@ -683,9 +709,19 @@ impl Error { #[cfg(feature = "error-context")] { + let mut styled_suggestion = StyledStr::new(); + styled_suggestion.none("If you tried to supply '"); + styled_suggestion.warning(&arg); + styled_suggestion.none("' as a subcommand, remove the '"); + styled_suggestion.warning("--"); + styled_suggestion.none("' before it."); + err = err.extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), - (ContextKind::TrailingArg, ContextValue::Bool(true)), + ( + ContextKind::Suggested, + ContextValue::StyledStrs(vec![styled_suggestion]), + ), ]); if let Some(usage) = usage { err = err