• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
10 use 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
20 use crate::builder::StyledStr;
21 use crate::output::fmt::Colorizer;
22 use crate::output::fmt::Stream;
23 use crate::parser::features::suggestions;
24 use crate::util::FlatMap;
25 use crate::util::{color::ColorChoice, safe_exit, SUCCESS_CODE, USAGE_CODE};
26 use crate::Command;
27 
28 #[cfg(feature = "error-context")]
29 mod context;
30 mod format;
31 mod kind;
32 
33 pub use format::ErrorFormatter;
34 pub use format::KindFormatter;
35 pub use kind::ErrorKind;
36 
37 #[cfg(feature = "error-context")]
38 pub use context::ContextKind;
39 #[cfg(feature = "error-context")]
40 pub use context::ContextValue;
41 #[cfg(feature = "error-context")]
42 pub use format::RichFormatter;
43 
44 #[cfg(not(feature = "error-context"))]
45 pub use KindFormatter as DefaultFormatter;
46 #[cfg(feature = "error-context")]
47 pub use RichFormatter as DefaultFormatter;
48 
49 /// Short hand for [`Result`] type
50 ///
51 /// [`Result`]: std::result::Result
52 pub 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
59 pub struct Error<F: ErrorFormatter = DefaultFormatter> {
60     inner: Box<ErrorInner>,
61     phantom: std::marker::PhantomData<F>,
62 }
63 
64 #[derive(Debug)]
65 struct 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 
77 impl<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
raw(kind: ErrorKind, message: impl std::fmt::Display) -> Self86     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]
format(mut self, cmd: &mut Command) -> Self92     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     /// ```
new(kind: ErrorKind) -> Self124     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`]
with_cmd(self, cmd: &Command) -> Self144     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     /// ```
apply<EF: ErrorFormatter>(self) -> Error<EF>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
kind(&self) -> ErrorKind173     pub fn kind(&self) -> ErrorKind {
174         self.inner.kind
175     }
176 
177     /// Additional information to further qualify the error
178     #[cfg(feature = "error-context")]
context(&self) -> impl Iterator<Item = (ContextKind, &ContextValue)>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")]
get(&self, kind: ContextKind) -> Option<&ContextValue>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")]
insert(&mut self, kind: ContextKind, value: ContextValue) -> Option<ContextValue>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]
use_stderr(&self) -> bool199     pub fn use_stderr(&self) -> bool {
200         self.stream() == Stream::Stderr
201     }
202 
stream(&self) -> Stream203     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`.
exit(&self) -> !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     /// ```
print(&self) -> io::Result<()>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     /// ```
render(&self) -> StyledStr274     pub fn render(&self) -> StyledStr {
275         self.formatted().into_owned()
276     }
277 
278     #[inline(never)]
for_app(kind: ErrorKind, cmd: &Command, styled: StyledStr) -> Self279     fn for_app(kind: ErrorKind, cmd: &Command, styled: StyledStr) -> Self {
280         Self::new(kind).set_message(styled).with_cmd(cmd)
281     }
282 
set_message(mut self, message: impl Into<Message>) -> Self283     pub(crate) fn set_message(mut self, message: impl Into<Message>) -> Self {
284         self.inner.message = Some(message.into());
285         self
286     }
287 
set_source(mut self, source: Box<dyn error::Error + Send + Sync>) -> Self288     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 
set_color(mut self, color_when: ColorChoice) -> Self293     pub(crate) fn set_color(mut self, color_when: ColorChoice) -> Self {
294         self.inner.color_when = color_when;
295         self
296     }
297 
set_colored_help(mut self, color_help_when: ColorChoice) -> Self298     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 
set_help_flag(mut self, help_flag: Option<&'static str>) -> Self303     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")]
insert_context_unchecked( mut self, kind: ContextKind, value: ContextValue, ) -> Self311     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")]
extend_context_unchecked<const N: usize>( mut self, context: [(ContextKind, ContextValue); N], ) -> Self323     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 
display_help(cmd: &Command, styled: StyledStr) -> Self331     pub(crate) fn display_help(cmd: &Command, styled: StyledStr) -> Self {
332         Self::for_app(ErrorKind::DisplayHelp, cmd, styled)
333     }
334 
display_help_error(cmd: &Command, styled: StyledStr) -> Self335     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 
display_version(cmd: &Command, styled: StyledStr) -> Self343     pub(crate) fn display_version(cmd: &Command, styled: StyledStr) -> Self {
344         Self::for_app(ErrorKind::DisplayVersion, cmd, styled)
345     }
346 
argument_conflict( cmd: &Command, arg: String, mut others: Vec<String>, usage: Option<StyledStr>, ) -> Self347     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 
empty_value(cmd: &Command, good_vals: &[String], arg: String) -> Self375     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 
no_equals(cmd: &Command, arg: String, usage: Option<StyledStr>) -> Self379     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 
invalid_value( cmd: &Command, bad_val: String, good_vals: &[String], arg: String, ) -> Self395     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 
invalid_subcommand( cmd: &Command, subcmd: String, did_you_mean: Vec<String>, name: String, usage: Option<StyledStr>, ) -> Self425     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 
unrecognized_subcommand( cmd: &Command, subcmd: String, usage: Option<StyledStr>, ) -> Self465     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 
missing_required_argument( cmd: &Command, required: Vec<String>, usage: Option<StyledStr>, ) -> Self487     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 
missing_subcommand( cmd: &Command, parent: String, available: Vec<String>, usage: Option<StyledStr>, ) -> Self509     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 
invalid_utf8(cmd: &Command, usage: Option<StyledStr>) -> Self535     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 
too_many_values( cmd: &Command, val: String, arg: String, usage: Option<StyledStr>, ) -> Self549     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 
too_few_values( cmd: &Command, arg: String, min_vals: usize, curr_vals: usize, usage: Option<StyledStr>, ) -> Self572     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 
value_validation( arg: String, val: String, err: Box<dyn error::Error + Send + Sync>, ) -> Self603     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 
wrong_number_of_values( cmd: &Command, arg: String, num_vals: usize, curr_vals: usize, usage: Option<StyledStr>, ) -> Self621     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 
unknown_argument( cmd: &Command, arg: String, did_you_mean: Option<(String, Option<String>)>, suggested_trailing_arg: bool, usage: Option<StyledStr>, ) -> Self652     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 
unnecessary_double_dash( cmd: &Command, arg: String, usage: Option<StyledStr>, ) -> Self711     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 
formatted(&self) -> Cow<'_, StyledStr>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 
753 impl<F: ErrorFormatter> From<io::Error> for Error<F> {
from(e: io::Error) -> Self754     fn from(e: io::Error) -> Self {
755         Error::raw(ErrorKind::Io, e)
756     }
757 }
758 
759 impl<F: ErrorFormatter> From<fmt::Error> for Error<F> {
from(e: fmt::Error) -> Self760     fn from(e: fmt::Error) -> Self {
761         Error::raw(ErrorKind::Format, e)
762     }
763 }
764 
765 impl<F: ErrorFormatter> std::fmt::Debug for Error<F> {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error>766     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
767         self.inner.fmt(f)
768     }
769 }
770 
771 impl<F: ErrorFormatter> error::Error for Error<F> {
772     #[allow(trivial_casts)]
source(&self) -> Option<&(dyn error::Error + 'static)>773     fn source(&self) -> Option<&(dyn error::Error + 'static)> {
774         self.inner.source.as_ref().map(|e| e.as_ref() as _)
775     }
776 }
777 
778 impl<F: ErrorFormatter> Display for Error<F> {
fmt(&self, f: &mut Formatter) -> fmt::Result779     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)]
792 pub(crate) enum Message {
793     Raw(String),
794     Formatted(StyledStr),
795 }
796 
797 impl Message {
format(&mut self, cmd: &Command, usage: Option<StyledStr>)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 
formatted(&self) -> Cow<StyledStr>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 
824 impl From<String> for Message {
from(inner: String) -> Self825     fn from(inner: String) -> Self {
826         Self::Raw(inner)
827     }
828 }
829 
830 impl From<StyledStr> for Message {
from(inner: StyledStr) -> Self831     fn from(inner: StyledStr) -> Self {
832         Self::Formatted(inner)
833     }
834 }
835 
836 #[cfg(feature = "debug")]
837 #[derive(Debug)]
838 struct Backtrace(backtrace::Backtrace);
839 
840 #[cfg(feature = "debug")]
841 impl Backtrace {
new() -> Option<Self>842     fn new() -> Option<Self> {
843         Some(Self(backtrace::Backtrace::new()))
844     }
845 }
846 
847 #[cfg(feature = "debug")]
848 impl Display for Backtrace {
fmt(&self, f: &mut Formatter) -> fmt::Result849     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)]
857 struct Backtrace;
858 
859 #[cfg(not(feature = "debug"))]
860 impl Backtrace {
new() -> Option<Self>861     fn new() -> Option<Self> {
862         None
863     }
864 }
865 
866 #[cfg(not(feature = "debug"))]
867 impl Display for Backtrace {
fmt(&self, _: &mut Formatter) -> fmt::Result868     fn fmt(&self, _: &mut Formatter) -> fmt::Result {
869         Ok(())
870     }
871 }
872 
873 #[test]
check_auto_traits()874 fn check_auto_traits() {
875     static_assertions::assert_impl_all!(Error: Send, Sync, Unpin);
876 }
877