//! Error reporting #![allow(deprecated)] // Std use std::{ borrow::Cow, convert::From, error, fmt::{self, Debug, Display, Formatter}, io::{self, BufRead}, result::Result as StdResult, }; // Internal use crate::output::fmt::Colorizer; use crate::output::fmt::Stream; use crate::parser::features::suggestions; use crate::util::{color::ColorChoice, safe_exit, SUCCESS_CODE, USAGE_CODE}; use crate::AppSettings; use crate::Command; mod context; mod kind; pub use context::ContextKind; pub use context::ContextValue; pub use kind::ErrorKind; /// Short hand for [`Result`] type /// /// [`Result`]: std::result::Result pub type Result = StdResult; /// Command Line Argument Parser Error /// /// See [`Command::error`] to create an error. /// /// [`Command::error`]: crate::Command::error #[derive(Debug)] pub struct Error { inner: Box, /// Deprecated, replaced with [`Error::kind()`] #[cfg_attr( feature = "deprecated", deprecated(since = "3.1.0", note = "Replaced with `Error::kind()`") )] pub kind: ErrorKind, /// Deprecated, replaced with [`Error::context()`] #[cfg_attr( feature = "deprecated", deprecated(since = "3.1.0", note = "Replaced with `Error::context()`") )] pub info: Vec, } #[derive(Debug)] struct ErrorInner { kind: ErrorKind, context: Vec<(ContextKind, ContextValue)>, message: Option, source: Option>, help_flag: Option<&'static str>, color_when: ColorChoice, wait_on_exit: bool, backtrace: Option, } impl Error { /// Create an unformatted error /// /// This is for you need to pass the error up to /// a place that has access to the `Command` at which point you can call [`Error::format`]. /// /// Prefer [`Command::error`] for generating errors. /// /// [`Command::error`]: crate::Command::error pub fn raw(kind: ErrorKind, message: impl std::fmt::Display) -> Self { Self::new(kind).set_message(message.to_string()) } /// Format the existing message with the Command's context #[must_use] pub fn format(mut self, cmd: &mut Command) -> Self { cmd._build_self(); let usage = cmd.render_usage(); if let Some(message) = self.inner.message.as_mut() { message.format(cmd, usage); } self.with_cmd(cmd) } /// Type of error for programmatic processing pub fn kind(&self) -> ErrorKind { self.inner.kind } /// Additional information to further qualify the error pub fn context(&self) -> impl Iterator { self.inner.context.iter().map(|(k, v)| (*k, v)) } /// Should the message be written to `stdout` or not? #[inline] pub fn use_stderr(&self) -> bool { self.stream() == Stream::Stderr } pub(crate) fn stream(&self) -> Stream { match self.kind() { ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => Stream::Stdout, _ => Stream::Stderr, } } /// Prints the error and exits. /// /// Depending on the error kind, this either prints to `stderr` and exits with a status of `2` /// or prints to `stdout` and exits with a status of `0`. pub fn exit(&self) -> ! { if self.use_stderr() { // Swallow broken pipe errors let _ = self.print(); if self.inner.wait_on_exit { wlnerr!("\nPress [ENTER] / [RETURN] to continue..."); let mut s = String::new(); let i = io::stdin(); i.lock().read_line(&mut s).unwrap(); } safe_exit(USAGE_CODE); } // Swallow broken pipe errors let _ = self.print(); safe_exit(SUCCESS_CODE) } /// Prints formatted and colored error to `stdout` or `stderr` according to its error kind /// /// # Example /// ```no_run /// use clap::Command; /// /// match Command::new("Command").try_get_matches() { /// Ok(matches) => { /// // do_something /// }, /// Err(err) => { /// err.print().expect("Error writing Error"); /// // do_something /// }, /// }; /// ``` pub fn print(&self) -> io::Result<()> { self.formatted().print() } /// Deprecated, replaced with [`Command::error`] /// /// [`Command::error`]: crate::Command::error #[cfg_attr( feature = "deprecated", deprecated(since = "3.0.0", note = "Replaced with `Command::error`") )] #[doc(hidden)] pub fn with_description(description: String, kind: ErrorKind) -> Self { Error::raw(kind, description) } fn new(kind: ErrorKind) -> Self { Self { inner: Box::new(ErrorInner { kind, context: Vec::new(), message: None, source: None, help_flag: None, color_when: ColorChoice::Never, wait_on_exit: false, backtrace: Backtrace::new(), }), kind, info: vec![], } } #[inline(never)] fn for_app(kind: ErrorKind, cmd: &Command, colorizer: Colorizer, info: Vec) -> Self { Self::new(kind) .set_message(colorizer) .with_cmd(cmd) .set_info(info) } pub(crate) fn with_cmd(self, cmd: &Command) -> Self { self.set_wait_on_exit(cmd.is_set(AppSettings::WaitOnError)) .set_color(cmd.get_color()) .set_help_flag(get_help_flag(cmd)) } pub(crate) fn set_message(mut self, message: impl Into) -> Self { self.inner.message = Some(message.into()); self } pub(crate) fn set_info(mut self, info: Vec) -> Self { self.info = info; self } pub(crate) fn set_source(mut self, source: Box) -> Self { self.inner.source = Some(source); self } pub(crate) fn set_color(mut self, color_when: ColorChoice) -> Self { self.inner.color_when = color_when; self } pub(crate) fn set_help_flag(mut self, help_flag: Option<&'static str>) -> Self { self.inner.help_flag = help_flag; self } pub(crate) fn set_wait_on_exit(mut self, yes: bool) -> Self { self.inner.wait_on_exit = yes; self } /// Does not verify if `ContextKind` is already present #[inline(never)] pub(crate) fn insert_context_unchecked( mut self, kind: ContextKind, value: ContextValue, ) -> Self { self.inner.context.push((kind, value)); self } /// Does not verify if `ContextKind` is already present #[inline(never)] pub(crate) fn extend_context_unchecked( mut self, context: [(ContextKind, ContextValue); N], ) -> Self { self.inner.context.extend(context); self } #[inline(never)] fn get_context(&self, kind: ContextKind) -> Option<&ContextValue> { self.inner .context .iter() .find_map(|(k, v)| (*k == kind).then(|| v)) } pub(crate) fn display_help(cmd: &Command, colorizer: Colorizer) -> Self { Self::for_app(ErrorKind::DisplayHelp, cmd, colorizer, vec![]) } pub(crate) fn display_help_error(cmd: &Command, colorizer: Colorizer) -> Self { Self::for_app( ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand, cmd, colorizer, vec![], ) } pub(crate) fn display_version(cmd: &Command, colorizer: Colorizer) -> Self { Self::for_app(ErrorKind::DisplayVersion, cmd, colorizer, vec![]) } pub(crate) fn argument_conflict( cmd: &Command, arg: String, mut others: Vec, usage: String, ) -> Self { let info = others.clone(); let others = match others.len() { 0 => ContextValue::None, 1 => ContextValue::String(others.pop().unwrap()), _ => ContextValue::Strings(others), }; Self::new(ErrorKind::ArgumentConflict) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), (ContextKind::PriorArg, others), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn empty_value(cmd: &Command, good_vals: &[&str], arg: String) -> Self { let info = vec![arg.clone()]; let mut err = Self::new(ErrorKind::EmptyValue) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]); if !good_vals.is_empty() { err = err.insert_context_unchecked( ContextKind::ValidValue, ContextValue::Strings(good_vals.iter().map(|s| (*s).to_owned()).collect()), ); } err } pub(crate) fn no_equals(cmd: &Command, arg: String, usage: String) -> Self { let info = vec![arg.clone()]; Self::new(ErrorKind::NoEquals) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn invalid_value( cmd: &Command, bad_val: String, good_vals: &[&str], arg: String, ) -> Self { let mut info = vec![arg.clone(), bad_val.clone()]; info.extend(good_vals.iter().map(|s| (*s).to_owned())); let suggestion = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop(); let mut err = Self::new(ErrorKind::InvalidValue) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), (ContextKind::InvalidValue, ContextValue::String(bad_val)), ( ContextKind::ValidValue, ContextValue::Strings(good_vals.iter().map(|s| (*s).to_owned()).collect()), ), ]); if let Some(suggestion) = suggestion { err = err.insert_context_unchecked( ContextKind::SuggestedValue, ContextValue::String(suggestion), ); } err } pub(crate) fn invalid_subcommand( cmd: &Command, subcmd: String, did_you_mean: String, name: String, usage: String, ) -> Self { let info = vec![subcmd.clone()]; let suggestion = format!("{} -- {}", name, subcmd); Self::new(ErrorKind::InvalidSubcommand) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)), ( ContextKind::SuggestedSubcommand, ContextValue::String(did_you_mean), ), ( ContextKind::SuggestedCommand, ContextValue::String(suggestion), ), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn unrecognized_subcommand(cmd: &Command, subcmd: String, usage: String) -> Self { let info = vec![subcmd.clone()]; Self::new(ErrorKind::UnrecognizedSubcommand) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn missing_required_argument( cmd: &Command, required: Vec, usage: String, ) -> Self { let info = required.clone(); Self::new(ErrorKind::MissingRequiredArgument) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::Strings(required)), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn missing_subcommand(cmd: &Command, name: String, usage: String) -> Self { let info = vec![]; Self::new(ErrorKind::MissingSubcommand) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidSubcommand, ContextValue::String(name)), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn invalid_utf8(cmd: &Command, usage: String) -> Self { let info = vec![]; Self::new(ErrorKind::InvalidUtf8) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([(ContextKind::Usage, ContextValue::String(usage))]) } pub(crate) fn too_many_occurrences( cmd: &Command, arg: String, max_occurs: usize, curr_occurs: usize, usage: String, ) -> Self { let info = vec![arg.clone(), curr_occurs.to_string(), max_occurs.to_string()]; Self::new(ErrorKind::TooManyOccurrences) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), ( ContextKind::MaxOccurrences, ContextValue::Number(max_occurs as isize), ), ( ContextKind::ActualNumValues, ContextValue::Number(curr_occurs as isize), ), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn too_many_values(cmd: &Command, val: String, arg: String, usage: String) -> Self { let info = vec![arg.clone(), val.clone()]; Self::new(ErrorKind::TooManyValues) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), (ContextKind::InvalidValue, ContextValue::String(val)), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn too_few_values( cmd: &Command, arg: String, min_vals: usize, curr_vals: usize, usage: String, ) -> Self { let info = vec![arg.clone(), curr_vals.to_string(), min_vals.to_string()]; Self::new(ErrorKind::TooFewValues) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), ( ContextKind::MinValues, ContextValue::Number(min_vals as isize), ), ( ContextKind::ActualNumValues, ContextValue::Number(curr_vals as isize), ), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn value_validation( arg: String, val: String, err: Box, ) -> Self { let info = vec![arg.clone(), val.to_string(), err.to_string()]; Self::new(ErrorKind::ValueValidation) .set_info(info) .set_source(err) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), (ContextKind::InvalidValue, ContextValue::String(val)), ]) } pub(crate) fn wrong_number_of_values( cmd: &Command, arg: String, num_vals: usize, curr_vals: usize, usage: String, ) -> Self { let info = vec![arg.clone(), curr_vals.to_string(), num_vals.to_string()]; Self::new(ErrorKind::WrongNumberOfValues) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), ( ContextKind::ExpectedNumValues, ContextValue::Number(num_vals as isize), ), ( ContextKind::ActualNumValues, ContextValue::Number(curr_vals as isize), ), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn unexpected_multiple_usage(cmd: &Command, arg: String, usage: String) -> Self { let info = vec![arg.clone()]; Self::new(ErrorKind::UnexpectedMultipleUsage) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn unknown_argument( cmd: &Command, arg: String, did_you_mean: Option<(String, Option)>, usage: String, ) -> Self { let info = vec![arg.clone()]; let mut err = Self::new(ErrorKind::UnknownArgument) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), (ContextKind::Usage, ContextValue::String(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 { err = err.insert_context_unchecked( ContextKind::SuggestedSubcommand, ContextValue::String(sub), ); } } err } pub(crate) fn unnecessary_double_dash(cmd: &Command, arg: String, usage: String) -> Self { let info = vec![arg.clone()]; Self::new(ErrorKind::UnknownArgument) .with_cmd(cmd) .set_info(info) .extend_context_unchecked([ (ContextKind::InvalidArg, ContextValue::String(arg)), (ContextKind::TrailingArg, ContextValue::Bool(true)), (ContextKind::Usage, ContextValue::String(usage)), ]) } pub(crate) fn argument_not_found_auto(arg: String) -> Self { let info = vec![arg.clone()]; Self::new(ErrorKind::ArgumentNotFound) .set_info(info) .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]) } fn formatted(&self) -> Cow<'_, Colorizer> { if let Some(message) = self.inner.message.as_ref() { message.formatted() } else { let mut c = Colorizer::new(self.stream(), self.inner.color_when); start_error(&mut c); if !self.write_dynamic_context(&mut c) { if let Some(msg) = self.kind().as_str() { c.none(msg.to_owned()); } else if let Some(source) = self.inner.source.as_ref() { c.none(source.to_string()); } else { c.none("Unknown cause"); } } let usage = self.get_context(ContextKind::Usage); if let Some(ContextValue::String(usage)) = usage { put_usage(&mut c, usage); } try_help(&mut c, self.inner.help_flag); Cow::Owned(c) } } #[must_use] fn write_dynamic_context(&self, c: &mut Colorizer) -> bool { match self.kind() { ErrorKind::ArgumentConflict => { let invalid_arg = self.get_context(ContextKind::InvalidArg); let prior_arg = self.get_context(ContextKind::PriorArg); if let (Some(ContextValue::String(invalid_arg)), Some(prior_arg)) = (invalid_arg, prior_arg) { c.none("The argument '"); c.warning(invalid_arg); c.none("' cannot be used with"); match prior_arg { ContextValue::Strings(values) => { c.none(":"); for v in values { c.none("\n "); c.warning(&**v); } } ContextValue::String(value) => { c.none(" '"); c.warning(value); c.none("'"); } _ => { c.none(" one or more of the other specified arguments"); } } true } else { false } } ErrorKind::EmptyValue => { let invalid_arg = self.get_context(ContextKind::InvalidArg); if let Some(ContextValue::String(invalid_arg)) = invalid_arg { c.none("The argument '"); c.warning(invalid_arg); c.none("' requires a value but none was supplied"); let possible_values = self.get_context(ContextKind::ValidValue); if let Some(ContextValue::Strings(possible_values)) = possible_values { c.none("\n\t[possible values: "); if let Some((last, elements)) = possible_values.split_last() { for v in elements { c.good(escape(v)); c.none(", "); } c.good(escape(last)); } c.none("]"); } true } else { false } } ErrorKind::NoEquals => { let invalid_arg = self.get_context(ContextKind::InvalidArg); if let Some(ContextValue::String(invalid_arg)) = invalid_arg { c.none("Equal sign is needed when assigning values to '"); c.warning(invalid_arg); c.none("'."); true } else { false } } ErrorKind::InvalidValue => { let invalid_arg = self.get_context(ContextKind::InvalidArg); let invalid_value = self.get_context(ContextKind::InvalidValue); if let ( Some(ContextValue::String(invalid_arg)), Some(ContextValue::String(invalid_value)), ) = (invalid_arg, invalid_value) { c.none(quote(invalid_value)); c.none(" isn't a valid value for '"); c.warning(invalid_arg); c.none("'"); let possible_values = self.get_context(ContextKind::ValidValue); if let Some(ContextValue::Strings(possible_values)) = possible_values { c.none("\n\t[possible values: "); if let Some((last, elements)) = possible_values.split_last() { for v in elements { c.good(escape(v)); c.none(", "); } c.good(escape(last)); } c.none("]"); } let suggestion = self.get_context(ContextKind::SuggestedValue); if let Some(ContextValue::String(suggestion)) = suggestion { c.none("\n\n\tDid you mean "); c.good(quote(suggestion)); c.none("?"); } true } else { false } } ErrorKind::InvalidSubcommand => { let invalid_sub = self.get_context(ContextKind::InvalidSubcommand); if let Some(ContextValue::String(invalid_sub)) = invalid_sub { c.none("The subcommand '"); c.warning(invalid_sub); c.none("' wasn't recognized"); let valid_sub = self.get_context(ContextKind::SuggestedSubcommand); if let Some(ContextValue::String(valid_sub)) = valid_sub { c.none("\n\n\tDid you mean "); c.good(valid_sub); c.none("?"); } let suggestion = self.get_context(ContextKind::SuggestedCommand); if let Some(ContextValue::String(suggestion)) = suggestion { c.none( "\n\nIf you believe you received this message in error, try re-running with '", ); c.good(suggestion); c.none("'"); } true } else { false } } ErrorKind::UnrecognizedSubcommand => { let invalid_sub = self.get_context(ContextKind::InvalidSubcommand); if let Some(ContextValue::String(invalid_sub)) = invalid_sub { c.none("The subcommand '"); c.warning(invalid_sub); c.none("' wasn't recognized"); true } else { false } } ErrorKind::MissingRequiredArgument => { let invalid_arg = self.get_context(ContextKind::InvalidArg); if let Some(ContextValue::Strings(invalid_arg)) = invalid_arg { c.none("The following required arguments were not provided:"); for v in invalid_arg { c.none("\n "); c.good(&**v); } true } else { false } } ErrorKind::MissingSubcommand => { let invalid_sub = self.get_context(ContextKind::InvalidSubcommand); if let Some(ContextValue::String(invalid_sub)) = invalid_sub { c.none("'"); c.warning(invalid_sub); c.none("' requires a subcommand but one was not provided"); true } else { false } } ErrorKind::InvalidUtf8 => false, ErrorKind::TooManyOccurrences => { let invalid_arg = self.get_context(ContextKind::InvalidArg); let actual_num_occurs = self.get_context(ContextKind::ActualNumOccurrences); let max_occurs = self.get_context(ContextKind::MaxOccurrences); if let ( Some(ContextValue::String(invalid_arg)), Some(ContextValue::Number(actual_num_occurs)), Some(ContextValue::Number(max_occurs)), ) = (invalid_arg, actual_num_occurs, max_occurs) { let were_provided = Error::singular_or_plural(*actual_num_occurs as usize); c.none("The argument '"); c.warning(invalid_arg); c.none("' allows at most "); c.warning(max_occurs.to_string()); c.none(" occurrences but "); c.warning(actual_num_occurs.to_string()); c.none(were_provided); true } else { false } } ErrorKind::TooManyValues => { let invalid_arg = self.get_context(ContextKind::InvalidArg); let invalid_value = self.get_context(ContextKind::InvalidValue); if let ( Some(ContextValue::String(invalid_arg)), Some(ContextValue::String(invalid_value)), ) = (invalid_arg, invalid_value) { c.none("The value '"); c.warning(invalid_value); c.none("' was provided to '"); c.warning(invalid_arg); c.none("' but it wasn't expecting any more values"); true } else { false } } ErrorKind::TooFewValues => { let invalid_arg = self.get_context(ContextKind::InvalidArg); let actual_num_values = self.get_context(ContextKind::ActualNumValues); let min_values = self.get_context(ContextKind::MinValues); if let ( Some(ContextValue::String(invalid_arg)), Some(ContextValue::Number(actual_num_values)), Some(ContextValue::Number(min_values)), ) = (invalid_arg, actual_num_values, min_values) { let were_provided = Error::singular_or_plural(*actual_num_values as usize); c.none("The argument '"); c.warning(invalid_arg); c.none("' requires at least "); c.warning(min_values.to_string()); c.none(" values but only "); c.warning(actual_num_values.to_string()); c.none(were_provided); true } else { false } } ErrorKind::ValueValidation => { let invalid_arg = self.get_context(ContextKind::InvalidArg); let invalid_value = self.get_context(ContextKind::InvalidValue); if let ( Some(ContextValue::String(invalid_arg)), Some(ContextValue::String(invalid_value)), ) = (invalid_arg, invalid_value) { c.none("Invalid value "); c.warning(quote(invalid_value)); c.none(" for '"); c.warning(invalid_arg); if let Some(source) = self.inner.source.as_deref() { c.none("': "); c.none(source.to_string()); } else { c.none("'"); } true } else { false } } ErrorKind::WrongNumberOfValues => { let invalid_arg = self.get_context(ContextKind::InvalidArg); let actual_num_values = self.get_context(ContextKind::ActualNumValues); let num_values = self.get_context(ContextKind::ExpectedNumValues); if let ( Some(ContextValue::String(invalid_arg)), Some(ContextValue::Number(actual_num_values)), Some(ContextValue::Number(num_values)), ) = (invalid_arg, actual_num_values, num_values) { let were_provided = Error::singular_or_plural(*actual_num_values as usize); c.none("The argument '"); c.warning(invalid_arg); c.none("' requires "); c.warning(num_values.to_string()); c.none(" values, but "); c.warning(actual_num_values.to_string()); c.none(were_provided); true } else { false } } ErrorKind::UnexpectedMultipleUsage => { let invalid_arg = self.get_context(ContextKind::InvalidArg); if let Some(ContextValue::String(invalid_arg)) = invalid_arg { c.none("The argument '"); c.warning(invalid_arg.to_string()); c.none("' was provided more than once, but cannot be used multiple times"); true } else { false } } ErrorKind::UnknownArgument => { let invalid_arg = self.get_context(ContextKind::InvalidArg); if let Some(ContextValue::String(invalid_arg)) = invalid_arg { c.none("Found argument '"); c.warning(invalid_arg.to_string()); c.none("' which wasn't expected, or isn't valid in this context"); let valid_sub = self.get_context(ContextKind::SuggestedSubcommand); let valid_arg = self.get_context(ContextKind::SuggestedArg); match (valid_sub, valid_arg) { ( Some(ContextValue::String(valid_sub)), Some(ContextValue::String(valid_arg)), ) => { c.none("\n\n\tDid you mean "); c.none("to put '"); c.good(valid_arg); c.none("' after the subcommand '"); c.good(valid_sub); c.none("'?"); } (None, Some(ContextValue::String(valid_arg))) => { c.none("\n\n\tDid you mean '"); c.good(valid_arg); c.none("'?"); } (_, _) => {} } let invalid_arg = self.get_context(ContextKind::InvalidArg); if let Some(ContextValue::String(invalid_arg)) = invalid_arg { if invalid_arg.starts_with('-') { c.none(format!( "\n\n\tIf you tried to supply `{}` as a value rather than a flag, use `-- {}`", invalid_arg, invalid_arg )); } let trailing_arg = self.get_context(ContextKind::TrailingArg); if trailing_arg == Some(&ContextValue::Bool(true)) { c.none(format!( "\n\n\tIf you tried to supply `{}` as a subcommand, remove the '--' before it.", invalid_arg )); } } true } else { false } } ErrorKind::ArgumentNotFound => { let invalid_arg = self.get_context(ContextKind::InvalidArg); if let Some(ContextValue::String(invalid_arg)) = invalid_arg { c.none("The argument '"); c.warning(invalid_arg.to_string()); c.none("' wasn't found"); true } else { false } } ErrorKind::DisplayHelp | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand | ErrorKind::DisplayVersion | ErrorKind::Io | ErrorKind::Format => false, } } /// Returns the singular or plural form on the verb to be based on the argument's value. fn singular_or_plural(n: usize) -> &'static str { if n > 1 { " were provided" } else { " was provided" } } } impl From for Error { fn from(e: io::Error) -> Self { Error::raw(ErrorKind::Io, e) } } impl From for Error { fn from(e: fmt::Error) -> Self { Error::raw(ErrorKind::Format, e) } } impl error::Error for Error { #[allow(trivial_casts)] fn source(&self) -> Option<&(dyn error::Error + 'static)> { self.inner.source.as_ref().map(|e| e.as_ref() as _) } } impl Display for Error { fn fmt(&self, f: &mut Formatter) -> fmt::Result { // Assuming `self.message` already has a trailing newline, from `try_help` or similar write!(f, "{}", self.formatted())?; if let Some(backtrace) = self.inner.backtrace.as_ref() { writeln!(f)?; writeln!(f, "Backtrace:")?; writeln!(f, "{}", backtrace)?; } Ok(()) } } fn start_error(c: &mut Colorizer) { c.error("error:"); c.none(" "); } fn put_usage(c: &mut Colorizer, usage: impl Into) { c.none("\n\n"); c.none(usage); } fn get_help_flag(cmd: &Command) -> Option<&'static str> { if !cmd.is_disable_help_flag_set() { Some("--help") } else if cmd.has_subcommands() && !cmd.is_disable_help_subcommand_set() { Some("help") } else { None } } fn try_help(c: &mut Colorizer, help: Option<&str>) { if let Some(help) = help { c.none("\n\nFor more information try "); c.good(help); c.none("\n"); } else { c.none("\n"); } } fn quote(s: impl AsRef) -> String { let s = s.as_ref(); format!("{:?}", s) } fn escape(s: impl AsRef) -> String { let s = s.as_ref(); if s.contains(char::is_whitespace) { quote(s) } else { s.to_owned() } } #[derive(Clone, Debug)] pub(crate) enum Message { Raw(String), Formatted(Colorizer), } impl Message { fn format(&mut self, cmd: &Command, usage: String) { match self { Message::Raw(s) => { let mut c = Colorizer::new(Stream::Stderr, cmd.get_color()); let mut message = String::new(); std::mem::swap(s, &mut message); start_error(&mut c); c.none(message); put_usage(&mut c, usage); try_help(&mut c, get_help_flag(cmd)); *self = Self::Formatted(c); } Message::Formatted(_) => {} } } fn formatted(&self) -> Cow { match self { Message::Raw(s) => { let mut c = Colorizer::new(Stream::Stderr, ColorChoice::Never); start_error(&mut c); c.none(s); Cow::Owned(c) } Message::Formatted(c) => Cow::Borrowed(c), } } } impl From for Message { fn from(inner: String) -> Self { Self::Raw(inner) } } impl From for Message { fn from(inner: Colorizer) -> Self { Self::Formatted(inner) } } #[cfg(feature = "debug")] #[derive(Debug)] struct Backtrace(backtrace::Backtrace); #[cfg(feature = "debug")] impl Backtrace { fn new() -> Option { Some(Self(backtrace::Backtrace::new())) } } #[cfg(feature = "debug")] impl Display for Backtrace { fn fmt(&self, f: &mut Formatter) -> fmt::Result { // `backtrace::Backtrace` uses `Debug` instead of `Display` write!(f, "{:?}", self.0) } } #[cfg(not(feature = "debug"))] #[derive(Debug)] struct Backtrace; #[cfg(not(feature = "debug"))] impl Backtrace { fn new() -> Option { None } } #[cfg(not(feature = "debug"))] impl Display for Backtrace { fn fmt(&self, _: &mut Formatter) -> fmt::Result { Ok(()) } } #[test] fn check_auto_traits() { static_assertions::assert_impl_all!(Error: Send, Sync, Unpin); }