1//! Error reporting
2
3#![cfg_attr(not(feature = "error-context"), allow(dead_code))]
4#![cfg_attr(not(feature = "error-context"), allow(unused_imports))]
5#![cfg_attr(not(feature = "error-context"), allow(unused_variables))]
6#![cfg_attr(not(feature = "error-context"), allow(unused_mut))]
7#![cfg_attr(not(feature = "error-context"), allow(clippy::let_and_return))]
8
9// Std
10use std::{
11 borrow::Cow,
12 convert::From,
13 error,
14 fmt::{self, Debug, Display, Formatter},
15 io::{self},
16 result::Result as StdResult,
17};
18
19// Internal
20use crate::builder::StyledStr;
21use crate::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 std::fmt::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 empty_value(cmd: &Command, good_vals: &[String], arg: String) -> Self {
395 Self::invalid_value(cmd, "".to_owned(), good_vals, arg)
396 }
397
398 pub(crate) fn no_equals(cmd: &Command, arg: String, usage: Option<StyledStr>) -> Self {
399 let mut err = Self::new(ErrorKind::NoEquals).with_cmd(cmd);
400
401 #[cfg(feature = "error-context")]
402 {
403 err = err
404 .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
405 if let Some(usage) = usage {
406 err = err
407 .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
408 }
409 }
410
411 err
412 }
413
414 pub(crate) fn invalid_value(
415 cmd: &Command,
416 bad_val: String,
417 good_vals: &[String],
418 arg: String,
419 ) -> Self {
420 let suggestion = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop();
421 let mut err = Self::new(ErrorKind::InvalidValue).with_cmd(cmd);
422
423 #[cfg(feature = "error-context")]
424 {
425 err = err.extend_context_unchecked([
426 (ContextKind::InvalidArg, ContextValue::String(arg)),
427 (ContextKind::InvalidValue, ContextValue::String(bad_val)),
428 (
429 ContextKind::ValidValue,
430 ContextValue::Strings(good_vals.iter().map(|s| (*s).to_owned()).collect()),
431 ),
432 ]);
433 if let Some(suggestion) = suggestion {
434 err = err.insert_context_unchecked(
435 ContextKind::SuggestedValue,
436 ContextValue::String(suggestion),
437 );
438 }
439 }
440
441 err
442 }
443
444 pub(crate) fn invalid_subcommand(
445 cmd: &Command,
446 subcmd: String,
447 did_you_mean: Vec<String>,
448 name: String,
449 suggested_trailing_arg: bool,
450 usage: Option<StyledStr>,
451 ) -> Self {
452 use std::fmt::Write as _;
453 let styles = cmd.get_styles();
454 let invalid = &styles.get_invalid();
455 let valid = &styles.get_valid();
456 let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
457
458 #[cfg(feature = "error-context")]
459 {
460 let mut suggestions = vec![];
461 if suggested_trailing_arg {
462 let mut styled_suggestion = StyledStr::new();
463 let _ = write!(
464 styled_suggestion,
465 "to pass '{}{subcmd}{}' as a value, use '{}{name} -- {subcmd}{}'",
466 invalid.render(),
467 invalid.render_reset(),
468 valid.render(),
469 valid.render_reset()
470 );
471 suggestions.push(styled_suggestion);
472 }
473
474 err = err.extend_context_unchecked([
475 (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)),
476 (
477 ContextKind::SuggestedSubcommand,
478 ContextValue::Strings(did_you_mean),
479 ),
480 (
481 ContextKind::Suggested,
482 ContextValue::StyledStrs(suggestions),
483 ),
484 ]);
485 if let Some(usage) = usage {
486 err = err
487 .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
488 }
489 }
490
491 err
492 }
493
494 pub(crate) fn unrecognized_subcommand(
495 cmd: &Command,
496 subcmd: String,
497 usage: Option<StyledStr>,
498 ) -> Self {
499 let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
500
501 #[cfg(feature = "error-context")]
502 {
503 err = err.extend_context_unchecked([(
504 ContextKind::InvalidSubcommand,
505 ContextValue::String(subcmd),
506 )]);
507 if let Some(usage) = usage {
508 err = err
509 .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
510 }
511 }
512
513 err
514 }
515
516 pub(crate) fn missing_required_argument(
517 cmd: &Command,
518 required: Vec<String>,
519 usage: Option<StyledStr>,
520 ) -> Self {
521 let mut err = Self::new(ErrorKind::MissingRequiredArgument).with_cmd(cmd);
522
523 #[cfg(feature = "error-context")]
524 {
525 err = err.extend_context_unchecked([(
526 ContextKind::InvalidArg,
527 ContextValue::Strings(required),
528 )]);
529 if let Some(usage) = usage {
530 err = err
531 .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
532 }
533 }
534
535 err
536 }
537
538 pub(crate) fn missing_subcommand(
539 cmd: &Command,
540 parent: String,
541 available: Vec<String>,
542 usage: Option<StyledStr>,
543 ) -> Self {
544 let mut err = Self::new(ErrorKind::MissingSubcommand).with_cmd(cmd);
545
546 #[cfg(feature = "error-context")]
547 {
548 err = err.extend_context_unchecked([
549 (ContextKind::InvalidSubcommand, ContextValue::String(parent)),
550 (
551 ContextKind::ValidSubcommand,
552 ContextValue::Strings(available),
553 ),
554 ]);
555 if let Some(usage) = usage {
556 err = err
557 .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
558 }
559 }
560
561 err
562 }
563
564 pub(crate) fn invalid_utf8(cmd: &Command, usage: Option<StyledStr>) -> Self {
565 let mut err = Self::new(ErrorKind::InvalidUtf8).with_cmd(cmd);
566
567 #[cfg(feature = "error-context")]
568 {
569 if let Some(usage) = usage {
570 err = err
571 .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
572 }
573 }
574
575 err
576 }
577
578 pub(crate) fn too_many_values(
579 cmd: &Command,
580 val: String,
581 arg: String,
582 usage: Option<StyledStr>,
583 ) -> Self {
584 let mut err = Self::new(ErrorKind::TooManyValues).with_cmd(cmd);
585
586 #[cfg(feature = "error-context")]
587 {
588 err = err.extend_context_unchecked([
589 (ContextKind::InvalidArg, ContextValue::String(arg)),
590 (ContextKind::InvalidValue, ContextValue::String(val)),
591 ]);
592 if let Some(usage) = usage {
593 err = err
594 .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
595 }
596 }
597
598 err
599 }
600
601 pub(crate) fn too_few_values(
602 cmd: &Command,
603 arg: String,
604 min_vals: usize,
605 curr_vals: usize,
606 usage: Option<StyledStr>,
607 ) -> Self {
608 let mut err = Self::new(ErrorKind::TooFewValues).with_cmd(cmd);
609
610 #[cfg(feature = "error-context")]
611 {
612 err = err.extend_context_unchecked([
613 (ContextKind::InvalidArg, ContextValue::String(arg)),
614 (
615 ContextKind::MinValues,
616 ContextValue::Number(min_vals as isize),
617 ),
618 (
619 ContextKind::ActualNumValues,
620 ContextValue::Number(curr_vals as isize),
621 ),
622 ]);
623 if let Some(usage) = usage {
624 err = err
625 .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
626 }
627 }
628
629 err
630 }
631
632 pub(crate) fn value_validation(
633 arg: String,
634 val: String,
635 err: Box<dyn error::Error + Send + Sync>,
636 ) -> Self {
637 let mut err = Self::new(ErrorKind::ValueValidation).set_source(err);
638
639 #[cfg(feature = "error-context")]
640 {
641 err = err.extend_context_unchecked([
642 (ContextKind::InvalidArg, ContextValue::String(arg)),
643 (ContextKind::InvalidValue, ContextValue::String(val)),
644 ]);
645 }
646
647 err
648 }
649
650 pub(crate) fn wrong_number_of_values(
651 cmd: &Command,
652 arg: String,
653 num_vals: usize,
654 curr_vals: usize,
655 usage: Option<StyledStr>,
656 ) -> Self {
657 let mut err = Self::new(ErrorKind::WrongNumberOfValues).with_cmd(cmd);
658
659 #[cfg(feature = "error-context")]
660 {
661 err = err.extend_context_unchecked([
662 (ContextKind::InvalidArg, ContextValue::String(arg)),
663 (
664 ContextKind::ExpectedNumValues,
665 ContextValue::Number(num_vals as isize),
666 ),
667 (
668 ContextKind::ActualNumValues,
669 ContextValue::Number(curr_vals as isize),
670 ),
671 ]);
672 if let Some(usage) = usage {
673 err = err
674 .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
675 }
676 }
677
678 err
679 }
680
681 pub(crate) fn unknown_argument(
682 cmd: &Command,
683 arg: String,
684 did_you_mean: Option<(String, Option<String>)>,
685 suggested_trailing_arg: bool,
686 usage: Option<StyledStr>,
687 ) -> Self {
688 use std::fmt::Write as _;
689 let styles = cmd.get_styles();
690 let invalid = &styles.get_invalid();
691 let valid = &styles.get_valid();
692 let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
693
694 #[cfg(feature = "error-context")]
695 {
696 let mut suggestions = vec![];
697 if suggested_trailing_arg {
698 let mut styled_suggestion = StyledStr::new();
699 let _ = write!(
700 styled_suggestion,
701 "to pass '{}{arg}{}' as a value, use '{}-- {arg}{}'",
702 invalid.render(),
703 invalid.render_reset(),
704 valid.render(),
705 valid.render_reset()
706 );
707 suggestions.push(styled_suggestion);
708 }
709
710 err = err
711 .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
712 if let Some(usage) = usage {
713 err = err
714 .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
715 }
716 match did_you_mean {
717 Some((flag, Some(sub))) => {
718 let mut styled_suggestion = StyledStr::new();
719 let _ = write!(
720 styled_suggestion,
721 "'{}{sub} {flag}{}' exists",
722 valid.render(),
723 valid.render_reset()
724 );
725 suggestions.push(styled_suggestion);
726 }
727 Some((flag, None)) => {
728 err = err.insert_context_unchecked(
729 ContextKind::SuggestedArg,
730 ContextValue::String(flag),
731 );
732 }
733 None => {}
734 }
735 if !suggestions.is_empty() {
736 err = err.insert_context_unchecked(
737 ContextKind::Suggested,
738 ContextValue::StyledStrs(suggestions),
739 );
740 }
741 }
742
743 err
744 }
745
746 pub(crate) fn unnecessary_double_dash(
747 cmd: &Command,
748 arg: String,
749 usage: Option<StyledStr>,
750 ) -> Self {
751 use std::fmt::Write as _;
752 let styles = cmd.get_styles();
753 let invalid = &styles.get_invalid();
754 let valid = &styles.get_valid();
755 let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
756
757 #[cfg(feature = "error-context")]
758 {
759 let mut styled_suggestion = StyledStr::new();
760 let _ = write!(
761 styled_suggestion,
762 "subcommand '{}{arg}{}' exists; to use it, remove the '{}--{}' before it",
763 valid.render(),
764 valid.render_reset(),
765 invalid.render(),
766 invalid.render_reset()
767 );
768
769 err = err.extend_context_unchecked([
770 (ContextKind::InvalidArg, ContextValue::String(arg)),
771 (
772 ContextKind::Suggested,
773 ContextValue::StyledStrs(vec![styled_suggestion]),
774 ),
775 ]);
776 if let Some(usage) = usage {
777 err = err
778 .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
779 }
780 }
781
782 err
783 }
784
785 fn formatted(&self) -> Cow<'_, StyledStr> {
786 if let Some(message) = self.inner.message.as_ref() {
787 message.formatted(&self.inner.styles)
788 } else {
789 let styled = F::format_error(self);
790 Cow::Owned(styled)
791 }
792 }
793}
794
795impl<F: ErrorFormatter> From<io::Error> for Error<F> {
796 fn from(e: io::Error) -> Self {
797 Error::raw(kind:ErrorKind::Io, message:e)
798 }
799}
800
801impl<F: ErrorFormatter> From<fmt::Error> for Error<F> {
802 fn from(e: fmt::Error) -> Self {
803 Error::raw(kind:ErrorKind::Format, message:e)
804 }
805}
806
807impl<F: ErrorFormatter> std::fmt::Debug for Error<F> {
808 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
809 self.inner.fmt(f)
810 }
811}
812
813impl<F: ErrorFormatter> error::Error for Error<F> {
814 #[allow(trivial_casts)]
815 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
816 self.inner.source.as_ref().map(|e: &Box| e.as_ref() as _)
817 }
818}
819
820impl<F: ErrorFormatter> Display for Error<F> {
821 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
822 // Assuming `self.message` already has a trailing newline, from `try_help` or similar
823 ok!(write!(f, "{}", self.formatted()));
824 if let Some(backtrace: &Backtrace) = self.inner.backtrace.as_ref() {
825 ok!(writeln!(f));
826 ok!(writeln!(f, "Backtrace:"));
827 ok!(writeln!(f, "{backtrace}"));
828 }
829 Ok(())
830 }
831}
832
833#[derive(Clone, Debug)]
834pub(crate) enum Message {
835 Raw(String),
836 Formatted(StyledStr),
837}
838
839impl Message {
840 fn format(&mut self, cmd: &Command, usage: Option<StyledStr>) {
841 match self {
842 Message::Raw(s) => {
843 let mut message = String::new();
844 std::mem::swap(s, &mut message);
845
846 let styled = format::format_error_message(
847 &message,
848 cmd.get_styles(),
849 Some(cmd),
850 usage.as_ref(),
851 );
852
853 *self = Self::Formatted(styled);
854 }
855 Message::Formatted(_) => {}
856 }
857 }
858
859 fn formatted(&self, styles: &Styles) -> Cow<StyledStr> {
860 match self {
861 Message::Raw(s) => {
862 let styled = format::format_error_message(s, styles, None, None);
863
864 Cow::Owned(styled)
865 }
866 Message::Formatted(s) => Cow::Borrowed(s),
867 }
868 }
869}
870
871impl From<String> for Message {
872 fn from(inner: String) -> Self {
873 Self::Raw(inner)
874 }
875}
876
877impl From<StyledStr> for Message {
878 fn from(inner: StyledStr) -> Self {
879 Self::Formatted(inner)
880 }
881}
882
883#[cfg(feature = "debug")]
884#[derive(Debug)]
885struct Backtrace(backtrace::Backtrace);
886
887#[cfg(feature = "debug")]
888impl Backtrace {
889 fn new() -> Option<Self> {
890 Some(Self(backtrace::Backtrace::new()))
891 }
892}
893
894#[cfg(feature = "debug")]
895impl Display for Backtrace {
896 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
897 // `backtrace::Backtrace` uses `Debug` instead of `Display`
898 write!(f, "{:?}", self.0)
899 }
900}
901
902#[cfg(not(feature = "debug"))]
903#[derive(Debug)]
904struct Backtrace;
905
906#[cfg(not(feature = "debug"))]
907impl Backtrace {
908 fn new() -> Option<Self> {
909 None
910 }
911}
912
913#[cfg(not(feature = "debug"))]
914impl Display for Backtrace {
915 fn fmt(&self, _: &mut Formatter) -> fmt::Result {
916 Ok(())
917 }
918}
919
920#[test]
921fn check_auto_traits() {
922 static_assertions::assert_impl_all!(Error: Send, Sync, Unpin);
923}
924