summaryrefslogtreecommitdiffstats
path: root/third_party/rust/clap_builder/src/error/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/rust/clap_builder/src/error/mod.rs923
1 files changed, 923 insertions, 0 deletions
diff --git a/third_party/rust/clap_builder/src/error/mod.rs b/third_party/rust/clap_builder/src/error/mod.rs
new file mode 100644
index 0000000000..af90e2756f
--- /dev/null
+++ b/third_party/rust/clap_builder/src/error/mod.rs
@@ -0,0 +1,923 @@
+//! 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::builder::Styles;
+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>,
+ styles: Styles,
+ 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,
+ styles: Styles::plain(),
+ 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_styles(cmd.get_styles().clone())
+ .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,
+ }
+ }
+
+ /// Returns the exit code that `.exit` will exit the process with.
+ ///
+ /// When the error's kind would print to `stderr` this returns `2`,
+ /// else it returns `0`.
+ pub fn exit_code(&self) -> i32 {
+ if self.use_stderr() {
+ USAGE_CODE
+ } else {
+ SUCCESS_CODE
+ }
+ }
+
+ /// 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) -> ! {
+ // Swallow broken pipe errors
+ let _ = self.print();
+ safe_exit(self.exit_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_styles(mut self, styles: Styles) -> Self {
+ self.inner.styles = styles;
+ 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,
+ suggested_trailing_arg: bool,
+ usage: Option<StyledStr>,
+ ) -> Self {
+ use std::fmt::Write as _;
+ let styles = cmd.get_styles();
+ let invalid = &styles.get_invalid();
+ let valid = &styles.get_valid();
+ let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ let mut suggestions = vec![];
+ if suggested_trailing_arg {
+ let mut styled_suggestion = StyledStr::new();
+ let _ = write!(
+ styled_suggestion,
+ "to pass '{}{subcmd}{}' as a value, use '{}{name} -- {subcmd}{}'",
+ invalid.render(),
+ invalid.render_reset(),
+ valid.render(),
+ valid.render_reset()
+ );
+ suggestions.push(styled_suggestion);
+ }
+
+ err = err.extend_context_unchecked([
+ (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)),
+ (
+ ContextKind::SuggestedSubcommand,
+ ContextValue::Strings(did_you_mean),
+ ),
+ (
+ ContextKind::Suggested,
+ ContextValue::StyledStrs(suggestions),
+ ),
+ ]);
+ 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 {
+ use std::fmt::Write as _;
+ let styles = cmd.get_styles();
+ let invalid = &styles.get_invalid();
+ let valid = &styles.get_valid();
+ 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();
+ let _ = write!(
+ styled_suggestion,
+ "to pass '{}{arg}{}' as a value, use '{}-- {arg}{}'",
+ invalid.render(),
+ invalid.render_reset(),
+ valid.render(),
+ valid.render_reset()
+ );
+ 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();
+ let _ = write!(
+ styled_suggestion,
+ "'{}{sub} {flag}{}' exists",
+ valid.render(),
+ valid.render_reset()
+ );
+ suggestions.push(styled_suggestion);
+ }
+ Some((flag, None)) => {
+ err = err.insert_context_unchecked(
+ ContextKind::SuggestedArg,
+ ContextValue::String(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 {
+ use std::fmt::Write as _;
+ let styles = cmd.get_styles();
+ let invalid = &styles.get_invalid();
+ let valid = &styles.get_valid();
+ let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
+
+ #[cfg(feature = "error-context")]
+ {
+ let mut styled_suggestion = StyledStr::new();
+ let _ = write!(
+ styled_suggestion,
+ "subcommand '{}{arg}{}' exists; to use it, remove the '{}--{}' before it",
+ valid.render(),
+ valid.render_reset(),
+ invalid.render(),
+ invalid.render_reset()
+ );
+
+ 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(&self.inner.styles)
+ } 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,
+ cmd.get_styles(),
+ Some(cmd),
+ usage.as_ref(),
+ );
+
+ *self = Self::Formatted(styled);
+ }
+ Message::Formatted(_) => {}
+ }
+ }
+
+ fn formatted(&self, styles: &Styles) -> Cow<StyledStr> {
+ match self {
+ Message::Raw(s) => {
+ let styled = format::format_error_message(s, styles, 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);
+}