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, safe_exit, 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<&'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        safe_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<&'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 '{}{subcmd}{}' as a value, use '{}{name} -- {subcmd}{}'",
494                    invalid.render(),
495                    invalid.render_reset(),
496                    valid.render(),
497                    valid.render_reset()
498                );
499                suggestions.push(styled_suggestion);
500            }
501
502            err = err.extend_context_unchecked([
503                (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)),
504                (
505                    ContextKind::SuggestedSubcommand,
506                    ContextValue::Strings(did_you_mean),
507                ),
508                (
509                    ContextKind::Suggested,
510                    ContextValue::StyledStrs(suggestions),
511                ),
512            ]);
513            if let Some(usage) = usage {
514                err = err
515                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
516            }
517        }
518
519        err
520    }
521
522    pub(crate) fn unrecognized_subcommand(
523        cmd: &Command,
524        subcmd: String,
525        usage: Option<StyledStr>,
526    ) -> Self {
527        let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
528
529        #[cfg(feature = "error-context")]
530        {
531            err = err.extend_context_unchecked([(
532                ContextKind::InvalidSubcommand,
533                ContextValue::String(subcmd),
534            )]);
535            if let Some(usage) = usage {
536                err = err
537                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
538            }
539        }
540
541        err
542    }
543
544    pub(crate) fn missing_required_argument(
545        cmd: &Command,
546        required: Vec<String>,
547        usage: Option<StyledStr>,
548    ) -> Self {
549        let mut err = Self::new(ErrorKind::MissingRequiredArgument).with_cmd(cmd);
550
551        #[cfg(feature = "error-context")]
552        {
553            err = err.extend_context_unchecked([(
554                ContextKind::InvalidArg,
555                ContextValue::Strings(required),
556            )]);
557            if let Some(usage) = usage {
558                err = err
559                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
560            }
561        }
562
563        err
564    }
565
566    pub(crate) fn missing_subcommand(
567        cmd: &Command,
568        parent: String,
569        available: Vec<String>,
570        usage: Option<StyledStr>,
571    ) -> Self {
572        let mut err = Self::new(ErrorKind::MissingSubcommand).with_cmd(cmd);
573
574        #[cfg(feature = "error-context")]
575        {
576            err = err.extend_context_unchecked([
577                (ContextKind::InvalidSubcommand, ContextValue::String(parent)),
578                (
579                    ContextKind::ValidSubcommand,
580                    ContextValue::Strings(available),
581                ),
582            ]);
583            if let Some(usage) = usage {
584                err = err
585                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
586            }
587        }
588
589        err
590    }
591
592    pub(crate) fn invalid_utf8(cmd: &Command, usage: Option<StyledStr>) -> Self {
593        let mut err = Self::new(ErrorKind::InvalidUtf8).with_cmd(cmd);
594
595        #[cfg(feature = "error-context")]
596        {
597            if let Some(usage) = usage {
598                err = err
599                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
600            }
601        }
602
603        err
604    }
605
606    pub(crate) fn too_many_values(
607        cmd: &Command,
608        val: String,
609        arg: String,
610        usage: Option<StyledStr>,
611    ) -> Self {
612        let mut err = Self::new(ErrorKind::TooManyValues).with_cmd(cmd);
613
614        #[cfg(feature = "error-context")]
615        {
616            err = err.extend_context_unchecked([
617                (ContextKind::InvalidArg, ContextValue::String(arg)),
618                (ContextKind::InvalidValue, ContextValue::String(val)),
619            ]);
620            if let Some(usage) = usage {
621                err = err
622                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
623            }
624        }
625
626        err
627    }
628
629    pub(crate) fn too_few_values(
630        cmd: &Command,
631        arg: String,
632        min_vals: usize,
633        curr_vals: usize,
634        usage: Option<StyledStr>,
635    ) -> Self {
636        let mut err = Self::new(ErrorKind::TooFewValues).with_cmd(cmd);
637
638        #[cfg(feature = "error-context")]
639        {
640            err = err.extend_context_unchecked([
641                (ContextKind::InvalidArg, ContextValue::String(arg)),
642                (
643                    ContextKind::MinValues,
644                    ContextValue::Number(min_vals as isize),
645                ),
646                (
647                    ContextKind::ActualNumValues,
648                    ContextValue::Number(curr_vals as isize),
649                ),
650            ]);
651            if let Some(usage) = usage {
652                err = err
653                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
654            }
655        }
656
657        err
658    }
659
660    pub(crate) fn value_validation(
661        arg: String,
662        val: String,
663        err: Box<dyn error::Error + Send + Sync>,
664    ) -> Self {
665        let mut err = Self::new(ErrorKind::ValueValidation).set_source(err);
666
667        #[cfg(feature = "error-context")]
668        {
669            err = err.extend_context_unchecked([
670                (ContextKind::InvalidArg, ContextValue::String(arg)),
671                (ContextKind::InvalidValue, ContextValue::String(val)),
672            ]);
673        }
674
675        err
676    }
677
678    pub(crate) fn wrong_number_of_values(
679        cmd: &Command,
680        arg: String,
681        num_vals: usize,
682        curr_vals: usize,
683        usage: Option<StyledStr>,
684    ) -> Self {
685        let mut err = Self::new(ErrorKind::WrongNumberOfValues).with_cmd(cmd);
686
687        #[cfg(feature = "error-context")]
688        {
689            err = err.extend_context_unchecked([
690                (ContextKind::InvalidArg, ContextValue::String(arg)),
691                (
692                    ContextKind::ExpectedNumValues,
693                    ContextValue::Number(num_vals as isize),
694                ),
695                (
696                    ContextKind::ActualNumValues,
697                    ContextValue::Number(curr_vals as isize),
698                ),
699            ]);
700            if let Some(usage) = usage {
701                err = err
702                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
703            }
704        }
705
706        err
707    }
708
709    pub(crate) fn unknown_argument(
710        cmd: &Command,
711        arg: String,
712        did_you_mean: Option<(String, Option<String>)>,
713        suggested_trailing_arg: bool,
714        usage: Option<StyledStr>,
715    ) -> Self {
716        use std::fmt::Write as _;
717        let styles = cmd.get_styles();
718        let invalid = &styles.get_invalid();
719        let valid = &styles.get_valid();
720        let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
721
722        #[cfg(feature = "error-context")]
723        {
724            let mut suggestions = vec![];
725            if suggested_trailing_arg {
726                let mut styled_suggestion = StyledStr::new();
727                let _ = write!(
728                    styled_suggestion,
729                    "to pass '{}{arg}{}' as a value, use '{}-- {arg}{}'",
730                    invalid.render(),
731                    invalid.render_reset(),
732                    valid.render(),
733                    valid.render_reset()
734                );
735                suggestions.push(styled_suggestion);
736            }
737
738            err = err
739                .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
740            if let Some(usage) = usage {
741                err = err
742                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
743            }
744            match did_you_mean {
745                Some((flag, Some(sub))) => {
746                    let mut styled_suggestion = StyledStr::new();
747                    let _ = write!(
748                        styled_suggestion,
749                        "'{}{sub} {flag}{}' exists",
750                        valid.render(),
751                        valid.render_reset()
752                    );
753                    suggestions.push(styled_suggestion);
754                }
755                Some((flag, None)) => {
756                    err = err.insert_context_unchecked(
757                        ContextKind::SuggestedArg,
758                        ContextValue::String(flag),
759                    );
760                }
761                None => {}
762            }
763            if !suggestions.is_empty() {
764                err = err.insert_context_unchecked(
765                    ContextKind::Suggested,
766                    ContextValue::StyledStrs(suggestions),
767                );
768            }
769        }
770
771        err
772    }
773
774    pub(crate) fn unnecessary_double_dash(
775        cmd: &Command,
776        arg: String,
777        usage: Option<StyledStr>,
778    ) -> Self {
779        use std::fmt::Write as _;
780        let styles = cmd.get_styles();
781        let invalid = &styles.get_invalid();
782        let valid = &styles.get_valid();
783        let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
784
785        #[cfg(feature = "error-context")]
786        {
787            let mut styled_suggestion = StyledStr::new();
788            let _ = write!(
789                styled_suggestion,
790                "subcommand '{}{arg}{}' exists; to use it, remove the '{}--{}' before it",
791                valid.render(),
792                valid.render_reset(),
793                invalid.render(),
794                invalid.render_reset()
795            );
796
797            err = err.extend_context_unchecked([
798                (ContextKind::InvalidArg, ContextValue::String(arg)),
799                (
800                    ContextKind::Suggested,
801                    ContextValue::StyledStrs(vec![styled_suggestion]),
802                ),
803            ]);
804            if let Some(usage) = usage {
805                err = err
806                    .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
807            }
808        }
809
810        err
811    }
812
813    fn formatted(&self) -> Cow<'_, StyledStr> {
814        if let Some(message) = self.inner.message.as_ref() {
815            message.formatted(&self.inner.styles)
816        } else {
817            let styled = F::format_error(self);
818            Cow::Owned(styled)
819        }
820    }
821}
822
823impl<F: ErrorFormatter> From<io::Error> for Error<F> {
824    fn from(e: io::Error) -> Self {
825        Error::raw(ErrorKind::Io, e)
826    }
827}
828
829impl<F: ErrorFormatter> From<fmt::Error> for Error<F> {
830    fn from(e: fmt::Error) -> Self {
831        Error::raw(ErrorKind::Format, e)
832    }
833}
834
835impl<F: ErrorFormatter> Debug for Error<F> {
836    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
837        self.inner.fmt(f)
838    }
839}
840
841impl<F: ErrorFormatter> error::Error for Error<F> {
842    #[allow(trivial_casts)]
843    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
844        self.inner.source.as_ref().map(|e| e.as_ref() as _)
845    }
846}
847
848impl<F: ErrorFormatter> Display for Error<F> {
849    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
850        // Assuming `self.message` already has a trailing newline, from `try_help` or similar
851        ok!(write!(f, "{}", self.formatted()));
852        if let Some(backtrace) = self.inner.backtrace.as_ref() {
853            ok!(writeln!(f));
854            ok!(writeln!(f, "Backtrace:"));
855            ok!(writeln!(f, "{backtrace}"));
856        }
857        Ok(())
858    }
859}
860
861#[derive(Clone, Debug)]
862pub(crate) enum Message {
863    Raw(String),
864    Formatted(StyledStr),
865}
866
867impl Message {
868    fn format(&mut self, cmd: &Command, usage: Option<StyledStr>) {
869        match self {
870            Message::Raw(s) => {
871                let mut message = String::new();
872                std::mem::swap(s, &mut message);
873
874                let styled = format::format_error_message(
875                    &message,
876                    cmd.get_styles(),
877                    Some(cmd),
878                    usage.as_ref(),
879                );
880
881                *self = Self::Formatted(styled);
882            }
883            Message::Formatted(_) => {}
884        }
885    }
886
887    fn formatted(&self, styles: &Styles) -> Cow<'_, StyledStr> {
888        match self {
889            Message::Raw(s) => {
890                let styled = format::format_error_message(s, styles, None, None);
891
892                Cow::Owned(styled)
893            }
894            Message::Formatted(s) => Cow::Borrowed(s),
895        }
896    }
897}
898
899impl From<String> for Message {
900    fn from(inner: String) -> Self {
901        Self::Raw(inner)
902    }
903}
904
905impl From<StyledStr> for Message {
906    fn from(inner: StyledStr) -> Self {
907        Self::Formatted(inner)
908    }
909}
910
911#[cfg(feature = "debug")]
912#[derive(Debug)]
913struct Backtrace(backtrace::Backtrace);
914
915#[cfg(feature = "debug")]
916impl Backtrace {
917    fn new() -> Option<Self> {
918        Some(Self(backtrace::Backtrace::new()))
919    }
920}
921
922#[cfg(feature = "debug")]
923impl Display for Backtrace {
924    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
925        // `backtrace::Backtrace` uses `Debug` instead of `Display`
926        write!(f, "{:?}", self.0)
927    }
928}
929
930#[cfg(not(feature = "debug"))]
931#[derive(Debug)]
932struct Backtrace;
933
934#[cfg(not(feature = "debug"))]
935impl Backtrace {
936    fn new() -> Option<Self> {
937        None
938    }
939}
940
941#[cfg(not(feature = "debug"))]
942impl Display for Backtrace {
943    fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result {
944        Ok(())
945    }
946}
947
948#[test]
949fn check_auto_traits() {
950    static_assertions::assert_impl_all!(Error: Send, Sync, Unpin);
951}