clap_builder/error/
mod.rs

1//! Error reporting
2
3#![cfg_attr(not(feature = "error-context"), allow(dead_code))]
4#![cfg_attr(not(feature = "error-context"), allow(unused_imports))]
5#![cfg_attr(not(feature = "error-context"), allow(unused_variables))]
6#![cfg_attr(not(feature = "error-context"), allow(unused_mut))]
7#![cfg_attr(not(feature = "error-context"), allow(clippy::let_and_return))]
8
9// Std
10use std::{
11    borrow::Cow,
12    convert::From,
13    error,
14    fmt::{self, Debug, Display, Formatter},
15    io,
16    result::Result as StdResult,
17};
18
19// Internal
20use crate::builder::StyledStr;
21use crate::builder::Styles;
22use crate::output::fmt::Colorizer;
23use crate::output::fmt::Stream;
24use crate::parser::features::suggestions;
25use crate::util::FlatMap;
26use crate::util::{color::ColorChoice, SUCCESS_CODE, USAGE_CODE};
27use crate::Command;
28
29#[cfg(feature = "error-context")]
30mod context;
31mod format;
32mod kind;
33
34pub use format::ErrorFormatter;
35pub use format::KindFormatter;
36pub use kind::ErrorKind;
37
38#[cfg(feature = "error-context")]
39pub use context::ContextKind;
40#[cfg(feature = "error-context")]
41pub use context::ContextValue;
42#[cfg(feature = "error-context")]
43pub use format::RichFormatter;
44
45#[cfg(not(feature = "error-context"))]
46pub use KindFormatter as DefaultFormatter;
47#[cfg(feature = "error-context")]
48pub use RichFormatter as DefaultFormatter;
49
50/// Short hand for [`Result`] type
51///
52/// [`Result`]: std::result::Result
53pub type Result<T, E = Error> = StdResult<T, E>;
54
55/// Command Line Argument Parser Error
56///
57/// See [`Command::error`] to create an error.
58///
59/// [`Command::error`]: crate::Command::error
60pub struct Error<F: ErrorFormatter = DefaultFormatter> {
61    inner: Box<ErrorInner>,
62    phantom: std::marker::PhantomData<F>,
63}
64
65#[derive(Debug)]
66struct ErrorInner {
67    kind: ErrorKind,
68    #[cfg(feature = "error-context")]
69    context: FlatMap<ContextKind, ContextValue>,
70    message: Option<Message>,
71    source: Option<Box<dyn error::Error + Send + Sync>>,
72    help_flag: Option<Cow<'static, str>>,
73    styles: Styles,
74    color_when: ColorChoice,
75    color_help_when: ColorChoice,
76    backtrace: Option<Backtrace>,
77}
78
79impl<F: ErrorFormatter> Error<F> {
80    /// Create an unformatted error
81    ///
82    /// This is for you need to pass the error up to
83    /// a place that has access to the `Command` at which point you can call [`Error::format`].
84    ///
85    /// Prefer [`Command::error`] for generating errors.
86    ///
87    /// [`Command::error`]: crate::Command::error
88    pub fn raw(kind: ErrorKind, message: impl Display) -> Self {
89        Self::new(kind).set_message(message.to_string())
90    }
91
92    /// Format the existing message with the Command's context
93    #[must_use]
94    pub fn format(mut self, cmd: &mut Command) -> Self {
95        cmd._build_self(false);
96        let usage = cmd.render_usage_();
97        if let Some(message) = self.inner.message.as_mut() {
98            message.format(cmd, usage);
99        }
100        self.with_cmd(cmd)
101    }
102
103    /// Create an error with a pre-defined message
104    ///
105    /// See also
106    /// - [`Error::insert`]
107    /// - [`Error::with_cmd`]
108    ///
109    /// # Example
110    ///
111    /// ```rust
112    /// # #[cfg(feature = "error-context")] {
113    /// # use clap_builder as clap;
114    /// # use clap::error::ErrorKind;
115    /// # use clap::error::ContextKind;
116    /// # use clap::error::ContextValue;
117    ///
118    /// let cmd = clap::Command::new("prog");
119    ///
120    /// let mut err = clap::Error::new(ErrorKind::ValueValidation)
121    ///     .with_cmd(&cmd);
122    /// err.insert(ContextKind::InvalidArg, ContextValue::String("--foo".to_owned()));
123    /// err.insert(ContextKind::InvalidValue, ContextValue::String("bar".to_owned()));
124    ///
125    /// err.print();
126    /// # }
127    /// ```
128    pub fn new(kind: ErrorKind) -> Self {
129        Self {
130            inner: Box::new(ErrorInner {
131                kind,
132                #[cfg(feature = "error-context")]
133                context: FlatMap::new(),
134                message: None,
135                source: None,
136                help_flag: None,
137                styles: Styles::plain(),
138                color_when: ColorChoice::Never,
139                color_help_when: ColorChoice::Never,
140                backtrace: Backtrace::new(),
141            }),
142            phantom: Default::default(),
143        }
144    }
145
146    /// Apply [`Command`]'s formatting to the error
147    ///
148    /// Generally, this is used with [`Error::new`]
149    pub fn with_cmd(self, cmd: &Command) -> Self {
150        self.set_styles(cmd.get_styles().clone())
151            .set_color(cmd.get_color())
152            .set_colored_help(cmd.color_help())
153            .set_help_flag(format::get_help_flag(cmd))
154    }
155
156    /// Apply an alternative formatter to the error
157    ///
158    /// # Example
159    ///
160    /// ```rust
161    /// # use clap_builder as clap;
162    /// # use clap::Command;
163    /// # use clap::Arg;
164    /// # use clap::error::KindFormatter;
165    /// let cmd = Command::new("foo")
166    ///     .arg(Arg::new("input").required(true));
167    /// let matches = cmd
168    ///     .try_get_matches_from(["foo", "input.txt"])
169    ///     .map_err(|e| e.apply::<KindFormatter>())
170    ///     .unwrap_or_else(|e| e.exit());
171    /// ```
172    pub fn apply<EF: ErrorFormatter>(self) -> Error<EF> {
173        Error {
174            inner: self.inner,
175            phantom: Default::default(),
176        }
177    }
178
179    /// Type of error for programmatic processing
180    pub fn kind(&self) -> ErrorKind {
181        self.inner.kind
182    }
183
184    /// Additional information to further qualify the error
185    #[cfg(feature = "error-context")]
186    pub fn context(&self) -> impl Iterator<Item = (ContextKind, &ContextValue)> {
187        self.inner.context.iter().map(|(k, v)| (*k, v))
188    }
189
190    /// Lookup a piece of context
191    #[inline(never)]
192    #[cfg(feature = "error-context")]
193    pub fn get(&self, kind: ContextKind) -> Option<&ContextValue> {
194        self.inner.context.get(&kind)
195    }
196
197    /// Insert a piece of context
198    #[inline(never)]
199    #[cfg(feature = "error-context")]
200    pub fn insert(&mut self, kind: ContextKind, value: ContextValue) -> Option<ContextValue> {
201        self.inner.context.insert(kind, value)
202    }
203
204    /// Should the message be written to `stdout` or not?
205    #[inline]
206    pub fn use_stderr(&self) -> bool {
207        self.stream() == Stream::Stderr
208    }
209
210    pub(crate) fn stream(&self) -> Stream {
211        match self.kind() {
212            ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => Stream::Stdout,
213            _ => Stream::Stderr,
214        }
215    }
216
217    /// Returns the exit code that `.exit` will exit the process with.
218    ///
219    /// When the error's kind would print to `stderr` this returns `2`,
220    /// else it returns `0`.
221    pub fn exit_code(&self) -> i32 {
222        if self.use_stderr() {
223            USAGE_CODE
224        } else {
225            SUCCESS_CODE
226        }
227    }
228
229    /// Prints the error and exits.
230    ///
231    /// Depending on the error kind, this either prints to `stderr` and exits with a status of `2`
232    /// or prints to `stdout` and exits with a status of `0`.
233    pub fn exit(&self) -> ! {
234        // Swallow broken pipe errors
235        let _ = self.print();
236        std::process::exit(self.exit_code());
237    }
238
239    /// Prints formatted and colored error to `stdout` or `stderr` according to its error kind
240    ///
241    /// # Example
242    /// ```no_run
243    /// # use clap_builder as clap;
244    /// use clap::Command;
245    ///
246    /// match Command::new("Command").try_get_matches() {
247    ///     Ok(matches) => {
248    ///         // do_something
249    ///     },
250    ///     Err(err) => {
251    ///         err.print().expect("Error writing Error");
252    ///         // do_something
253    ///     },
254    /// };
255    /// ```
256    pub fn print(&self) -> io::Result<()> {
257        let style = self.formatted();
258        let color_when = if matches!(
259            self.kind(),
260            ErrorKind::DisplayHelp | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
261        ) {
262            self.inner.color_help_when
263        } else {
264            self.inner.color_when
265        };
266        let c = Colorizer::new(self.stream(), color_when).with_content(style.into_owned());
267        c.print()
268    }
269
270    /// Render the error message to a [`StyledStr`].
271    ///
272    /// # Example
273    /// ```no_run
274    /// # use clap_builder as clap;
275    /// use clap::Command;
276    ///
277    /// match Command::new("Command").try_get_matches() {
278    ///     Ok(matches) => {
279    ///         // do_something
280    ///     },
281    ///     Err(err) => {
282    ///         let err = err.render();
283    ///         println!("{err}");
284    ///         // do_something
285    ///     },
286    /// };
287    /// ```
288    pub fn render(&self) -> StyledStr {
289        self.formatted().into_owned()
290    }
291
292    #[inline(never)]
293    fn for_app(kind: ErrorKind, cmd: &Command, styled: StyledStr) -> Self {
294        Self::new(kind).set_message(styled).with_cmd(cmd)
295    }
296
297    pub(crate) fn set_message(mut self, message: impl Into<Message>) -> Self {
298        self.inner.message = Some(message.into());
299        self
300    }
301
302    pub(crate) fn set_source(mut self, source: Box<dyn error::Error + Send + Sync>) -> Self {
303        self.inner.source = Some(source);
304        self
305    }
306
307    pub(crate) fn set_styles(mut self, styles: Styles) -> Self {
308        self.inner.styles = styles;
309        self
310    }
311
312    pub(crate) fn set_color(mut self, color_when: ColorChoice) -> Self {
313        self.inner.color_when = color_when;
314        self
315    }
316
317    pub(crate) fn set_colored_help(mut self, color_help_when: ColorChoice) -> Self {
318        self.inner.color_help_when = color_help_when;
319        self
320    }
321
322    pub(crate) fn set_help_flag(mut self, help_flag: Option<Cow<'static, str>>) -> Self {
323        self.inner.help_flag = help_flag;
324        self
325    }
326
327    /// Does not verify if `ContextKind` is already present
328    #[inline(never)]
329    #[cfg(feature = "error-context")]
330    pub(crate) fn insert_context_unchecked(
331        mut self,
332        kind: ContextKind,
333        value: ContextValue,
334    ) -> Self {
335        self.inner.context.insert_unchecked(kind, value);
336        self
337    }
338
339    /// Does not verify if `ContextKind` is already present
340    #[inline(never)]
341    #[cfg(feature = "error-context")]
342    pub(crate) fn extend_context_unchecked<const N: usize>(
343        mut self,
344        context: [(ContextKind, ContextValue); N],
345    ) -> Self {
346        self.inner.context.extend_unchecked(context);
347        self
348    }
349
350    pub(crate) fn display_help(cmd: &Command, styled: StyledStr) -> Self {
351        Self::for_app(ErrorKind::DisplayHelp, cmd, styled)
352    }
353
354    pub(crate) fn display_help_error(cmd: &Command, styled: StyledStr) -> Self {
355        Self::for_app(
356            ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
357            cmd,
358            styled,
359        )
360    }
361
362    pub(crate) fn display_version(cmd: &Command, styled: StyledStr) -> Self {
363        Self::for_app(ErrorKind::DisplayVersion, cmd, styled)
364    }
365
366    pub(crate) fn argument_conflict(
367        cmd: &Command,
368        arg: String,
369        mut others: Vec<String>,
370        usage: Option<StyledStr>,
371    ) -> Self {
372        let mut err = Self::new(ErrorKind::ArgumentConflict).with_cmd(cmd);
373
374        #[cfg(feature = "error-context")]
375        {
376            let others = match others.len() {
377                0 => ContextValue::None,
378                1 => ContextValue::String(others.pop().unwrap()),
379                _ => ContextValue::Strings(others),
380            };
381            err = err.extend_context_unchecked([
382                (ContextKind::InvalidArg, ContextValue::String(arg)),
383                (ContextKind::PriorArg, others),
384            ]);
385            if let Some(usage) = usage {
386                err = err
387                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
388            }
389        }
390
391        err
392    }
393
394    pub(crate) fn subcommand_conflict(
395        cmd: &Command,
396        sub: String,
397        mut others: Vec<String>,
398        usage: Option<StyledStr>,
399    ) -> Self {
400        let mut err = Self::new(ErrorKind::ArgumentConflict).with_cmd(cmd);
401
402        #[cfg(feature = "error-context")]
403        {
404            let others = match others.len() {
405                0 => ContextValue::None,
406                1 => ContextValue::String(others.pop().unwrap()),
407                _ => ContextValue::Strings(others),
408            };
409            err = err.extend_context_unchecked([
410                (ContextKind::InvalidSubcommand, ContextValue::String(sub)),
411                (ContextKind::PriorArg, others),
412            ]);
413            if let Some(usage) = usage {
414                err = err
415                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
416            }
417        }
418
419        err
420    }
421
422    pub(crate) fn empty_value(cmd: &Command, good_vals: &[String], arg: String) -> Self {
423        Self::invalid_value(cmd, "".to_owned(), good_vals, arg)
424    }
425
426    pub(crate) fn no_equals(cmd: &Command, arg: String, usage: Option<StyledStr>) -> Self {
427        let mut err = Self::new(ErrorKind::NoEquals).with_cmd(cmd);
428
429        #[cfg(feature = "error-context")]
430        {
431            err = err
432                .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
433            if let Some(usage) = usage {
434                err = err
435                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
436            }
437        }
438
439        err
440    }
441
442    pub(crate) fn invalid_value(
443        cmd: &Command,
444        bad_val: String,
445        good_vals: &[String],
446        arg: String,
447    ) -> Self {
448        let suggestion = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop();
449        let mut err = Self::new(ErrorKind::InvalidValue).with_cmd(cmd);
450
451        #[cfg(feature = "error-context")]
452        {
453            err = err.extend_context_unchecked([
454                (ContextKind::InvalidArg, ContextValue::String(arg)),
455                (ContextKind::InvalidValue, ContextValue::String(bad_val)),
456                (
457                    ContextKind::ValidValue,
458                    ContextValue::Strings(good_vals.iter().map(|s| (*s).clone()).collect()),
459                ),
460            ]);
461            if let Some(suggestion) = suggestion {
462                err = err.insert_context_unchecked(
463                    ContextKind::SuggestedValue,
464                    ContextValue::String(suggestion),
465                );
466            }
467        }
468
469        err
470    }
471
472    pub(crate) fn invalid_subcommand(
473        cmd: &Command,
474        subcmd: String,
475        did_you_mean: Vec<String>,
476        name: String,
477        suggested_trailing_arg: bool,
478        usage: Option<StyledStr>,
479    ) -> Self {
480        use std::fmt::Write as _;
481        let styles = cmd.get_styles();
482        let invalid = &styles.get_invalid();
483        let valid = &styles.get_valid();
484        let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
485
486        #[cfg(feature = "error-context")]
487        {
488            let mut suggestions = vec![];
489            if suggested_trailing_arg {
490                let mut styled_suggestion = StyledStr::new();
491                let _ = write!(
492                    styled_suggestion,
493                    "to pass '{invalid}{subcmd}{invalid:#}' as a value, use '{valid}{name} -- {subcmd}{valid:#}'",
494                );
495                suggestions.push(styled_suggestion);
496            }
497
498            err = err.extend_context_unchecked([
499                (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)),
500                (
501                    ContextKind::SuggestedSubcommand,
502                    ContextValue::Strings(did_you_mean),
503                ),
504                (
505                    ContextKind::Suggested,
506                    ContextValue::StyledStrs(suggestions),
507                ),
508            ]);
509            if let Some(usage) = usage {
510                err = err
511                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
512            }
513        }
514
515        err
516    }
517
518    pub(crate) fn unrecognized_subcommand(
519        cmd: &Command,
520        subcmd: String,
521        usage: Option<StyledStr>,
522    ) -> Self {
523        let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
524
525        #[cfg(feature = "error-context")]
526        {
527            err = err.extend_context_unchecked([(
528                ContextKind::InvalidSubcommand,
529                ContextValue::String(subcmd),
530            )]);
531            if let Some(usage) = usage {
532                err = err
533                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
534            }
535        }
536
537        err
538    }
539
540    pub(crate) fn missing_required_argument(
541        cmd: &Command,
542        required: Vec<String>,
543        usage: Option<StyledStr>,
544    ) -> Self {
545        let mut err = Self::new(ErrorKind::MissingRequiredArgument).with_cmd(cmd);
546
547        #[cfg(feature = "error-context")]
548        {
549            err = err.extend_context_unchecked([(
550                ContextKind::InvalidArg,
551                ContextValue::Strings(required),
552            )]);
553            if let Some(usage) = usage {
554                err = err
555                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
556            }
557        }
558
559        err
560    }
561
562    pub(crate) fn missing_subcommand(
563        cmd: &Command,
564        parent: String,
565        available: Vec<String>,
566        usage: Option<StyledStr>,
567    ) -> Self {
568        let mut err = Self::new(ErrorKind::MissingSubcommand).with_cmd(cmd);
569
570        #[cfg(feature = "error-context")]
571        {
572            err = err.extend_context_unchecked([
573                (ContextKind::InvalidSubcommand, ContextValue::String(parent)),
574                (
575                    ContextKind::ValidSubcommand,
576                    ContextValue::Strings(available),
577                ),
578            ]);
579            if let Some(usage) = usage {
580                err = err
581                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
582            }
583        }
584
585        err
586    }
587
588    pub(crate) fn invalid_utf8(cmd: &Command, usage: Option<StyledStr>) -> Self {
589        let mut err = Self::new(ErrorKind::InvalidUtf8).with_cmd(cmd);
590
591        #[cfg(feature = "error-context")]
592        {
593            if let Some(usage) = usage {
594                err = err
595                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
596            }
597        }
598
599        err
600    }
601
602    pub(crate) fn too_many_values(
603        cmd: &Command,
604        val: String,
605        arg: String,
606        usage: Option<StyledStr>,
607    ) -> Self {
608        let mut err = Self::new(ErrorKind::TooManyValues).with_cmd(cmd);
609
610        #[cfg(feature = "error-context")]
611        {
612            err = err.extend_context_unchecked([
613                (ContextKind::InvalidArg, ContextValue::String(arg)),
614                (ContextKind::InvalidValue, ContextValue::String(val)),
615            ]);
616            if let Some(usage) = usage {
617                err = err
618                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
619            }
620        }
621
622        err
623    }
624
625    pub(crate) fn too_few_values(
626        cmd: &Command,
627        arg: String,
628        min_vals: usize,
629        curr_vals: usize,
630        usage: Option<StyledStr>,
631    ) -> Self {
632        let mut err = Self::new(ErrorKind::TooFewValues).with_cmd(cmd);
633
634        #[cfg(feature = "error-context")]
635        {
636            err = err.extend_context_unchecked([
637                (ContextKind::InvalidArg, ContextValue::String(arg)),
638                (
639                    ContextKind::MinValues,
640                    ContextValue::Number(min_vals as isize),
641                ),
642                (
643                    ContextKind::ActualNumValues,
644                    ContextValue::Number(curr_vals as isize),
645                ),
646            ]);
647            if let Some(usage) = usage {
648                err = err
649                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
650            }
651        }
652
653        err
654    }
655
656    pub(crate) fn value_validation(
657        arg: String,
658        val: String,
659        err: Box<dyn error::Error + Send + Sync>,
660    ) -> Self {
661        let mut err = Self::new(ErrorKind::ValueValidation).set_source(err);
662
663        #[cfg(feature = "error-context")]
664        {
665            err = err.extend_context_unchecked([
666                (ContextKind::InvalidArg, ContextValue::String(arg)),
667                (ContextKind::InvalidValue, ContextValue::String(val)),
668            ]);
669        }
670
671        err
672    }
673
674    pub(crate) fn wrong_number_of_values(
675        cmd: &Command,
676        arg: String,
677        num_vals: usize,
678        curr_vals: usize,
679        usage: Option<StyledStr>,
680    ) -> Self {
681        let mut err = Self::new(ErrorKind::WrongNumberOfValues).with_cmd(cmd);
682
683        #[cfg(feature = "error-context")]
684        {
685            err = err.extend_context_unchecked([
686                (ContextKind::InvalidArg, ContextValue::String(arg)),
687                (
688                    ContextKind::ExpectedNumValues,
689                    ContextValue::Number(num_vals as isize),
690                ),
691                (
692                    ContextKind::ActualNumValues,
693                    ContextValue::Number(curr_vals as isize),
694                ),
695            ]);
696            if let Some(usage) = usage {
697                err = err
698                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
699            }
700        }
701
702        err
703    }
704
705    pub(crate) fn unknown_argument(
706        cmd: &Command,
707        arg: String,
708        did_you_mean: Option<(String, Option<String>)>,
709        suggested_trailing_arg: bool,
710        usage: Option<StyledStr>,
711    ) -> Self {
712        use std::fmt::Write as _;
713        let styles = cmd.get_styles();
714        let invalid = &styles.get_invalid();
715        let valid = &styles.get_valid();
716        let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
717
718        #[cfg(feature = "error-context")]
719        {
720            let mut suggestions = vec![];
721            if suggested_trailing_arg {
722                let mut styled_suggestion = StyledStr::new();
723                let _ = write!(
724                    styled_suggestion,
725                    "to pass '{invalid}{arg}{invalid:#}' as a value, use '{valid}-- {arg}{valid:#}'",
726                );
727                suggestions.push(styled_suggestion);
728            }
729
730            err = err
731                .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
732            if let Some(usage) = usage {
733                err = err
734                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
735            }
736            match did_you_mean {
737                Some((flag, Some(sub))) => {
738                    let mut styled_suggestion = StyledStr::new();
739                    let _ = write!(styled_suggestion, "'{valid}{sub} {flag}{valid:#}' exists",);
740                    suggestions.push(styled_suggestion);
741                }
742                Some((flag, None)) => {
743                    err = err.insert_context_unchecked(
744                        ContextKind::SuggestedArg,
745                        ContextValue::String(flag),
746                    );
747                }
748                None => {}
749            }
750            if !suggestions.is_empty() {
751                err = err.insert_context_unchecked(
752                    ContextKind::Suggested,
753                    ContextValue::StyledStrs(suggestions),
754                );
755            }
756        }
757
758        err
759    }
760
761    pub(crate) fn unnecessary_double_dash(
762        cmd: &Command,
763        arg: String,
764        usage: Option<StyledStr>,
765    ) -> Self {
766        use std::fmt::Write as _;
767        let styles = cmd.get_styles();
768        let invalid = &styles.get_invalid();
769        let valid = &styles.get_valid();
770        let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
771
772        #[cfg(feature = "error-context")]
773        {
774            let mut styled_suggestion = StyledStr::new();
775            let _ = write!(
776                styled_suggestion,
777                "subcommand '{valid}{arg}{valid:#}' exists; to use it, remove the '{invalid}--{invalid:#}' before it",
778            );
779
780            err = err.extend_context_unchecked([
781                (ContextKind::InvalidArg, ContextValue::String(arg)),
782                (
783                    ContextKind::Suggested,
784                    ContextValue::StyledStrs(vec![styled_suggestion]),
785                ),
786            ]);
787            if let Some(usage) = usage {
788                err = err
789                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
790            }
791        }
792
793        err
794    }
795
796    fn formatted(&self) -> Cow<'_, StyledStr> {
797        if let Some(message) = self.inner.message.as_ref() {
798            message.formatted(&self.inner.styles)
799        } else {
800            let styled = F::format_error(self);
801            Cow::Owned(styled)
802        }
803    }
804}
805
806impl<F: ErrorFormatter> From<io::Error> for Error<F> {
807    fn from(e: io::Error) -> Self {
808        Error::raw(ErrorKind::Io, e)
809    }
810}
811
812impl<F: ErrorFormatter> From<fmt::Error> for Error<F> {
813    fn from(e: fmt::Error) -> Self {
814        Error::raw(ErrorKind::Format, e)
815    }
816}
817
818impl<F: ErrorFormatter> Debug for Error<F> {
819    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
820        self.inner.fmt(f)
821    }
822}
823
824impl<F: ErrorFormatter> error::Error for Error<F> {
825    #[allow(trivial_casts)]
826    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
827        self.inner.source.as_ref().map(|e| e.as_ref() as _)
828    }
829}
830
831impl<F: ErrorFormatter> Display for Error<F> {
832    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
833        // Assuming `self.message` already has a trailing newline, from `try_help` or similar
834        ok!(write!(f, "{}", self.formatted()));
835        if let Some(backtrace) = self.inner.backtrace.as_ref() {
836            ok!(writeln!(f));
837            ok!(writeln!(f, "Backtrace:"));
838            ok!(writeln!(f, "{backtrace}"));
839        }
840        Ok(())
841    }
842}
843
844#[derive(Clone, Debug)]
845pub(crate) enum Message {
846    Raw(String),
847    Formatted(StyledStr),
848}
849
850impl Message {
851    fn format(&mut self, cmd: &Command, usage: Option<StyledStr>) {
852        match self {
853            Message::Raw(s) => {
854                let mut message = String::new();
855                std::mem::swap(s, &mut message);
856
857                let styled = format::format_error_message(
858                    &message,
859                    cmd.get_styles(),
860                    Some(cmd),
861                    usage.as_ref(),
862                );
863
864                *self = Self::Formatted(styled);
865            }
866            Message::Formatted(_) => {}
867        }
868    }
869
870    fn formatted(&self, styles: &Styles) -> Cow<'_, StyledStr> {
871        match self {
872            Message::Raw(s) => {
873                let styled = format::format_error_message(s, styles, None, None);
874
875                Cow::Owned(styled)
876            }
877            Message::Formatted(s) => Cow::Borrowed(s),
878        }
879    }
880}
881
882impl From<String> for Message {
883    fn from(inner: String) -> Self {
884        Self::Raw(inner)
885    }
886}
887
888impl From<StyledStr> for Message {
889    fn from(inner: StyledStr) -> Self {
890        Self::Formatted(inner)
891    }
892}
893
894#[cfg(feature = "debug")]
895#[derive(Debug)]
896struct Backtrace(backtrace::Backtrace);
897
898#[cfg(feature = "debug")]
899impl Backtrace {
900    fn new() -> Option<Self> {
901        Some(Self(backtrace::Backtrace::new()))
902    }
903}
904
905#[cfg(feature = "debug")]
906impl Display for Backtrace {
907    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
908        // `backtrace::Backtrace` uses `Debug` instead of `Display`
909        write!(f, "{:?}", self.0)
910    }
911}
912
913#[cfg(not(feature = "debug"))]
914#[derive(Debug)]
915struct Backtrace;
916
917#[cfg(not(feature = "debug"))]
918impl Backtrace {
919    fn new() -> Option<Self> {
920        None
921    }
922}
923
924#[cfg(not(feature = "debug"))]
925impl Display for Backtrace {
926    fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result {
927        Ok(())
928    }
929}
930
931#[test]
932fn check_auto_traits() {
933    static_assertions::assert_impl_all!(Error: Send, Sync, Unpin);
934}