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