summaryrefslogtreecommitdiffstats
path: root/vendor/clap-3.2.20/src/error
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/clap-3.2.20/src/error')
-rw-r--r--vendor/clap-3.2.20/src/error/context.rs55
-rw-r--r--vendor/clap-3.2.20/src/error/kind.rs440
-rw-r--r--vendor/clap-3.2.20/src/error/mod.rs1153
3 files changed, 1648 insertions, 0 deletions
diff --git a/vendor/clap-3.2.20/src/error/context.rs b/vendor/clap-3.2.20/src/error/context.rs
new file mode 100644
index 000000000..985cd4d70
--- /dev/null
+++ b/vendor/clap-3.2.20/src/error/context.rs
@@ -0,0 +1,55 @@
+/// Semantics for a piece of error information
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum ContextKind {
+ /// The cause of the error
+ InvalidSubcommand,
+ /// The cause of the error
+ InvalidArg,
+ /// Existing arguments
+ PriorArg,
+ /// Accepted values
+ ValidValue,
+ /// Rejected values
+ InvalidValue,
+ /// Number of values present
+ ActualNumValues,
+ /// Number of allowed values
+ ExpectedNumValues,
+ /// Minimum number of allowed values
+ MinValues,
+ /// Number of occurrences present
+ ActualNumOccurrences,
+ /// Maximum number of allowed occurrences
+ MaxOccurrences,
+ /// Potential fix for the user
+ SuggestedCommand,
+ /// Potential fix for the user
+ SuggestedSubcommand,
+ /// Potential fix for the user
+ SuggestedArg,
+ /// Potential fix for the user
+ SuggestedValue,
+ /// Trailing argument
+ TrailingArg,
+ /// A usage string
+ Usage,
+ /// An opaque message to the user
+ Custom,
+}
+
+/// A piece of error information
+#[derive(Clone, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum ContextValue {
+ /// [`ContextKind`] is self-sufficient, no additional information needed
+ None,
+ /// A single value
+ Bool(bool),
+ /// A single value
+ String(String),
+ /// Many values
+ Strings(Vec<String>),
+ /// A single value
+ Number(isize),
+}
diff --git a/vendor/clap-3.2.20/src/error/kind.rs b/vendor/clap-3.2.20/src/error/kind.rs
new file mode 100644
index 000000000..4c3dc48b0
--- /dev/null
+++ b/vendor/clap-3.2.20/src/error/kind.rs
@@ -0,0 +1,440 @@
+/// Command line argument parser kind of error
+#[derive(Debug, Copy, Clone, PartialEq)]
+#[non_exhaustive]
+pub enum ErrorKind {
+ /// Occurs when an [`Arg`][crate::Arg] has a set of possible values,
+ /// and the user provides a value which isn't in that set.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("speed")
+ /// .value_parser(["fast", "slow"]))
+ /// .try_get_matches_from(vec!["prog", "other"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
+ /// ```
+ InvalidValue,
+
+ /// Occurs when a user provides a flag, option, argument or subcommand which isn't defined.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, arg, ErrorKind};
+ /// let result = Command::new("prog")
+ /// .arg(arg!(--flag "some flag"))
+ /// .try_get_matches_from(vec!["prog", "--other"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::UnknownArgument);
+ /// ```
+ UnknownArgument,
+
+ /// Occurs when the user provides an unrecognized [`Subcommand`] which meets the threshold for
+ /// being similar enough to an existing subcommand.
+ /// If it doesn't meet the threshold, or the 'suggestions' feature is disabled,
+ /// the more general [`UnknownArgument`] error is returned.
+ ///
+ /// # Examples
+ ///
+ #[cfg_attr(not(feature = "suggestions"), doc = " ```no_run")]
+ #[cfg_attr(feature = "suggestions", doc = " ```")]
+ /// # use clap::{Command, Arg, ErrorKind, };
+ /// let result = Command::new("prog")
+ /// .subcommand(Command::new("config")
+ /// .about("Used for configuration")
+ /// .arg(Arg::new("config_file")
+ /// .help("The configuration file to use")))
+ /// .try_get_matches_from(vec!["prog", "confi"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
+ /// ```
+ ///
+ /// [`Subcommand`]: crate::Subcommand
+ /// [`UnknownArgument`]: ErrorKind::UnknownArgument
+ InvalidSubcommand,
+
+ /// Occurs when the user provides an unrecognized [`Subcommand`] which either
+ /// doesn't meet the threshold for being similar enough to an existing subcommand,
+ /// or the 'suggestions' feature is disabled.
+ /// Otherwise the more detailed [`InvalidSubcommand`] error is returned.
+ ///
+ /// This error typically happens when passing additional subcommand names to the `help`
+ /// subcommand. Otherwise, the more general [`UnknownArgument`] error is used.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind, };
+ /// let result = Command::new("prog")
+ /// .subcommand(Command::new("config")
+ /// .about("Used for configuration")
+ /// .arg(Arg::new("config_file")
+ /// .help("The configuration file to use")))
+ /// .try_get_matches_from(vec!["prog", "help", "nothing"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::UnrecognizedSubcommand);
+ /// ```
+ ///
+ /// [`Subcommand`]: crate::Subcommand
+ /// [`InvalidSubcommand`]: ErrorKind::InvalidSubcommand
+ /// [`UnknownArgument`]: ErrorKind::UnknownArgument
+ UnrecognizedSubcommand,
+
+ /// Occurs when the user provides an empty value for an option that does not allow empty
+ /// values.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let res = Command::new("prog")
+ /// .arg(Arg::new("color")
+ /// .takes_value(true)
+ /// .forbid_empty_values(true)
+ /// .long("color"))
+ /// .try_get_matches_from(vec!["prog", "--color="]);
+ /// assert!(res.is_err());
+ /// assert_eq!(res.unwrap_err().kind(), ErrorKind::EmptyValue);
+ /// ```
+ EmptyValue,
+
+ /// Occurs when the user doesn't use equals for an option that requires equal
+ /// sign to provide values.
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let res = Command::new("prog")
+ /// .arg(Arg::new("color")
+ /// .takes_value(true)
+ /// .require_equals(true)
+ /// .long("color"))
+ /// .try_get_matches_from(vec!["prog", "--color", "red"]);
+ /// assert!(res.is_err());
+ /// assert_eq!(res.unwrap_err().kind(), ErrorKind::NoEquals);
+ /// ```
+ NoEquals,
+
+ /// Occurs when the user provides a value for an argument with a custom validation and the
+ /// value fails that validation.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// fn is_numeric(val: &str) -> Result<(), String> {
+ /// match val.parse::<i64>() {
+ /// Ok(..) => Ok(()),
+ /// Err(..) => Err(String::from("Value wasn't a number!")),
+ /// }
+ /// }
+ ///
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("num")
+ /// .validator(is_numeric))
+ /// .try_get_matches_from(vec!["prog", "NotANumber"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::ValueValidation);
+ /// ```
+ ValueValidation,
+
+ /// Occurs when a user provides more values for an argument than were defined by setting
+ /// [`Arg::max_values`].
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("arg")
+ /// .max_values(2))
+ /// .try_get_matches_from(vec!["prog", "too", "many", "values"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::TooManyValues);
+ /// ```
+ /// [`Arg::max_values`]: crate::Arg::max_values()
+ TooManyValues,
+
+ /// Occurs when the user provides fewer values for an argument than were defined by setting
+ /// [`Arg::min_values`].
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("some_opt")
+ /// .long("opt")
+ /// .min_values(3))
+ /// .try_get_matches_from(vec!["prog", "--opt", "too", "few"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::TooFewValues);
+ /// ```
+ /// [`Arg::min_values`]: crate::Arg::min_values()
+ TooFewValues,
+
+ /// Occurs when a user provides more occurrences for an argument than were defined by setting
+ /// [`Arg::max_occurrences`].
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("verbosity")
+ /// .short('v')
+ /// .max_occurrences(2))
+ /// .try_get_matches_from(vec!["prog", "-vvv"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::TooManyOccurrences);
+ /// ```
+ /// [`Arg::max_occurrences`]: crate::Arg::max_occurrences()
+ TooManyOccurrences,
+
+ /// Occurs when the user provides a different number of values for an argument than what's
+ /// been defined by setting [`Arg::number_of_values`] or than was implicitly set by
+ /// [`Arg::value_names`].
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("some_opt")
+ /// .long("opt")
+ /// .takes_value(true)
+ /// .number_of_values(2))
+ /// .try_get_matches_from(vec!["prog", "--opt", "wrong"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::WrongNumberOfValues);
+ /// ```
+ ///
+ /// [`Arg::number_of_values`]: crate::Arg::number_of_values()
+ /// [`Arg::value_names`]: crate::Arg::value_names()
+ WrongNumberOfValues,
+
+ /// Occurs when the user provides two values which conflict with each other and can't be used
+ /// together.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("debug")
+ /// .long("debug")
+ /// .conflicts_with("color"))
+ /// .arg(Arg::new("color")
+ /// .long("color"))
+ /// .try_get_matches_from(vec!["prog", "--debug", "--color"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::ArgumentConflict);
+ /// ```
+ ArgumentConflict,
+
+ /// Occurs when the user does not provide one or more required arguments.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("debug")
+ /// .required(true))
+ /// .try_get_matches_from(vec!["prog"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
+ /// ```
+ MissingRequiredArgument,
+
+ /// Occurs when a subcommand is required (as defined by [`Command::subcommand_required`]),
+ /// but the user does not provide one.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, ErrorKind};
+ /// let err = Command::new("prog")
+ /// .subcommand_required(true)
+ /// .subcommand(Command::new("test"))
+ /// .try_get_matches_from(vec![
+ /// "myprog",
+ /// ]);
+ /// assert!(err.is_err());
+ /// assert_eq!(err.unwrap_err().kind(), ErrorKind::MissingSubcommand);
+ /// # ;
+ /// ```
+ ///
+ /// [`Command::subcommand_required`]: crate::Command::subcommand_required
+ MissingSubcommand,
+
+ /// Occurs when the user provides multiple values to an argument which doesn't allow that.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("debug")
+ /// .long("debug")
+ /// .multiple_occurrences(false))
+ /// .try_get_matches_from(vec!["prog", "--debug", "--debug"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::UnexpectedMultipleUsage);
+ /// ```
+ UnexpectedMultipleUsage,
+
+ /// Occurs when the user provides a value containing invalid UTF-8.
+ ///
+ /// To allow arbitrary data
+ /// - Set [`Arg::allow_invalid_utf8`] for argument values
+ /// - Set [`Command::allow_invalid_utf8_for_external_subcommands`] for external-subcommand
+ /// values
+ ///
+ /// # Platform Specific
+ ///
+ /// Non-Windows platforms only (such as Linux, Unix, OSX, etc.)
+ ///
+ /// # Examples
+ ///
+ #[cfg_attr(not(unix), doc = " ```ignore")]
+ #[cfg_attr(unix, doc = " ```")]
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// # use std::os::unix::ffi::OsStringExt;
+ /// # use std::ffi::OsString;
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("utf8")
+ /// .short('u')
+ /// .takes_value(true))
+ /// .try_get_matches_from(vec![OsString::from("myprog"),
+ /// OsString::from("-u"),
+ /// OsString::from_vec(vec![0xE9])]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidUtf8);
+ /// ```
+ ///
+ /// [`Arg::allow_invalid_utf8`]: crate::Arg::allow_invalid_utf8
+ /// [`Command::allow_invalid_utf8_for_external_subcommands`]: crate::Command::allow_invalid_utf8_for_external_subcommands
+ InvalidUtf8,
+
+ /// Not a true "error" as it means `--help` or similar was used.
+ /// The help message will be sent to `stdout`.
+ ///
+ /// **Note**: If the help is displayed due to an error (such as missing subcommands) it will
+ /// be sent to `stderr` instead of `stdout`.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let result = Command::new("prog")
+ /// .try_get_matches_from(vec!["prog", "--help"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::DisplayHelp);
+ /// ```
+ DisplayHelp,
+
+ /// Occurs when either an argument or a [`Subcommand`] is required, as defined by
+ /// [`Command::arg_required_else_help`] , but the user did not provide
+ /// one.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind, };
+ /// let result = Command::new("prog")
+ /// .arg_required_else_help(true)
+ /// .subcommand(Command::new("config")
+ /// .about("Used for configuration")
+ /// .arg(Arg::new("config_file")
+ /// .help("The configuration file to use")))
+ /// .try_get_matches_from(vec!["prog"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand);
+ /// ```
+ ///
+ /// [`Subcommand`]: crate::Subcommand
+ /// [`Command::arg_required_else_help`]: crate::Command::arg_required_else_help
+ DisplayHelpOnMissingArgumentOrSubcommand,
+
+ /// Not a true "error" as it means `--version` or similar was used.
+ /// The message will be sent to `stdout`.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{Command, Arg, ErrorKind};
+ /// let result = Command::new("prog")
+ /// .version("3.0")
+ /// .try_get_matches_from(vec!["prog", "--version"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::DisplayVersion);
+ /// ```
+ DisplayVersion,
+
+ /// Occurs when using the [`ArgMatches::value_of_t`] and friends to convert an argument value
+ /// into type `T`, but the argument you requested wasn't used. I.e. you asked for an argument
+ /// with name `config` to be converted, but `config` wasn't used by the user.
+ ///
+ /// [`ArgMatches::value_of_t`]: crate::ArgMatches::value_of_t()
+ ArgumentNotFound,
+
+ /// Represents an [I/O error].
+ /// Can occur when writing to `stderr` or `stdout` or reading a configuration file.
+ ///
+ /// [I/O error]: std::io::Error
+ Io,
+
+ /// Represents a [Format error] (which is a part of [`Display`]).
+ /// Typically caused by writing to `stderr` or `stdout`.
+ ///
+ /// [`Display`]: std::fmt::Display
+ /// [Format error]: std::fmt::Error
+ Format,
+}
+
+impl ErrorKind {
+ /// End-user description of the error case, where relevant
+ pub fn as_str(self) -> Option<&'static str> {
+ match self {
+ Self::InvalidValue => Some("One of the values isn't valid for an argument"),
+ Self::UnknownArgument => {
+ Some("Found an argument which wasn't expected or isn't valid in this context")
+ }
+ Self::InvalidSubcommand => Some("A subcommand wasn't recognized"),
+ Self::UnrecognizedSubcommand => Some("A subcommand wasn't recognized"),
+ Self::EmptyValue => Some("An argument requires a value but none was supplied"),
+ Self::NoEquals => Some("Equal is needed when assigning values to one of the arguments"),
+ Self::ValueValidation => Some("Invalid value for one of the arguments"),
+ Self::TooManyValues => Some("An argument received an unexpected value"),
+ Self::TooFewValues => Some("An argument requires more values"),
+ Self::TooManyOccurrences => Some("An argument occurred too many times"),
+ Self::WrongNumberOfValues => Some("An argument received too many or too few values"),
+ Self::ArgumentConflict => {
+ Some("An argument cannot be used with one or more of the other specified arguments")
+ }
+ Self::MissingRequiredArgument => {
+ Some("One or more required arguments were not provided")
+ }
+ Self::MissingSubcommand => Some("A subcommand is required but one was not provided"),
+ Self::UnexpectedMultipleUsage => {
+ Some("An argument was provided more than once but cannot be used multiple times")
+ }
+ Self::InvalidUtf8 => Some("Invalid UTF-8 was detected in one or more arguments"),
+ Self::DisplayHelp => None,
+ Self::DisplayHelpOnMissingArgumentOrSubcommand => None,
+ Self::DisplayVersion => None,
+ Self::ArgumentNotFound => Some("An argument wasn't found"),
+ Self::Io => None,
+ Self::Format => None,
+ }
+ }
+}
+
+impl std::fmt::Display for ErrorKind {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.as_str().unwrap_or_default().fmt(f)
+ }
+}
diff --git a/vendor/clap-3.2.20/src/error/mod.rs b/vendor/clap-3.2.20/src/error/mod.rs
new file mode 100644
index 000000000..fbd2b30a8
--- /dev/null
+++ b/vendor/clap-3.2.20/src/error/mod.rs
@@ -0,0 +1,1153 @@
+//! 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<T, E = Error> = StdResult<T, E>;
+
+/// Command Line Argument Parser Error
+///
+/// See [`Command::error`] to create an error.
+///
+/// [`Command::error`]: crate::Command::error
+#[derive(Debug)]
+pub struct Error {
+ inner: Box<ErrorInner>,
+ /// 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<String>,
+}
+
+#[derive(Debug)]
+struct ErrorInner {
+ kind: ErrorKind,
+ context: Vec<(ContextKind, ContextValue)>,
+ message: Option<Message>,
+ source: Option<Box<dyn error::Error + Send + Sync>>,
+ help_flag: Option<&'static str>,
+ color_when: ColorChoice,
+ wait_on_exit: bool,
+ backtrace: Option<Backtrace>,
+}
+
+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<Item = (ContextKind, &ContextValue)> {
+ 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<String>) -> 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<Message>) -> Self {
+ self.inner.message = Some(message.into());
+ self
+ }
+
+ pub(crate) fn set_info(mut self, info: Vec<String>) -> Self {
+ self.info = info;
+ self
+ }
+
+ pub(crate) fn set_source(mut self, source: Box<dyn error::Error + Send + Sync>) -> 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<const N: usize>(
+ 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<String>,
+ 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<String>,
+ 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<dyn error::Error + Send + Sync>,
+ ) -> 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<String>)>,
+ 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<io::Error> for Error {
+ fn from(e: io::Error) -> Self {
+ Error::raw(ErrorKind::Io, e)
+ }
+}
+
+impl From<fmt::Error> 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<String>) {
+ 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<str>) -> String {
+ let s = s.as_ref();
+ format!("{:?}", s)
+}
+
+fn escape(s: impl AsRef<str>) -> 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<Colorizer> {
+ 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<String> for Message {
+ fn from(inner: String) -> Self {
+ Self::Raw(inner)
+ }
+}
+
+impl From<Colorizer> 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<Self> {
+ 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<Self> {
+ 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);
+}