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