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    ///
199    /// If this `ContextKind` is already present, its value is replaced and the old value is returned.
200    #[inline(never)]
201    #[cfg(feature = "error-context")]
202    pub fn insert(&mut self, kind: ContextKind, value: ContextValue) -> Option<ContextValue> {
203        self.inner.context.insert(kind, value)
204    }
205
206    /// Remove a piece of context, return the old value if any
207    ///
208    /// The context is currently implemented in a vector, so `remove` takes
209    /// linear time.
210    #[inline(never)]
211    #[cfg(feature = "error-context")]
212    pub fn remove(&mut self, kind: ContextKind) -> Option<ContextValue> {
213        self.inner.context.remove(&kind)
214    }
215
216    /// Should the message be written to `stdout` or not?
217    #[inline]
218    pub fn use_stderr(&self) -> bool {
219        self.stream() == Stream::Stderr
220    }
221
222    pub(crate) fn stream(&self) -> Stream {
223        match self.kind() {
224            ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => Stream::Stdout,
225            _ => Stream::Stderr,
226        }
227    }
228
229    /// Returns the exit code that `.exit` will exit the process with.
230    ///
231    /// When the error's kind would print to `stderr` this returns `2`,
232    /// else it returns `0`.
233    pub fn exit_code(&self) -> i32 {
234        if self.use_stderr() {
235            USAGE_CODE
236        } else {
237            SUCCESS_CODE
238        }
239    }
240
241    /// Prints the error and exits.
242    ///
243    /// Depending on the error kind, this either prints to `stderr` and exits with a status of `2`
244    /// or prints to `stdout` and exits with a status of `0`.
245    pub fn exit(&self) -> ! {
246        // Swallow broken pipe errors
247        let _ = self.print();
248        std::process::exit(self.exit_code());
249    }
250
251    /// Prints formatted and colored error to `stdout` or `stderr` according to its error kind
252    ///
253    /// # Example
254    /// ```no_run
255    /// # use clap_builder as clap;
256    /// use clap::Command;
257    ///
258    /// match Command::new("Command").try_get_matches() {
259    ///     Ok(matches) => {
260    ///         // do_something
261    ///     },
262    ///     Err(err) => {
263    ///         err.print().expect("Error writing Error");
264    ///         // do_something
265    ///     },
266    /// };
267    /// ```
268    pub fn print(&self) -> io::Result<()> {
269        let style = self.formatted();
270        let color_when = if matches!(
271            self.kind(),
272            ErrorKind::DisplayHelp | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
273        ) {
274            self.inner.color_help_when
275        } else {
276            self.inner.color_when
277        };
278        let c = Colorizer::new(self.stream(), color_when).with_content(style.into_owned());
279        c.print()
280    }
281
282    /// Render the error message to a [`StyledStr`].
283    ///
284    /// # Example
285    /// ```no_run
286    /// # use clap_builder as clap;
287    /// use clap::Command;
288    ///
289    /// match Command::new("Command").try_get_matches() {
290    ///     Ok(matches) => {
291    ///         // do_something
292    ///     },
293    ///     Err(err) => {
294    ///         let err = err.render();
295    ///         println!("{err}");
296    ///         // do_something
297    ///     },
298    /// };
299    /// ```
300    pub fn render(&self) -> StyledStr {
301        self.formatted().into_owned()
302    }
303
304    #[inline(never)]
305    fn for_app(kind: ErrorKind, cmd: &Command, styled: StyledStr) -> Self {
306        Self::new(kind).set_message(styled).with_cmd(cmd)
307    }
308
309    pub(crate) fn set_message(mut self, message: impl Into<Message>) -> Self {
310        self.inner.message = Some(message.into());
311        self
312    }
313
314    pub(crate) fn set_source(mut self, source: Box<dyn error::Error + Send + Sync>) -> Self {
315        self.inner.source = Some(source);
316        self
317    }
318
319    pub(crate) fn set_styles(mut self, styles: Styles) -> Self {
320        self.inner.styles = styles;
321        self
322    }
323
324    pub(crate) fn set_color(mut self, color_when: ColorChoice) -> Self {
325        self.inner.color_when = color_when;
326        self
327    }
328
329    pub(crate) fn set_colored_help(mut self, color_help_when: ColorChoice) -> Self {
330        self.inner.color_help_when = color_help_when;
331        self
332    }
333
334    pub(crate) fn set_help_flag(mut self, help_flag: Option<Cow<'static, str>>) -> Self {
335        self.inner.help_flag = help_flag;
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 insert_context_unchecked(
343        mut self,
344        kind: ContextKind,
345        value: ContextValue,
346    ) -> Self {
347        self.inner.context.insert_unchecked(kind, value);
348        self
349    }
350
351    /// Does not verify if `ContextKind` is already present
352    #[inline(never)]
353    #[cfg(feature = "error-context")]
354    pub(crate) fn extend_context_unchecked<const N: usize>(
355        mut self,
356        context: [(ContextKind, ContextValue); N],
357    ) -> Self {
358        self.inner.context.extend_unchecked(context);
359        self
360    }
361
362    pub(crate) fn display_help(cmd: &Command, styled: StyledStr) -> Self {
363        Self::for_app(ErrorKind::DisplayHelp, cmd, styled)
364    }
365
366    pub(crate) fn display_help_error(cmd: &Command, styled: StyledStr) -> Self {
367        Self::for_app(
368            ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
369            cmd,
370            styled,
371        )
372    }
373
374    pub(crate) fn display_version(cmd: &Command, styled: StyledStr) -> Self {
375        Self::for_app(ErrorKind::DisplayVersion, cmd, styled)
376    }
377
378    pub(crate) fn argument_conflict(
379        cmd: &Command,
380        arg: String,
381        mut others: Vec<String>,
382        usage: Option<StyledStr>,
383    ) -> Self {
384        let mut err = Self::new(ErrorKind::ArgumentConflict).with_cmd(cmd);
385
386        #[cfg(feature = "error-context")]
387        {
388            let others = match others.len() {
389                0 => ContextValue::None,
390                1 => ContextValue::String(others.pop().unwrap()),
391                _ => ContextValue::Strings(others),
392            };
393            err = err.extend_context_unchecked([
394                (ContextKind::InvalidArg, ContextValue::String(arg)),
395                (ContextKind::PriorArg, others),
396            ]);
397            if let Some(usage) = usage {
398                err = err
399                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
400            }
401        }
402
403        err
404    }
405
406    pub(crate) fn subcommand_conflict(
407        cmd: &Command,
408        sub: String,
409        mut others: Vec<String>,
410        usage: Option<StyledStr>,
411    ) -> Self {
412        let mut err = Self::new(ErrorKind::ArgumentConflict).with_cmd(cmd);
413
414        #[cfg(feature = "error-context")]
415        {
416            let others = match others.len() {
417                0 => ContextValue::None,
418                1 => ContextValue::String(others.pop().unwrap()),
419                _ => ContextValue::Strings(others),
420            };
421            err = err.extend_context_unchecked([
422                (ContextKind::InvalidSubcommand, ContextValue::String(sub)),
423                (ContextKind::PriorArg, others),
424            ]);
425            if let Some(usage) = usage {
426                err = err
427                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
428            }
429        }
430
431        err
432    }
433
434    pub(crate) fn empty_value(cmd: &Command, good_vals: &[String], arg: String) -> Self {
435        Self::invalid_value(cmd, "".to_owned(), good_vals, arg)
436    }
437
438    pub(crate) fn no_equals(cmd: &Command, arg: String, usage: Option<StyledStr>) -> Self {
439        let mut err = Self::new(ErrorKind::NoEquals).with_cmd(cmd);
440
441        #[cfg(feature = "error-context")]
442        {
443            err = err
444                .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
445            if let Some(usage) = usage {
446                err = err
447                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
448            }
449        }
450
451        err
452    }
453
454    pub(crate) fn invalid_value(
455        cmd: &Command,
456        bad_val: String,
457        good_vals: &[String],
458        arg: String,
459    ) -> Self {
460        let suggestion = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop();
461        let mut err = Self::new(ErrorKind::InvalidValue).with_cmd(cmd);
462
463        #[cfg(feature = "error-context")]
464        {
465            err = err.extend_context_unchecked([
466                (ContextKind::InvalidArg, ContextValue::String(arg)),
467                (ContextKind::InvalidValue, ContextValue::String(bad_val)),
468                (
469                    ContextKind::ValidValue,
470                    ContextValue::Strings(good_vals.iter().map(|s| (*s).clone()).collect()),
471                ),
472            ]);
473            if let Some(suggestion) = suggestion {
474                err = err.insert_context_unchecked(
475                    ContextKind::SuggestedValue,
476                    ContextValue::String(suggestion),
477                );
478            }
479        }
480
481        err
482    }
483
484    pub(crate) fn invalid_subcommand(
485        cmd: &Command,
486        subcmd: String,
487        did_you_mean: Vec<String>,
488        name: String,
489        suggested_trailing_arg: bool,
490        usage: Option<StyledStr>,
491    ) -> Self {
492        use std::fmt::Write as _;
493        let styles = cmd.get_styles();
494        let invalid = &styles.get_invalid();
495        let valid = &styles.get_valid();
496        let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
497
498        #[cfg(feature = "error-context")]
499        {
500            let mut suggestions = vec![];
501            if suggested_trailing_arg {
502                let mut styled_suggestion = StyledStr::new();
503                let _ = write!(
504                    styled_suggestion,
505                    "to pass '{invalid}{subcmd}{invalid:#}' as a value, use '{valid}{name} -- {subcmd}{valid:#}'",
506                );
507                suggestions.push(styled_suggestion);
508            }
509
510            err = err.extend_context_unchecked([
511                (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)),
512                (
513                    ContextKind::SuggestedSubcommand,
514                    ContextValue::Strings(did_you_mean),
515                ),
516                (
517                    ContextKind::Suggested,
518                    ContextValue::StyledStrs(suggestions),
519                ),
520            ]);
521            if let Some(usage) = usage {
522                err = err
523                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
524            }
525        }
526
527        err
528    }
529
530    pub(crate) fn unrecognized_subcommand(
531        cmd: &Command,
532        subcmd: String,
533        usage: Option<StyledStr>,
534    ) -> Self {
535        let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
536
537        #[cfg(feature = "error-context")]
538        {
539            err = err.extend_context_unchecked([(
540                ContextKind::InvalidSubcommand,
541                ContextValue::String(subcmd),
542            )]);
543            if let Some(usage) = usage {
544                err = err
545                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
546            }
547        }
548
549        err
550    }
551
552    pub(crate) fn missing_required_argument(
553        cmd: &Command,
554        required: Vec<String>,
555        usage: Option<StyledStr>,
556    ) -> Self {
557        let mut err = Self::new(ErrorKind::MissingRequiredArgument).with_cmd(cmd);
558
559        #[cfg(feature = "error-context")]
560        {
561            err = err.extend_context_unchecked([(
562                ContextKind::InvalidArg,
563                ContextValue::Strings(required),
564            )]);
565            if let Some(usage) = usage {
566                err = err
567                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
568            }
569        }
570
571        err
572    }
573
574    pub(crate) fn missing_subcommand(
575        cmd: &Command,
576        parent: String,
577        available: Vec<String>,
578        usage: Option<StyledStr>,
579    ) -> Self {
580        let mut err = Self::new(ErrorKind::MissingSubcommand).with_cmd(cmd);
581
582        #[cfg(feature = "error-context")]
583        {
584            err = err.extend_context_unchecked([
585                (ContextKind::InvalidSubcommand, ContextValue::String(parent)),
586                (
587                    ContextKind::ValidSubcommand,
588                    ContextValue::Strings(available),
589                ),
590            ]);
591            if let Some(usage) = usage {
592                err = err
593                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
594            }
595        }
596
597        err
598    }
599
600    pub(crate) fn invalid_utf8(cmd: &Command, usage: Option<StyledStr>) -> Self {
601        let mut err = Self::new(ErrorKind::InvalidUtf8).with_cmd(cmd);
602
603        #[cfg(feature = "error-context")]
604        {
605            if let Some(usage) = usage {
606                err = err
607                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
608            }
609        }
610
611        err
612    }
613
614    pub(crate) fn too_many_values(
615        cmd: &Command,
616        val: String,
617        arg: String,
618        usage: Option<StyledStr>,
619    ) -> Self {
620        let mut err = Self::new(ErrorKind::TooManyValues).with_cmd(cmd);
621
622        #[cfg(feature = "error-context")]
623        {
624            err = err.extend_context_unchecked([
625                (ContextKind::InvalidArg, ContextValue::String(arg)),
626                (ContextKind::InvalidValue, ContextValue::String(val)),
627            ]);
628            if let Some(usage) = usage {
629                err = err
630                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
631            }
632        }
633
634        err
635    }
636
637    pub(crate) fn too_few_values(
638        cmd: &Command,
639        arg: String,
640        min_vals: usize,
641        curr_vals: usize,
642        usage: Option<StyledStr>,
643    ) -> Self {
644        let mut err = Self::new(ErrorKind::TooFewValues).with_cmd(cmd);
645
646        #[cfg(feature = "error-context")]
647        {
648            err = err.extend_context_unchecked([
649                (ContextKind::InvalidArg, ContextValue::String(arg)),
650                (
651                    ContextKind::MinValues,
652                    ContextValue::Number(min_vals as isize),
653                ),
654                (
655                    ContextKind::ActualNumValues,
656                    ContextValue::Number(curr_vals as isize),
657                ),
658            ]);
659            if let Some(usage) = usage {
660                err = err
661                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
662            }
663        }
664
665        err
666    }
667
668    pub(crate) fn value_validation(
669        arg: String,
670        val: String,
671        err: Box<dyn error::Error + Send + Sync>,
672    ) -> Self {
673        let mut err = Self::new(ErrorKind::ValueValidation).set_source(err);
674
675        #[cfg(feature = "error-context")]
676        {
677            err = err.extend_context_unchecked([
678                (ContextKind::InvalidArg, ContextValue::String(arg)),
679                (ContextKind::InvalidValue, ContextValue::String(val)),
680            ]);
681        }
682
683        err
684    }
685
686    pub(crate) fn wrong_number_of_values(
687        cmd: &Command,
688        arg: String,
689        num_vals: usize,
690        curr_vals: usize,
691        usage: Option<StyledStr>,
692    ) -> Self {
693        let mut err = Self::new(ErrorKind::WrongNumberOfValues).with_cmd(cmd);
694
695        #[cfg(feature = "error-context")]
696        {
697            err = err.extend_context_unchecked([
698                (ContextKind::InvalidArg, ContextValue::String(arg)),
699                (
700                    ContextKind::ExpectedNumValues,
701                    ContextValue::Number(num_vals as isize),
702                ),
703                (
704                    ContextKind::ActualNumValues,
705                    ContextValue::Number(curr_vals as isize),
706                ),
707            ]);
708            if let Some(usage) = usage {
709                err = err
710                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
711            }
712        }
713
714        err
715    }
716
717    pub(crate) fn unknown_argument(
718        cmd: &Command,
719        arg: String,
720        did_you_mean: Option<(String, Option<String>)>,
721        suggested_trailing_arg: bool,
722        usage: Option<StyledStr>,
723    ) -> Self {
724        use std::fmt::Write as _;
725        let styles = cmd.get_styles();
726        let invalid = &styles.get_invalid();
727        let valid = &styles.get_valid();
728        let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
729
730        #[cfg(feature = "error-context")]
731        {
732            let mut suggestions = vec![];
733            if suggested_trailing_arg {
734                let mut styled_suggestion = StyledStr::new();
735                let _ = write!(
736                    styled_suggestion,
737                    "to pass '{invalid}{arg}{invalid:#}' as a value, use '{valid}-- {arg}{valid:#}'",
738                );
739                suggestions.push(styled_suggestion);
740            }
741
742            err = err
743                .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
744            if let Some(usage) = usage {
745                err = err
746                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
747            }
748            match did_you_mean {
749                Some((flag, Some(sub))) => {
750                    let mut styled_suggestion = StyledStr::new();
751                    let _ = write!(styled_suggestion, "'{valid}{sub} {flag}{valid:#}' exists",);
752                    suggestions.push(styled_suggestion);
753                }
754                Some((flag, None)) => {
755                    err = err.insert_context_unchecked(
756                        ContextKind::SuggestedArg,
757                        ContextValue::String(flag),
758                    );
759                }
760                None => {}
761            }
762            if !suggestions.is_empty() {
763                err = err.insert_context_unchecked(
764                    ContextKind::Suggested,
765                    ContextValue::StyledStrs(suggestions),
766                );
767            }
768        }
769
770        err
771    }
772
773    pub(crate) fn unnecessary_double_dash(
774        cmd: &Command,
775        arg: String,
776        usage: Option<StyledStr>,
777    ) -> Self {
778        use std::fmt::Write as _;
779        let styles = cmd.get_styles();
780        let invalid = &styles.get_invalid();
781        let valid = &styles.get_valid();
782        let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
783
784        #[cfg(feature = "error-context")]
785        {
786            let mut styled_suggestion = StyledStr::new();
787            let _ = write!(
788                styled_suggestion,
789                "subcommand '{valid}{arg}{valid:#}' exists; to use it, remove the '{invalid}--{invalid:#}' before it",
790            );
791
792            err = err.extend_context_unchecked([
793                (ContextKind::InvalidArg, ContextValue::String(arg)),
794                (
795                    ContextKind::Suggested,
796                    ContextValue::StyledStrs(vec![styled_suggestion]),
797                ),
798            ]);
799            if let Some(usage) = usage {
800                err = err
801                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
802            }
803        }
804
805        err
806    }
807
808    fn formatted(&self) -> Cow<'_, StyledStr> {
809        if let Some(message) = self.inner.message.as_ref() {
810            message.formatted(&self.inner.styles)
811        } else {
812            let styled = F::format_error(self);
813            Cow::Owned(styled)
814        }
815    }
816}
817
818impl<F: ErrorFormatter> From<io::Error> for Error<F> {
819    fn from(e: io::Error) -> Self {
820        Error::raw(ErrorKind::Io, e)
821    }
822}
823
824impl<F: ErrorFormatter> From<fmt::Error> for Error<F> {
825    fn from(e: fmt::Error) -> Self {
826        Error::raw(ErrorKind::Format, e)
827    }
828}
829
830impl<F: ErrorFormatter> Debug for Error<F> {
831    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
832        self.inner.fmt(f)
833    }
834}
835
836impl<F: ErrorFormatter> error::Error for Error<F> {
837    #[allow(trivial_casts)]
838    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
839        self.inner.source.as_ref().map(|e| e.as_ref() as _)
840    }
841}
842
843impl<F: ErrorFormatter> Display for Error<F> {
844    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
845        // Assuming `self.message` already has a trailing newline, from `try_help` or similar
846        ok!(write!(f, "{}", self.formatted()));
847        if let Some(backtrace) = self.inner.backtrace.as_ref() {
848            ok!(writeln!(f));
849            ok!(writeln!(f, "Backtrace:"));
850            ok!(writeln!(f, "{backtrace}"));
851        }
852        Ok(())
853    }
854}
855
856#[derive(Clone, Debug)]
857pub(crate) enum Message {
858    Raw(String),
859    Formatted(StyledStr),
860}
861
862impl Message {
863    fn format(&mut self, cmd: &Command, usage: Option<StyledStr>) {
864        match self {
865            Message::Raw(s) => {
866                let mut message = String::new();
867                std::mem::swap(s, &mut message);
868
869                let styled = format::format_error_message(
870                    &message,
871                    cmd.get_styles(),
872                    Some(cmd),
873                    usage.as_ref(),
874                );
875
876                *self = Self::Formatted(styled);
877            }
878            Message::Formatted(_) => {}
879        }
880    }
881
882    fn formatted(&self, styles: &Styles) -> Cow<'_, StyledStr> {
883        match self {
884            Message::Raw(s) => {
885                let styled = format::format_error_message(s, styles, None, None);
886
887                Cow::Owned(styled)
888            }
889            Message::Formatted(s) => Cow::Borrowed(s),
890        }
891    }
892}
893
894impl From<String> for Message {
895    fn from(inner: String) -> Self {
896        Self::Raw(inner)
897    }
898}
899
900impl From<StyledStr> for Message {
901    fn from(inner: StyledStr) -> Self {
902        Self::Formatted(inner)
903    }
904}
905
906#[cfg(feature = "debug")]
907#[derive(Debug)]
908struct Backtrace(backtrace::Backtrace);
909
910#[cfg(feature = "debug")]
911impl Backtrace {
912    fn new() -> Option<Self> {
913        Some(Self(backtrace::Backtrace::new()))
914    }
915}
916
917#[cfg(feature = "debug")]
918impl Display for Backtrace {
919    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
920        // `backtrace::Backtrace` uses `Debug` instead of `Display`
921        write!(f, "{:?}", self.0)
922    }
923}
924
925#[cfg(not(feature = "debug"))]
926#[derive(Debug)]
927struct Backtrace;
928
929#[cfg(not(feature = "debug"))]
930impl Backtrace {
931    fn new() -> Option<Self> {
932        None
933    }
934}
935
936#[cfg(not(feature = "debug"))]
937impl Display for Backtrace {
938    fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result {
939        Ok(())
940    }
941}
942
943#[test]
944fn check_auto_traits() {
945    static_assertions::assert_impl_all!(Error: Send, Sync, Unpin);
946}