summaryrefslogtreecommitdiffstats
path: root/vendor/clap_builder/src/error
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/clap_builder/src/error')
-rw-r--r--vendor/clap_builder/src/error/context.rs114
-rw-r--r--vendor/clap_builder/src/error/format.rs466
-rw-r--r--vendor/clap_builder/src/error/kind.rs366
-rw-r--r--vendor/clap_builder/src/error/mod.rs881
4 files changed, 1827 insertions, 0 deletions
diff --git a/vendor/clap_builder/src/error/context.rs b/vendor/clap_builder/src/error/context.rs
new file mode 100644
index 000000000..045923c47
--- /dev/null
+++ b/vendor/clap_builder/src/error/context.rs
@@ -0,0 +1,114 @@
+/// Semantics for a piece of error information
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[non_exhaustive]
+#[cfg(feature = "error-context")]
+pub enum ContextKind {
+ /// The cause of the error
+ InvalidSubcommand,
+ /// The cause of the error
+ InvalidArg,
+ /// Existing arguments
+ PriorArg,
+ /// Accepted subcommands
+ ValidSubcommand,
+ /// Accepted values
+ ValidValue,
+ /// Rejected values
+ InvalidValue,
+ /// Number of values present
+ ActualNumValues,
+ /// Number of allowed values
+ ExpectedNumValues,
+ /// Minimum number of allowed values
+ MinValues,
+ /// 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,
+ /// Potential fix for the user
+ Suggested,
+ /// A usage string
+ Usage,
+ /// An opaque message to the user
+ Custom,
+}
+
+impl ContextKind {
+ /// End-user description of the error case, where relevant
+ pub fn as_str(self) -> Option<&'static str> {
+ match self {
+ Self::InvalidSubcommand => Some("Invalid Subcommand"),
+ Self::InvalidArg => Some("Invalid Argument"),
+ Self::PriorArg => Some("Prior Argument"),
+ Self::ValidSubcommand => Some("Valid Subcommand"),
+ Self::ValidValue => Some("Valid Value"),
+ Self::InvalidValue => Some("Invalid Value"),
+ Self::ActualNumValues => Some("Actual Number of Values"),
+ Self::ExpectedNumValues => Some("Expected Number of Values"),
+ Self::MinValues => Some("Minimum Number of Values"),
+ Self::SuggestedCommand => Some("Suggested Command"),
+ Self::SuggestedSubcommand => Some("Suggested Subcommand"),
+ Self::SuggestedArg => Some("Suggested Argument"),
+ Self::SuggestedValue => Some("Suggested Value"),
+ Self::TrailingArg => Some("Trailing Argument"),
+ Self::Suggested => Some("Suggested"),
+ Self::Usage => None,
+ Self::Custom => None,
+ }
+ }
+}
+
+impl std::fmt::Display for ContextKind {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.as_str().unwrap_or_default().fmt(f)
+ }
+}
+
+/// A piece of error information
+#[derive(Clone, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+#[cfg(feature = "error-context")]
+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
+ StyledStr(crate::builder::StyledStr),
+ /// many value
+ StyledStrs(Vec<crate::builder::StyledStr>),
+ /// A single value
+ Number(isize),
+}
+
+impl std::fmt::Display for ContextValue {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::None => "".fmt(f),
+ Self::Bool(v) => v.fmt(f),
+ 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/vendor/clap_builder/src/error/format.rs b/vendor/clap_builder/src/error/format.rs
new file mode 100644
index 000000000..5ada0abe5
--- /dev/null
+++ b/vendor/clap_builder/src/error/format.rs
@@ -0,0 +1,466 @@
+#![allow(missing_copy_implementations)]
+#![allow(missing_debug_implementations)]
+#![cfg_attr(not(feature = "error-context"), allow(dead_code))]
+#![cfg_attr(not(feature = "error-context"), allow(unused_imports))]
+
+use crate::builder::Command;
+use crate::builder::StyledStr;
+#[cfg(feature = "error-context")]
+use crate::error::ContextKind;
+#[cfg(feature = "error-context")]
+use crate::error::ContextValue;
+use crate::error::ErrorKind;
+use crate::output::TAB;
+
+/// Defines how to format an error for displaying to the user
+pub trait ErrorFormatter: Sized {
+ /// Stylize the error for the terminal
+ fn format_error(error: &crate::error::Error<Self>) -> StyledStr;
+}
+
+/// Report [`ErrorKind`]
+///
+/// No context is included.
+///
+/// **NOTE:** Consider removing the `error-context` default feature if using this to remove all
+/// overhead for [`RichFormatter`].
+#[non_exhaustive]
+pub struct KindFormatter;
+
+impl ErrorFormatter for KindFormatter {
+ fn format_error(error: &crate::error::Error<Self>) -> StyledStr {
+ let mut styled = StyledStr::new();
+ start_error(&mut styled);
+ if let Some(msg) = error.kind().as_str() {
+ styled.none(msg.to_owned());
+ } else if let Some(source) = error.inner.source.as_ref() {
+ styled.none(source.to_string());
+ } else {
+ styled.none("unknown cause");
+ }
+ styled.none("\n");
+ styled
+ }
+}
+
+/// Richly formatted error context
+///
+/// This follows the [rustc diagnostic style guide](https://rustc-dev-guide.rust-lang.org/diagnostics.html#suggestion-style-guide).
+#[non_exhaustive]
+#[cfg(feature = "error-context")]
+pub struct RichFormatter;
+
+#[cfg(feature = "error-context")]
+impl ErrorFormatter for RichFormatter {
+ fn format_error(error: &crate::error::Error<Self>) -> StyledStr {
+ let mut styled = StyledStr::new();
+ start_error(&mut styled);
+
+ if !write_dynamic_context(error, &mut styled) {
+ if let Some(msg) = error.kind().as_str() {
+ styled.none(msg.to_owned());
+ } else if let Some(source) = error.inner.source.as_ref() {
+ styled.none(source.to_string());
+ } else {
+ styled.none("unknown cause");
+ }
+ }
+
+ let mut suggested = false;
+ if let Some(valid) = error.get(ContextKind::SuggestedSubcommand) {
+ styled.none("\n");
+ if !suggested {
+ styled.none("\n");
+ suggested = true;
+ }
+ did_you_mean(&mut styled, "subcommand", valid);
+ }
+ if let Some(valid) = error.get(ContextKind::SuggestedArg) {
+ styled.none("\n");
+ if !suggested {
+ styled.none("\n");
+ suggested = true;
+ }
+ did_you_mean(&mut styled, "argument", valid);
+ }
+ if let Some(valid) = error.get(ContextKind::SuggestedValue) {
+ styled.none("\n");
+ if !suggested {
+ styled.none("\n");
+ suggested = true;
+ }
+ did_you_mean(&mut styled, "value", valid);
+ }
+ let suggestions = error.get(ContextKind::Suggested);
+ if let Some(ContextValue::StyledStrs(suggestions)) = suggestions {
+ if !suggested {
+ styled.none("\n");
+ }
+ for suggestion in suggestions {
+ styled.none("\n");
+ styled.none(TAB);
+ styled.good("tip: ");
+ styled.push_styled(suggestion);
+ }
+ }
+
+ let usage = error.get(ContextKind::Usage);
+ if let Some(ContextValue::StyledStr(usage)) = usage {
+ put_usage(&mut styled, usage);
+ }
+
+ try_help(&mut styled, error.inner.help_flag);
+
+ styled
+ }
+}
+
+fn start_error(styled: &mut StyledStr) {
+ styled.error("error:");
+ styled.none(" ");
+}
+
+#[must_use]
+#[cfg(feature = "error-context")]
+fn write_dynamic_context(error: &crate::error::Error, styled: &mut StyledStr) -> bool {
+ match error.kind() {
+ ErrorKind::ArgumentConflict => {
+ let invalid_arg = error.get(ContextKind::InvalidArg);
+ let prior_arg = error.get(ContextKind::PriorArg);
+ if let (Some(ContextValue::String(invalid_arg)), Some(prior_arg)) =
+ (invalid_arg, prior_arg)
+ {
+ if ContextValue::String(invalid_arg.clone()) == *prior_arg {
+ styled.none("the argument '");
+ styled.warning(invalid_arg);
+ styled.none("' cannot be used multiple times");
+ } else {
+ styled.none("the argument '");
+ styled.warning(invalid_arg);
+ styled.none("' cannot be used with");
+
+ match prior_arg {
+ ContextValue::Strings(values) => {
+ styled.none(":");
+ for v in values {
+ styled.none("\n");
+ styled.none(TAB);
+ styled.warning(&**v);
+ }
+ }
+ ContextValue::String(value) => {
+ styled.none(" '");
+ styled.warning(value);
+ styled.none("'");
+ }
+ _ => {
+ styled.none(" one or more of the other specified arguments");
+ }
+ }
+ }
+ true
+ } else {
+ false
+ }
+ }
+ ErrorKind::NoEquals => {
+ let invalid_arg = error.get(ContextKind::InvalidArg);
+ if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
+ styled.none("equal sign is needed when assigning values to '");
+ styled.warning(invalid_arg);
+ styled.none("'");
+ true
+ } else {
+ false
+ }
+ }
+ ErrorKind::InvalidValue => {
+ let invalid_arg = error.get(ContextKind::InvalidArg);
+ let invalid_value = error.get(ContextKind::InvalidValue);
+ if let (
+ Some(ContextValue::String(invalid_arg)),
+ Some(ContextValue::String(invalid_value)),
+ ) = (invalid_arg, invalid_value)
+ {
+ if invalid_value.is_empty() {
+ styled.none("a value is required for '");
+ styled.warning(invalid_arg);
+ styled.none("' but none was supplied");
+ } else {
+ styled.none("invalid value '");
+ styled.none(invalid_value);
+ styled.none("' for '");
+ styled.warning(invalid_arg);
+ styled.none("'");
+ }
+
+ let possible_values = error.get(ContextKind::ValidValue);
+ if let Some(ContextValue::Strings(possible_values)) = possible_values {
+ if !possible_values.is_empty() {
+ styled.none("\n");
+ styled.none(TAB);
+ styled.none("[possible values: ");
+ 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
+ }
+ }
+ ErrorKind::InvalidSubcommand => {
+ let invalid_sub = error.get(ContextKind::InvalidSubcommand);
+ if let Some(ContextValue::String(invalid_sub)) = invalid_sub {
+ styled.none("unrecognized subcommand '");
+ styled.warning(invalid_sub);
+ styled.none("'");
+ true
+ } else {
+ false
+ }
+ }
+ ErrorKind::MissingRequiredArgument => {
+ let invalid_arg = error.get(ContextKind::InvalidArg);
+ if let Some(ContextValue::Strings(invalid_arg)) = invalid_arg {
+ styled.none("the following required arguments were not provided:");
+ for v in invalid_arg {
+ styled.none("\n");
+ styled.none(TAB);
+ styled.good(&**v);
+ }
+ true
+ } else {
+ false
+ }
+ }
+ ErrorKind::MissingSubcommand => {
+ let invalid_sub = error.get(ContextKind::InvalidSubcommand);
+ if let Some(ContextValue::String(invalid_sub)) = invalid_sub {
+ 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
+ }
+ }
+ ErrorKind::InvalidUtf8 => false,
+ ErrorKind::TooManyValues => {
+ let invalid_arg = error.get(ContextKind::InvalidArg);
+ let invalid_value = error.get(ContextKind::InvalidValue);
+ if let (
+ Some(ContextValue::String(invalid_arg)),
+ Some(ContextValue::String(invalid_value)),
+ ) = (invalid_arg, invalid_value)
+ {
+ styled.none("unexpected value '");
+ styled.warning(invalid_value);
+ styled.none("' for '");
+ styled.warning(invalid_arg);
+ styled.none("' found; no more were expected");
+ true
+ } else {
+ false
+ }
+ }
+ ErrorKind::TooFewValues => {
+ let invalid_arg = error.get(ContextKind::InvalidArg);
+ let actual_num_values = error.get(ContextKind::ActualNumValues);
+ let min_values = error.get(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 = singular_or_plural(*actual_num_values as usize);
+ styled.warning(min_values.to_string());
+ styled.none(" more values required by '");
+ styled.warning(invalid_arg);
+ styled.none("'; only ");
+ styled.warning(actual_num_values.to_string());
+ styled.none(were_provided);
+ true
+ } else {
+ false
+ }
+ }
+ ErrorKind::ValueValidation => {
+ let invalid_arg = error.get(ContextKind::InvalidArg);
+ let invalid_value = error.get(ContextKind::InvalidValue);
+ if let (
+ Some(ContextValue::String(invalid_arg)),
+ Some(ContextValue::String(invalid_value)),
+ ) = (invalid_arg, invalid_value)
+ {
+ styled.none("invalid value '");
+ styled.warning(invalid_value);
+ styled.none("' for '");
+ styled.warning(invalid_arg);
+ if let Some(source) = error.inner.source.as_deref() {
+ styled.none("': ");
+ styled.none(source.to_string());
+ } else {
+ styled.none("'");
+ }
+ true
+ } else {
+ false
+ }
+ }
+ ErrorKind::WrongNumberOfValues => {
+ let invalid_arg = error.get(ContextKind::InvalidArg);
+ let actual_num_values = error.get(ContextKind::ActualNumValues);
+ let num_values = error.get(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 = singular_or_plural(*actual_num_values as usize);
+ styled.warning(num_values.to_string());
+ styled.none(" values required for '");
+ styled.warning(invalid_arg);
+ styled.none("' but ");
+ styled.warning(actual_num_values.to_string());
+ styled.none(were_provided);
+ true
+ } else {
+ false
+ }
+ }
+ ErrorKind::UnknownArgument => {
+ let invalid_arg = error.get(ContextKind::InvalidArg);
+ if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
+ styled.none("unexpected argument '");
+ styled.warning(invalid_arg.to_string());
+ styled.none("' found");
+ true
+ } else {
+ false
+ }
+ }
+ ErrorKind::DisplayHelp
+ | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
+ | ErrorKind::DisplayVersion
+ | ErrorKind::Io
+ | ErrorKind::Format => false,
+ }
+}
+
+pub(crate) fn format_error_message(
+ message: &str,
+ cmd: Option<&Command>,
+ usage: Option<&StyledStr>,
+) -> StyledStr {
+ let mut styled = StyledStr::new();
+ start_error(&mut styled);
+ styled.none(message);
+ if let Some(usage) = usage {
+ put_usage(&mut styled, usage);
+ }
+ if let Some(cmd) = cmd {
+ try_help(&mut styled, get_help_flag(cmd));
+ }
+ styled
+}
+
+/// 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"
+ }
+}
+
+fn put_usage(styled: &mut StyledStr, usage: &StyledStr) {
+ styled.none("\n\n");
+ styled.push_styled(usage);
+}
+
+pub(crate) 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(styled: &mut StyledStr, help: Option<&str>) {
+ if let Some(help) = help {
+ styled.none("\n\nFor more information, try '");
+ styled.literal(help.to_owned());
+ styled.none("'.\n");
+ } else {
+ styled.none("\n");
+ }
+}
+
+#[cfg(feature = "error-context")]
+fn did_you_mean(styled: &mut StyledStr, context: &str, valid: &ContextValue) {
+ styled.none(TAB);
+ styled.good("tip:");
+ if let ContextValue::String(valid) = valid {
+ styled.none(" a similar ");
+ styled.none(context);
+ styled.none(" exists: '");
+ styled.good(valid);
+ styled.none("'");
+ } else if let ContextValue::Strings(valid) = valid {
+ if valid.len() == 1 {
+ styled.none(" a similar ");
+ styled.none(context);
+ styled.none(" exists: ");
+ } else {
+ styled.none(" some similar ");
+ styled.none(context);
+ styled.none("s exist: ");
+ }
+ for (i, valid) in valid.iter().enumerate() {
+ if i != 0 {
+ styled.none(", ");
+ }
+ styled.none("'");
+ styled.good(valid);
+ styled.none("'");
+ }
+ }
+}
+
+fn escape(s: impl AsRef<str>) -> String {
+ let s = s.as_ref();
+ if s.contains(char::is_whitespace) {
+ format!("{s:?}")
+ } else {
+ s.to_owned()
+ }
+}
diff --git a/vendor/clap_builder/src/error/kind.rs b/vendor/clap_builder/src/error/kind.rs
new file mode 100644
index 000000000..a9d576c6f
--- /dev/null
+++ b/vendor/clap_builder/src/error/kind.rs
@@ -0,0 +1,366 @@
+/// Command line argument parser kind of error
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[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_builder as clap;
+ /// # use clap::{Command, Arg, error::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_builder as clap;
+ /// # use clap::{Command, arg, error::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
+ ///
+ /// ```rust
+ /// # #[cfg(feature = "suggestions")] {
+ /// # use clap_builder as clap;
+ /// # use clap::{Command, Arg, error::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 doesn't use equals for an option that requires equal
+ /// sign to provide values.
+ ///
+ /// ```rust
+ /// # use clap_builder as clap;
+ /// # use clap::{Command, Arg, error::ErrorKind, ArgAction};
+ /// let res = Command::new("prog")
+ /// .arg(Arg::new("color")
+ /// .action(ArgAction::Set)
+ /// .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_builder as clap;
+ /// # use clap::{Command, Arg, error::ErrorKind, value_parser};
+ /// 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")
+ /// .value_parser(value_parser!(u8)))
+ /// .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::num_args`].
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap_builder as clap;
+ /// # use clap::{Command, Arg, error::ErrorKind};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("arg")
+ /// .num_args(1..=2))
+ /// .try_get_matches_from(vec!["prog", "too", "many", "values"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::TooManyValues);
+ /// ```
+ /// [`Arg::num_args`]: crate::Arg::num_args()
+ TooManyValues,
+
+ /// Occurs when the user provides fewer values for an argument than were defined by setting
+ /// [`Arg::num_args`].
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap_builder as clap;
+ /// # use clap::{Command, Arg, error::ErrorKind};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("some_opt")
+ /// .long("opt")
+ /// .num_args(3..))
+ /// .try_get_matches_from(vec!["prog", "--opt", "too", "few"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::TooFewValues);
+ /// ```
+ /// [`Arg::num_args`]: crate::Arg::num_args()
+ TooFewValues,
+
+ /// Occurs when the user provides a different number of values for an argument than what's
+ /// been defined by setting [`Arg::num_args`] or than was implicitly set by
+ /// [`Arg::value_names`].
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap_builder as clap;
+ /// # use clap::{Command, Arg, error::ErrorKind, ArgAction};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("some_opt")
+ /// .long("opt")
+ /// .action(ArgAction::Set)
+ /// .num_args(2))
+ /// .try_get_matches_from(vec!["prog", "--opt", "wrong"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind(), ErrorKind::WrongNumberOfValues);
+ /// ```
+ ///
+ /// [`Arg::num_args`]: crate::Arg::num_args()
+ /// [`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_builder as clap;
+ /// # use clap::{Command, Arg, error::ErrorKind, ArgAction};
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("debug")
+ /// .long("debug")
+ /// .action(ArgAction::SetTrue)
+ /// .conflicts_with("color"))
+ /// .arg(Arg::new("color")
+ /// .long("color")
+ /// .action(ArgAction::SetTrue))
+ /// .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_builder as clap;
+ /// # use clap::{Command, Arg, error::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_builder as clap;
+ /// # use clap::{Command, error::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 a value containing invalid UTF-8.
+ ///
+ /// To allow arbitrary data
+ /// - Set [`Arg::value_parser(value_parser!(OsString))`] for argument values
+ /// - Set [`Command::external_subcommand_value_parser`] for external-subcommand
+ /// values
+ ///
+ /// # Platform Specific
+ ///
+ /// Non-Windows platforms only (such as Linux, Unix, OSX, etc.)
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # #[cfg(unix)] {
+ /// # use clap_builder as clap;
+ /// # use clap::{Command, Arg, error::ErrorKind, ArgAction};
+ /// # use std::os::unix::ffi::OsStringExt;
+ /// # use std::ffi::OsString;
+ /// let result = Command::new("prog")
+ /// .arg(Arg::new("utf8")
+ /// .short('u')
+ /// .action(ArgAction::Set))
+ /// .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::external_subcommand_value_parser`]: crate::Command::external_subcommand_value_parser
+ 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
+ /// # #[cfg(feature = "help")] {
+ /// # use clap_builder as clap;
+ /// # use clap::{Command, Arg, error::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_builder as clap;
+ /// # use clap::{Command, Arg, error::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_builder as clap;
+ /// # use clap::{Command, Arg, error::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,
+
+ /// 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("unexpected argument found"),
+ Self::InvalidSubcommand => Some("unrecognized subcommand"),
+ 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("unexpected value for an argument found"),
+ Self::TooFewValues => Some("more values required for an argument"),
+ Self::WrongNumberOfValues => Some("too many or too few values for an argument"),
+ 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::InvalidUtf8 => Some("invalid UTF-8 was detected in one or more arguments"),
+ Self::DisplayHelp => None,
+ Self::DisplayHelpOnMissingArgumentOrSubcommand => None,
+ Self::DisplayVersion => None,
+ 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_builder/src/error/mod.rs b/vendor/clap_builder/src/error/mod.rs
new file mode 100644
index 000000000..52f17bd56
--- /dev/null
+++ b/vendor/clap_builder/src/error/mod.rs
@@ -0,0 +1,881 @@
+//! Error reporting
+
+#![cfg_attr(not(feature = "error-context"), allow(dead_code))]
+#![cfg_attr(not(feature = "error-context"), allow(unused_imports))]
+#![cfg_attr(not(feature = "error-context"), allow(unused_variables))]
+#![cfg_attr(not(feature = "error-context"), allow(unused_mut))]
+#![cfg_attr(not(feature = "error-context"), allow(clippy::let_and_return))]
+
+// Std
+use std::{
+ borrow::Cow,
+ convert::From,
+ error,
+ fmt::{self, Debug, Display, Formatter},
+ io::{self},
+ result::Result as StdResult,
+};
+
+// Internal
+use crate::builder::StyledStr;
+use crate::output::fmt::Colorizer;
+use crate::output::fmt::Stream;
+use crate::parser::features::suggestions;
+use crate::util::FlatMap;
+use crate::util::{color::ColorChoice, safe_exit, SUCCESS_CODE, USAGE_CODE};
+use crate::Command;
+
+#[cfg(feature = "error-context")]
+mod context;
+mod format;
+mod kind;
+
+pub use format::ErrorFormatter;
+pub use format::KindFormatter;
+pub use kind::ErrorKind;
+
+#[cfg(feature = "error-context")]
+pub use context::ContextKind;
+#[cfg(feature = "error-context")]
+pub use context::ContextValue;
+#[cfg(feature = "error-context")]
+pub use format::RichFormatter;
+
+#[cfg(not(feature = "error-context"))]
+pub use KindFormatter as DefaultFormatter;
+#[cfg(feature = "error-context")]
+pub use RichFormatter as DefaultFormatter;
+
+/// 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
+pub struct Error<F: ErrorFormatter = DefaultFormatter> {
+ inner: Box<ErrorInner>,
+ phantom: std::marker::PhantomData<F>,
+}
+
+#[derive(Debug)]
+struct ErrorInner {
+ kind: ErrorKind,
+ #[cfg(feature = "error-context")]
+ context: FlatMap<ContextKind, ContextValue>,
+ message: Option<Message>,
+ source: Option<Box<dyn error::Error + Send + Sync>>,
+ help_flag: Option<&'static str>,
+ color_when: ColorChoice,
+ color_help_when: ColorChoice,
+ backtrace: Option<Backtrace>,
+}
+
+impl<F: ErrorFormatter> Error<F> {
+ /// 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(false);
+ let usage = cmd.render_usage_();
+ if let Some(message) = self.inner.message.as_mut() {
+ message.format(cmd, usage);
+ }
+ self.with_cmd(cmd)
+ }
+
+ /// Create an error with a pre-defined message
+ ///
+ /// See also
+ /// - [`Error::insert`]
+ /// - [`Error::with_cmd`]
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # #[cfg(feature = "error-context")] {
+ /// # use clap_builder as clap;
+ /// # use clap::error::ErrorKind;
+ /// # use clap::error::ContextKind;
+ /// # use clap::error::ContextValue;
+ ///
+ /// let cmd = clap::Command::new("prog");
+ ///
+ /// let mut err = clap::Error::new(ErrorKind::ValueValidation)
+ /// .with_cmd(&cmd);
+ /// err.insert(ContextKind::InvalidArg, ContextValue::String("--foo".to_owned()));
+ /// err.insert(ContextKind::InvalidValue, ContextValue::String("bar".to_owned()));
+ ///
+ /// err.print();
+ /// # }
+ /// ```
+ pub fn new(kind: ErrorKind) -> Self {
+ Self {
+ inner: Box::new(ErrorInner {
+ kind,
+ #[cfg(feature = "error-context")]
+ context: FlatMap::new(),
+ message: None,
+ source: None,
+ help_flag: None,
+ color_when: ColorChoice::Never,
+ color_help_when: ColorChoice::Never,
+ backtrace: Backtrace::new(),
+ }),
+ phantom: Default::default(),
+ }
+ }
+
+ /// Apply [`Command`]'s formatting to the error
+ ///
+ /// Generally, this is used with [`Error::new`]
+ pub fn with_cmd(self, cmd: &Command) -> Self {
+ self.set_color(cmd.get_color())
+ .set_colored_help(cmd.color_help())
+ .set_help_flag(format::get_help_flag(cmd))
+ }
+
+ /// Apply an alternative formatter to the error
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use clap_builder as clap;
+ /// # use clap::Command;
+ /// # use clap::Arg;
+ /// # use clap::error::KindFormatter;
+ /// let cmd = Command::new("foo")
+ /// .arg(Arg::new("input").required(true));
+ /// let matches = cmd
+ /// .try_get_matches_from(["foo", "input.txt"])
+ /// .map_err(|e| e.apply::<KindFormatter>())
+ /// .unwrap_or_else(|e| e.exit());
+ /// ```
+ pub fn apply<EF: ErrorFormatter>(self) -> Error<EF> {
+ Error {
+ inner: self.inner,
+ phantom: Default::default(),
+ }
+ }
+
+ /// Type of error for programmatic processing
+ pub fn kind(&self) -> ErrorKind {
+ self.inner.kind
+ }
+
+ /// Additional information to further qualify the error
+ #[cfg(feature = "error-context")]
+ pub fn context(&self) -> impl Iterator<Item = (ContextKind, &ContextValue)> {
+ self.inner.context.iter().map(|(k, v)| (*k, v))
+ }
+
+ /// Lookup a piece of context
+ #[inline(never)]
+ #[cfg(feature = "error-context")]
+ pub fn get(&self, kind: ContextKind) -> Option<&ContextValue> {
+ self.inner.context.get(&kind)
+ }
+
+ /// Insert a piece of context
+ #[inline(never)]
+ #[cfg(feature = "error-context")]
+ pub fn insert(&mut self, kind: ContextKind, value: ContextValue) -> Option<ContextValue> {
+ self.inner.context.insert(kind, value)
+ }
+
+ /// 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();
+
+ 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_builder as clap;
+ /// 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<()> {
+ let style = self.formatted();
+ let color_when = if matches!(
+ self.kind(),
+ ErrorKind::DisplayHelp | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
+ ) {
+ self.inner.color_help_when
+ } else {
+ self.inner.color_when
+ };
+ let c = Colorizer::new(self.stream(), color_when).with_content(style.into_owned());
+ c.print()
+ }
+
+ /// Render the error message to a [`StyledStr`].
+ ///
+ /// # Example
+ /// ```no_run
+ /// # use clap_builder as clap;
+ /// use clap::Command;
+ ///
+ /// match Command::new("Command").try_get_matches() {
+ /// Ok(matches) => {
+ /// // do_something
+ /// },
+ /// Err(err) => {
+ /// let err = err.render();
+ /// println!("{}", err);
+ /// // do_something
+ /// },
+ /// };
+ /// ```
+ pub fn render(&self) -> StyledStr {
+ self.formatted().into_owned()
+ }
+
+ #[inline(never)]
+ fn for_app(kind: ErrorKind, cmd: &Command, styled: StyledStr) -> Self {
+ Self::new(kind).set_message(styled).with_cmd(cmd)
+ }
+
+ pub(crate) fn set_message(mut self, message: impl Into<Message>) -> Self {
+ self.inner.message = Some(message.into());
+ 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_colored_help(mut self, color_help_when: ColorChoice) -> Self {
+ self.inner.color_help_when = color_help_when;
+ self
+ }
+
+ pub(crate) fn set_help_flag(mut self, help_flag: Option<&'static str>) -> Self {
+ self.inner.help_flag = help_flag;
+ self
+ }
+
+ /// Does not verify if `ContextKind` is already present
+ #[inline(never)]
+ #[cfg(feature = "error-context")]
+ pub(crate) fn insert_context_unchecked(
+ mut self,
+ kind: ContextKind,
+ value: ContextValue,
+ ) -> Self {
+ self.inner.context.insert_unchecked(kind, value);
+ self
+ }
+
+ /// Does not verify if `ContextKind` is already present
+ #[inline(never)]
+ #[cfg(feature = "error-context")]
+ pub(crate) fn extend_context_unchecked<const N: usize>(
+ mut self,
+ context: [(ContextKind, ContextValue); N],
+ ) -> Self {
+ self.inner.context.extend_unchecked(context);
+ self
+ }
+
+ pub(crate) fn display_help(cmd: &Command, styled: StyledStr) -> Self {
+ Self::for_app(ErrorKind::DisplayHelp, cmd, styled)
+ }
+
+ pub(crate) fn display_help_error(cmd: &Command, styled: StyledStr) -> Self {
+ Self::for_app(
+ ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
+ cmd,
+ styled,
+ )
+ }
+
+ pub(crate) fn display_version(cmd: &Command, styled: StyledStr) -> Self {
+ Self::for_app(ErrorKind::DisplayVersion, cmd, styled)
+ }
+
+ pub(crate) fn argument_conflict(
+ cmd: &Command,
+ arg: String,
+ mut others: Vec<String>,
+ usage: Option<StyledStr>,
+ ) -> Self {
+ let mut err = Self::new(ErrorKind::ArgumentConflict).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ let others = match others.len() {
+ 0 => ContextValue::None,
+ 1 => ContextValue::String(others.pop().unwrap()),
+ _ => ContextValue::Strings(others),
+ };
+ err = err.extend_context_unchecked([
+ (ContextKind::InvalidArg, ContextValue::String(arg)),
+ (ContextKind::PriorArg, others),
+ ]);
+ if let Some(usage) = usage {
+ err = err
+ .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
+ }
+ }
+
+ err
+ }
+
+ pub(crate) fn empty_value(cmd: &Command, good_vals: &[String], arg: String) -> Self {
+ Self::invalid_value(cmd, "".to_owned(), good_vals, arg)
+ }
+
+ pub(crate) fn no_equals(cmd: &Command, arg: String, usage: Option<StyledStr>) -> Self {
+ let mut err = Self::new(ErrorKind::NoEquals).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ 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));
+ }
+ }
+
+ err
+ }
+
+ pub(crate) fn invalid_value(
+ cmd: &Command,
+ bad_val: String,
+ good_vals: &[String],
+ arg: String,
+ ) -> Self {
+ let suggestion = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop();
+ let mut err = Self::new(ErrorKind::InvalidValue).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ err = err.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: Vec<String>,
+ name: String,
+ usage: Option<StyledStr>,
+ ) -> Self {
+ let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ let mut styled_suggestion = StyledStr::new();
+ styled_suggestion.none("to pass '");
+ styled_suggestion.warning(&subcmd);
+ styled_suggestion.none("' as a value, use '");
+ styled_suggestion.good(name);
+ styled_suggestion.good(" -- ");
+ styled_suggestion.good(&subcmd);
+ styled_suggestion.none("'");
+
+ err = err.extend_context_unchecked([
+ (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)),
+ (
+ ContextKind::SuggestedSubcommand,
+ ContextValue::Strings(did_you_mean),
+ ),
+ (
+ ContextKind::Suggested,
+ ContextValue::StyledStrs(vec![styled_suggestion]),
+ ),
+ ]);
+ if let Some(usage) = usage {
+ err = err
+ .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
+ }
+ }
+
+ err
+ }
+
+ pub(crate) fn unrecognized_subcommand(
+ cmd: &Command,
+ subcmd: String,
+ usage: Option<StyledStr>,
+ ) -> Self {
+ let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ err = err.extend_context_unchecked([(
+ ContextKind::InvalidSubcommand,
+ ContextValue::String(subcmd),
+ )]);
+ if let Some(usage) = usage {
+ err = err
+ .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
+ }
+ }
+
+ err
+ }
+
+ pub(crate) fn missing_required_argument(
+ cmd: &Command,
+ required: Vec<String>,
+ usage: Option<StyledStr>,
+ ) -> Self {
+ let mut err = Self::new(ErrorKind::MissingRequiredArgument).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ err = err.extend_context_unchecked([(
+ ContextKind::InvalidArg,
+ ContextValue::Strings(required),
+ )]);
+ if let Some(usage) = usage {
+ err = err
+ .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
+ }
+ }
+
+ err
+ }
+
+ pub(crate) fn missing_subcommand(
+ cmd: &Command,
+ 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(parent)),
+ (
+ ContextKind::ValidSubcommand,
+ ContextValue::Strings(available),
+ ),
+ ]);
+ if let Some(usage) = usage {
+ err = err
+ .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
+ }
+ }
+
+ err
+ }
+
+ pub(crate) fn invalid_utf8(cmd: &Command, usage: Option<StyledStr>) -> Self {
+ let mut err = Self::new(ErrorKind::InvalidUtf8).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ if let Some(usage) = usage {
+ err = err
+ .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
+ }
+ }
+
+ err
+ }
+
+ pub(crate) fn too_many_values(
+ cmd: &Command,
+ val: String,
+ arg: String,
+ usage: Option<StyledStr>,
+ ) -> Self {
+ let mut err = Self::new(ErrorKind::TooManyValues).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ err = err.extend_context_unchecked([
+ (ContextKind::InvalidArg, ContextValue::String(arg)),
+ (ContextKind::InvalidValue, ContextValue::String(val)),
+ ]);
+ if let Some(usage) = usage {
+ err = err
+ .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
+ }
+ }
+
+ err
+ }
+
+ pub(crate) fn too_few_values(
+ cmd: &Command,
+ arg: String,
+ min_vals: usize,
+ curr_vals: usize,
+ usage: Option<StyledStr>,
+ ) -> Self {
+ let mut err = Self::new(ErrorKind::TooFewValues).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ err = err.extend_context_unchecked([
+ (ContextKind::InvalidArg, ContextValue::String(arg)),
+ (
+ ContextKind::MinValues,
+ ContextValue::Number(min_vals as isize),
+ ),
+ (
+ ContextKind::ActualNumValues,
+ ContextValue::Number(curr_vals as isize),
+ ),
+ ]);
+ if let Some(usage) = usage {
+ err = err
+ .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
+ }
+ }
+
+ err
+ }
+
+ pub(crate) fn value_validation(
+ arg: String,
+ val: String,
+ err: Box<dyn error::Error + Send + Sync>,
+ ) -> Self {
+ let mut err = Self::new(ErrorKind::ValueValidation).set_source(err);
+
+ #[cfg(feature = "error-context")]
+ {
+ err = err.extend_context_unchecked([
+ (ContextKind::InvalidArg, ContextValue::String(arg)),
+ (ContextKind::InvalidValue, ContextValue::String(val)),
+ ]);
+ }
+
+ err
+ }
+
+ pub(crate) fn wrong_number_of_values(
+ cmd: &Command,
+ arg: String,
+ num_vals: usize,
+ curr_vals: usize,
+ usage: Option<StyledStr>,
+ ) -> Self {
+ let mut err = Self::new(ErrorKind::WrongNumberOfValues).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ err = err.extend_context_unchecked([
+ (ContextKind::InvalidArg, ContextValue::String(arg)),
+ (
+ ContextKind::ExpectedNumValues,
+ ContextValue::Number(num_vals as isize),
+ ),
+ (
+ ContextKind::ActualNumValues,
+ ContextValue::Number(curr_vals as isize),
+ ),
+ ]);
+ if let Some(usage) = usage {
+ err = err
+ .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
+ }
+ }
+
+ err
+ }
+
+ pub(crate) fn unknown_argument(
+ cmd: &Command,
+ arg: String,
+ did_you_mean: Option<(String, Option<String>)>,
+ suggested_trailing_arg: bool,
+ usage: Option<StyledStr>,
+ ) -> Self {
+ let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ let mut suggestions = vec![];
+ if suggested_trailing_arg {
+ let mut styled_suggestion = StyledStr::new();
+ styled_suggestion.none("to pass '");
+ styled_suggestion.warning(&arg);
+ styled_suggestion.none("' as a value, 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));
+ }
+ match did_you_mean {
+ Some((flag, Some(sub))) => {
+ let mut styled_suggestion = StyledStr::new();
+ styled_suggestion.none("'");
+ styled_suggestion.good(sub);
+ styled_suggestion.none(" ");
+ styled_suggestion.good("--");
+ styled_suggestion.good(flag);
+ styled_suggestion.none("' exists");
+ suggestions.push(styled_suggestion);
+ }
+ Some((flag, None)) => {
+ err = err.insert_context_unchecked(
+ ContextKind::SuggestedArg,
+ ContextValue::String(format!("--{flag}")),
+ );
+ }
+ None => {}
+ }
+ if !suggestions.is_empty() {
+ err = err.insert_context_unchecked(
+ ContextKind::Suggested,
+ ContextValue::StyledStrs(suggestions),
+ );
+ }
+ }
+
+ err
+ }
+
+ pub(crate) fn unnecessary_double_dash(
+ cmd: &Command,
+ arg: String,
+ usage: Option<StyledStr>,
+ ) -> Self {
+ let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ let mut styled_suggestion = StyledStr::new();
+ styled_suggestion.none("subcommand '");
+ styled_suggestion.good(&arg);
+ styled_suggestion.none("' exists; to use it, remove the '");
+ styled_suggestion.warning("--");
+ styled_suggestion.none("' before it");
+
+ err = err.extend_context_unchecked([
+ (ContextKind::InvalidArg, ContextValue::String(arg)),
+ (
+ ContextKind::Suggested,
+ ContextValue::StyledStrs(vec![styled_suggestion]),
+ ),
+ ]);
+ if let Some(usage) = usage {
+ err = err
+ .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
+ }
+ }
+
+ err
+ }
+
+ fn formatted(&self) -> Cow<'_, StyledStr> {
+ if let Some(message) = self.inner.message.as_ref() {
+ message.formatted()
+ } else {
+ let styled = F::format_error(self);
+ Cow::Owned(styled)
+ }
+ }
+}
+
+impl<F: ErrorFormatter> From<io::Error> for Error<F> {
+ fn from(e: io::Error) -> Self {
+ Error::raw(ErrorKind::Io, e)
+ }
+}
+
+impl<F: ErrorFormatter> From<fmt::Error> for Error<F> {
+ fn from(e: fmt::Error) -> Self {
+ Error::raw(ErrorKind::Format, e)
+ }
+}
+
+impl<F: ErrorFormatter> std::fmt::Debug for Error<F> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ self.inner.fmt(f)
+ }
+}
+
+impl<F: ErrorFormatter> error::Error for Error<F> {
+ #[allow(trivial_casts)]
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ self.inner.source.as_ref().map(|e| e.as_ref() as _)
+ }
+}
+
+impl<F: ErrorFormatter> Display for Error<F> {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ // Assuming `self.message` already has a trailing newline, from `try_help` or similar
+ ok!(write!(f, "{}", self.formatted()));
+ if let Some(backtrace) = self.inner.backtrace.as_ref() {
+ ok!(writeln!(f));
+ ok!(writeln!(f, "Backtrace:"));
+ ok!(writeln!(f, "{backtrace}"));
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) enum Message {
+ Raw(String),
+ Formatted(StyledStr),
+}
+
+impl Message {
+ fn format(&mut self, cmd: &Command, usage: Option<StyledStr>) {
+ match self {
+ Message::Raw(s) => {
+ let mut message = String::new();
+ std::mem::swap(s, &mut message);
+
+ let styled = format::format_error_message(&message, Some(cmd), usage.as_ref());
+
+ *self = Self::Formatted(styled);
+ }
+ Message::Formatted(_) => {}
+ }
+ }
+
+ fn formatted(&self) -> Cow<StyledStr> {
+ match self {
+ Message::Raw(s) => {
+ let styled = format::format_error_message(s, None, None);
+
+ Cow::Owned(styled)
+ }
+ Message::Formatted(s) => Cow::Borrowed(s),
+ }
+ }
+}
+
+impl From<String> for Message {
+ fn from(inner: String) -> Self {
+ Self::Raw(inner)
+ }
+}
+
+impl From<StyledStr> for Message {
+ fn from(inner: StyledStr) -> 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);
+}