]> git.proxmox.com Git - rustc.git/blob - vendor/clap/src/error/mod.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / vendor / clap / src / error / mod.rs
1 //! Error reporting
2 #![allow(deprecated)]
3
4 // Std
5 use std::{
6 borrow::Cow,
7 convert::From,
8 error,
9 fmt::{self, Debug, Display, Formatter},
10 io::{self, BufRead},
11 result::Result as StdResult,
12 };
13
14 // Internal
15 use crate::output::fmt::Colorizer;
16 use crate::output::fmt::Stream;
17 use crate::parser::features::suggestions;
18 use crate::util::{color::ColorChoice, safe_exit, SUCCESS_CODE, USAGE_CODE};
19 use crate::AppSettings;
20 use crate::Command;
21
22 mod context;
23 mod kind;
24
25 pub use context::ContextKind;
26 pub use context::ContextValue;
27 pub use kind::ErrorKind;
28
29 /// Short hand for [`Result`] type
30 ///
31 /// [`Result`]: std::result::Result
32 pub type Result<T, E = Error> = StdResult<T, E>;
33
34 /// Command Line Argument Parser Error
35 ///
36 /// See [`Command::error`] to create an error.
37 ///
38 /// [`Command::error`]: crate::Command::error
39 #[derive(Debug)]
40 pub struct Error {
41 inner: Box<ErrorInner>,
42 /// Deprecated, replaced with [`Error::kind()`]
43 #[cfg_attr(
44 feature = "deprecated",
45 deprecated(since = "3.1.0", note = "Replaced with `Error::kind()`")
46 )]
47 pub kind: ErrorKind,
48 /// Deprecated, replaced with [`Error::context()`]
49 #[cfg_attr(
50 feature = "deprecated",
51 deprecated(since = "3.1.0", note = "Replaced with `Error::context()`")
52 )]
53 pub info: Vec<String>,
54 }
55
56 #[derive(Debug)]
57 struct ErrorInner {
58 kind: ErrorKind,
59 context: Vec<(ContextKind, ContextValue)>,
60 message: Option<Message>,
61 source: Option<Box<dyn error::Error + Send + Sync>>,
62 help_flag: Option<&'static str>,
63 color_when: ColorChoice,
64 wait_on_exit: bool,
65 backtrace: Option<Backtrace>,
66 }
67
68 impl Error {
69 /// Create an unformatted error
70 ///
71 /// This is for you need to pass the error up to
72 /// a place that has access to the `Command` at which point you can call [`Error::format`].
73 ///
74 /// Prefer [`Command::error`] for generating errors.
75 ///
76 /// [`Command::error`]: crate::Command::error
77 pub fn raw(kind: ErrorKind, message: impl std::fmt::Display) -> Self {
78 Self::new(kind).set_message(message.to_string())
79 }
80
81 /// Format the existing message with the Command's context
82 #[must_use]
83 pub fn format(mut self, cmd: &mut Command) -> Self {
84 cmd._build_self();
85 let usage = cmd.render_usage();
86 if let Some(message) = self.inner.message.as_mut() {
87 message.format(cmd, usage);
88 }
89 self.with_cmd(cmd)
90 }
91
92 /// Type of error for programmatic processing
93 pub fn kind(&self) -> ErrorKind {
94 self.inner.kind
95 }
96
97 /// Additional information to further qualify the error
98 pub fn context(&self) -> impl Iterator<Item = (ContextKind, &ContextValue)> {
99 self.inner.context.iter().map(|(k, v)| (*k, v))
100 }
101
102 /// Should the message be written to `stdout` or not?
103 #[inline]
104 pub fn use_stderr(&self) -> bool {
105 self.stream() == Stream::Stderr
106 }
107
108 pub(crate) fn stream(&self) -> Stream {
109 match self.kind() {
110 ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => Stream::Stdout,
111 _ => Stream::Stderr,
112 }
113 }
114
115 /// Prints the error and exits.
116 ///
117 /// Depending on the error kind, this either prints to `stderr` and exits with a status of `2`
118 /// or prints to `stdout` and exits with a status of `0`.
119 pub fn exit(&self) -> ! {
120 if self.use_stderr() {
121 // Swallow broken pipe errors
122 let _ = self.print();
123
124 if self.inner.wait_on_exit {
125 wlnerr!("\nPress [ENTER] / [RETURN] to continue...");
126 let mut s = String::new();
127 let i = io::stdin();
128 i.lock().read_line(&mut s).unwrap();
129 }
130
131 safe_exit(USAGE_CODE);
132 }
133
134 // Swallow broken pipe errors
135 let _ = self.print();
136 safe_exit(SUCCESS_CODE)
137 }
138
139 /// Prints formatted and colored error to `stdout` or `stderr` according to its error kind
140 ///
141 /// # Example
142 /// ```no_run
143 /// use clap::Command;
144 ///
145 /// match Command::new("Command").try_get_matches() {
146 /// Ok(matches) => {
147 /// // do_something
148 /// },
149 /// Err(err) => {
150 /// err.print().expect("Error writing Error");
151 /// // do_something
152 /// },
153 /// };
154 /// ```
155 pub fn print(&self) -> io::Result<()> {
156 self.formatted().print()
157 }
158
159 /// Deprecated, replaced with [`Command::error`]
160 ///
161 /// [`Command::error`]: crate::Command::error
162 #[cfg_attr(
163 feature = "deprecated",
164 deprecated(since = "3.0.0", note = "Replaced with `Command::error`")
165 )]
166 #[doc(hidden)]
167 pub fn with_description(description: String, kind: ErrorKind) -> Self {
168 Error::raw(kind, description)
169 }
170
171 fn new(kind: ErrorKind) -> Self {
172 Self {
173 inner: Box::new(ErrorInner {
174 kind,
175 context: Vec::new(),
176 message: None,
177 source: None,
178 help_flag: None,
179 color_when: ColorChoice::Never,
180 wait_on_exit: false,
181 backtrace: Backtrace::new(),
182 }),
183 kind,
184 info: vec![],
185 }
186 }
187
188 #[inline(never)]
189 fn for_app(kind: ErrorKind, cmd: &Command, colorizer: Colorizer, info: Vec<String>) -> Self {
190 Self::new(kind)
191 .set_message(colorizer)
192 .with_cmd(cmd)
193 .set_info(info)
194 }
195
196 pub(crate) fn with_cmd(self, cmd: &Command) -> Self {
197 self.set_wait_on_exit(cmd.is_set(AppSettings::WaitOnError))
198 .set_color(cmd.get_color())
199 .set_help_flag(get_help_flag(cmd))
200 }
201
202 pub(crate) fn set_message(mut self, message: impl Into<Message>) -> Self {
203 self.inner.message = Some(message.into());
204 self
205 }
206
207 pub(crate) fn set_info(mut self, info: Vec<String>) -> Self {
208 self.info = info;
209 self
210 }
211
212 pub(crate) fn set_source(mut self, source: Box<dyn error::Error + Send + Sync>) -> Self {
213 self.inner.source = Some(source);
214 self
215 }
216
217 pub(crate) fn set_color(mut self, color_when: ColorChoice) -> Self {
218 self.inner.color_when = color_when;
219 self
220 }
221
222 pub(crate) fn set_help_flag(mut self, help_flag: Option<&'static str>) -> Self {
223 self.inner.help_flag = help_flag;
224 self
225 }
226
227 pub(crate) fn set_wait_on_exit(mut self, yes: bool) -> Self {
228 self.inner.wait_on_exit = yes;
229 self
230 }
231
232 /// Does not verify if `ContextKind` is already present
233 #[inline(never)]
234 pub(crate) fn insert_context_unchecked(
235 mut self,
236 kind: ContextKind,
237 value: ContextValue,
238 ) -> Self {
239 self.inner.context.push((kind, value));
240 self
241 }
242
243 /// Does not verify if `ContextKind` is already present
244 #[inline(never)]
245 pub(crate) fn extend_context_unchecked<const N: usize>(
246 mut self,
247 context: [(ContextKind, ContextValue); N],
248 ) -> Self {
249 self.inner.context.extend(context);
250 self
251 }
252
253 #[inline(never)]
254 fn get_context(&self, kind: ContextKind) -> Option<&ContextValue> {
255 self.inner
256 .context
257 .iter()
258 .find_map(|(k, v)| (*k == kind).then(|| v))
259 }
260
261 pub(crate) fn display_help(cmd: &Command, colorizer: Colorizer) -> Self {
262 Self::for_app(ErrorKind::DisplayHelp, cmd, colorizer, vec![])
263 }
264
265 pub(crate) fn display_help_error(cmd: &Command, colorizer: Colorizer) -> Self {
266 Self::for_app(
267 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
268 cmd,
269 colorizer,
270 vec![],
271 )
272 }
273
274 pub(crate) fn display_version(cmd: &Command, colorizer: Colorizer) -> Self {
275 Self::for_app(ErrorKind::DisplayVersion, cmd, colorizer, vec![])
276 }
277
278 pub(crate) fn argument_conflict(
279 cmd: &Command,
280 arg: String,
281 mut others: Vec<String>,
282 usage: String,
283 ) -> Self {
284 let info = others.clone();
285 let others = match others.len() {
286 0 => ContextValue::None,
287 1 => ContextValue::String(others.pop().unwrap()),
288 _ => ContextValue::Strings(others),
289 };
290 Self::new(ErrorKind::ArgumentConflict)
291 .with_cmd(cmd)
292 .set_info(info)
293 .extend_context_unchecked([
294 (ContextKind::InvalidArg, ContextValue::String(arg)),
295 (ContextKind::PriorArg, others),
296 (ContextKind::Usage, ContextValue::String(usage)),
297 ])
298 }
299
300 pub(crate) fn empty_value(cmd: &Command, good_vals: &[&str], arg: String) -> Self {
301 let info = vec![arg.clone()];
302 let mut err = Self::new(ErrorKind::EmptyValue)
303 .with_cmd(cmd)
304 .set_info(info)
305 .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
306 if !good_vals.is_empty() {
307 err = err.insert_context_unchecked(
308 ContextKind::ValidValue,
309 ContextValue::Strings(good_vals.iter().map(|s| (*s).to_owned()).collect()),
310 );
311 }
312 err
313 }
314
315 pub(crate) fn no_equals(cmd: &Command, arg: String, usage: String) -> Self {
316 let info = vec![arg.clone()];
317 Self::new(ErrorKind::NoEquals)
318 .with_cmd(cmd)
319 .set_info(info)
320 .extend_context_unchecked([
321 (ContextKind::InvalidArg, ContextValue::String(arg)),
322 (ContextKind::Usage, ContextValue::String(usage)),
323 ])
324 }
325
326 pub(crate) fn invalid_value(
327 cmd: &Command,
328 bad_val: String,
329 good_vals: &[&str],
330 arg: String,
331 ) -> Self {
332 let mut info = vec![arg.clone(), bad_val.clone()];
333 info.extend(good_vals.iter().map(|s| (*s).to_owned()));
334
335 let suggestion = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop();
336 let mut err = Self::new(ErrorKind::InvalidValue)
337 .with_cmd(cmd)
338 .set_info(info)
339 .extend_context_unchecked([
340 (ContextKind::InvalidArg, ContextValue::String(arg)),
341 (ContextKind::InvalidValue, ContextValue::String(bad_val)),
342 (
343 ContextKind::ValidValue,
344 ContextValue::Strings(good_vals.iter().map(|s| (*s).to_owned()).collect()),
345 ),
346 ]);
347 if let Some(suggestion) = suggestion {
348 err = err.insert_context_unchecked(
349 ContextKind::SuggestedValue,
350 ContextValue::String(suggestion),
351 );
352 }
353 err
354 }
355
356 pub(crate) fn invalid_subcommand(
357 cmd: &Command,
358 subcmd: String,
359 did_you_mean: String,
360 name: String,
361 usage: String,
362 ) -> Self {
363 let info = vec![subcmd.clone()];
364 let suggestion = format!("{} -- {}", name, subcmd);
365 Self::new(ErrorKind::InvalidSubcommand)
366 .with_cmd(cmd)
367 .set_info(info)
368 .extend_context_unchecked([
369 (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)),
370 (
371 ContextKind::SuggestedSubcommand,
372 ContextValue::String(did_you_mean),
373 ),
374 (
375 ContextKind::SuggestedCommand,
376 ContextValue::String(suggestion),
377 ),
378 (ContextKind::Usage, ContextValue::String(usage)),
379 ])
380 }
381
382 pub(crate) fn unrecognized_subcommand(cmd: &Command, subcmd: String, usage: String) -> Self {
383 let info = vec![subcmd.clone()];
384 Self::new(ErrorKind::UnrecognizedSubcommand)
385 .with_cmd(cmd)
386 .set_info(info)
387 .extend_context_unchecked([
388 (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)),
389 (ContextKind::Usage, ContextValue::String(usage)),
390 ])
391 }
392
393 pub(crate) fn missing_required_argument(
394 cmd: &Command,
395 required: Vec<String>,
396 usage: String,
397 ) -> Self {
398 let info = required.clone();
399 Self::new(ErrorKind::MissingRequiredArgument)
400 .with_cmd(cmd)
401 .set_info(info)
402 .extend_context_unchecked([
403 (ContextKind::InvalidArg, ContextValue::Strings(required)),
404 (ContextKind::Usage, ContextValue::String(usage)),
405 ])
406 }
407
408 pub(crate) fn missing_subcommand(cmd: &Command, name: String, usage: String) -> Self {
409 let info = vec![];
410 Self::new(ErrorKind::MissingSubcommand)
411 .with_cmd(cmd)
412 .set_info(info)
413 .extend_context_unchecked([
414 (ContextKind::InvalidSubcommand, ContextValue::String(name)),
415 (ContextKind::Usage, ContextValue::String(usage)),
416 ])
417 }
418
419 pub(crate) fn invalid_utf8(cmd: &Command, usage: String) -> Self {
420 let info = vec![];
421 Self::new(ErrorKind::InvalidUtf8)
422 .with_cmd(cmd)
423 .set_info(info)
424 .extend_context_unchecked([(ContextKind::Usage, ContextValue::String(usage))])
425 }
426
427 pub(crate) fn too_many_occurrences(
428 cmd: &Command,
429 arg: String,
430 max_occurs: usize,
431 curr_occurs: usize,
432 usage: String,
433 ) -> Self {
434 let info = vec![arg.clone(), curr_occurs.to_string(), max_occurs.to_string()];
435 Self::new(ErrorKind::TooManyOccurrences)
436 .with_cmd(cmd)
437 .set_info(info)
438 .extend_context_unchecked([
439 (ContextKind::InvalidArg, ContextValue::String(arg)),
440 (
441 ContextKind::MaxOccurrences,
442 ContextValue::Number(max_occurs as isize),
443 ),
444 (
445 ContextKind::ActualNumValues,
446 ContextValue::Number(curr_occurs as isize),
447 ),
448 (ContextKind::Usage, ContextValue::String(usage)),
449 ])
450 }
451
452 pub(crate) fn too_many_values(cmd: &Command, val: String, arg: String, usage: String) -> Self {
453 let info = vec![arg.clone(), val.clone()];
454 Self::new(ErrorKind::TooManyValues)
455 .with_cmd(cmd)
456 .set_info(info)
457 .extend_context_unchecked([
458 (ContextKind::InvalidArg, ContextValue::String(arg)),
459 (ContextKind::InvalidValue, ContextValue::String(val)),
460 (ContextKind::Usage, ContextValue::String(usage)),
461 ])
462 }
463
464 pub(crate) fn too_few_values(
465 cmd: &Command,
466 arg: String,
467 min_vals: usize,
468 curr_vals: usize,
469 usage: String,
470 ) -> Self {
471 let info = vec![arg.clone(), curr_vals.to_string(), min_vals.to_string()];
472 Self::new(ErrorKind::TooFewValues)
473 .with_cmd(cmd)
474 .set_info(info)
475 .extend_context_unchecked([
476 (ContextKind::InvalidArg, ContextValue::String(arg)),
477 (
478 ContextKind::MinValues,
479 ContextValue::Number(min_vals as isize),
480 ),
481 (
482 ContextKind::ActualNumValues,
483 ContextValue::Number(curr_vals as isize),
484 ),
485 (ContextKind::Usage, ContextValue::String(usage)),
486 ])
487 }
488
489 pub(crate) fn value_validation(
490 arg: String,
491 val: String,
492 err: Box<dyn error::Error + Send + Sync>,
493 ) -> Self {
494 let info = vec![arg.clone(), val.to_string(), err.to_string()];
495 Self::new(ErrorKind::ValueValidation)
496 .set_info(info)
497 .set_source(err)
498 .extend_context_unchecked([
499 (ContextKind::InvalidArg, ContextValue::String(arg)),
500 (ContextKind::InvalidValue, ContextValue::String(val)),
501 ])
502 }
503
504 pub(crate) fn wrong_number_of_values(
505 cmd: &Command,
506 arg: String,
507 num_vals: usize,
508 curr_vals: usize,
509 usage: String,
510 ) -> Self {
511 let info = vec![arg.clone(), curr_vals.to_string(), num_vals.to_string()];
512 Self::new(ErrorKind::WrongNumberOfValues)
513 .with_cmd(cmd)
514 .set_info(info)
515 .extend_context_unchecked([
516 (ContextKind::InvalidArg, ContextValue::String(arg)),
517 (
518 ContextKind::ExpectedNumValues,
519 ContextValue::Number(num_vals as isize),
520 ),
521 (
522 ContextKind::ActualNumValues,
523 ContextValue::Number(curr_vals as isize),
524 ),
525 (ContextKind::Usage, ContextValue::String(usage)),
526 ])
527 }
528
529 pub(crate) fn unexpected_multiple_usage(cmd: &Command, arg: String, usage: String) -> Self {
530 let info = vec![arg.clone()];
531 Self::new(ErrorKind::UnexpectedMultipleUsage)
532 .with_cmd(cmd)
533 .set_info(info)
534 .extend_context_unchecked([
535 (ContextKind::InvalidArg, ContextValue::String(arg)),
536 (ContextKind::Usage, ContextValue::String(usage)),
537 ])
538 }
539
540 pub(crate) fn unknown_argument(
541 cmd: &Command,
542 arg: String,
543 did_you_mean: Option<(String, Option<String>)>,
544 usage: String,
545 ) -> Self {
546 let info = vec![arg.clone()];
547 let mut err = Self::new(ErrorKind::UnknownArgument)
548 .with_cmd(cmd)
549 .set_info(info)
550 .extend_context_unchecked([
551 (ContextKind::InvalidArg, ContextValue::String(arg)),
552 (ContextKind::Usage, ContextValue::String(usage)),
553 ]);
554 if let Some((flag, sub)) = did_you_mean {
555 err = err.insert_context_unchecked(
556 ContextKind::SuggestedArg,
557 ContextValue::String(format!("--{}", flag)),
558 );
559 if let Some(sub) = sub {
560 err = err.insert_context_unchecked(
561 ContextKind::SuggestedSubcommand,
562 ContextValue::String(sub),
563 );
564 }
565 }
566 err
567 }
568
569 pub(crate) fn unnecessary_double_dash(cmd: &Command, arg: String, usage: String) -> Self {
570 let info = vec![arg.clone()];
571 Self::new(ErrorKind::UnknownArgument)
572 .with_cmd(cmd)
573 .set_info(info)
574 .extend_context_unchecked([
575 (ContextKind::InvalidArg, ContextValue::String(arg)),
576 (ContextKind::TrailingArg, ContextValue::Bool(true)),
577 (ContextKind::Usage, ContextValue::String(usage)),
578 ])
579 }
580
581 pub(crate) fn argument_not_found_auto(arg: String) -> Self {
582 let info = vec![arg.clone()];
583 Self::new(ErrorKind::ArgumentNotFound)
584 .set_info(info)
585 .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))])
586 }
587
588 fn formatted(&self) -> Cow<'_, Colorizer> {
589 if let Some(message) = self.inner.message.as_ref() {
590 message.formatted()
591 } else {
592 let mut c = Colorizer::new(self.stream(), self.inner.color_when);
593
594 start_error(&mut c);
595
596 if !self.write_dynamic_context(&mut c) {
597 if let Some(msg) = self.kind().as_str() {
598 c.none(msg.to_owned());
599 } else if let Some(source) = self.inner.source.as_ref() {
600 c.none(source.to_string());
601 } else {
602 c.none("Unknown cause");
603 }
604 }
605
606 let usage = self.get_context(ContextKind::Usage);
607 if let Some(ContextValue::String(usage)) = usage {
608 put_usage(&mut c, usage);
609 }
610
611 try_help(&mut c, self.inner.help_flag);
612
613 Cow::Owned(c)
614 }
615 }
616
617 #[must_use]
618 fn write_dynamic_context(&self, c: &mut Colorizer) -> bool {
619 match self.kind() {
620 ErrorKind::ArgumentConflict => {
621 let invalid_arg = self.get_context(ContextKind::InvalidArg);
622 let prior_arg = self.get_context(ContextKind::PriorArg);
623 if let (Some(ContextValue::String(invalid_arg)), Some(prior_arg)) =
624 (invalid_arg, prior_arg)
625 {
626 c.none("The argument '");
627 c.warning(invalid_arg);
628 c.none("' cannot be used with");
629
630 match prior_arg {
631 ContextValue::Strings(values) => {
632 c.none(":");
633 for v in values {
634 c.none("\n ");
635 c.warning(&**v);
636 }
637 }
638 ContextValue::String(value) => {
639 c.none(" '");
640 c.warning(value);
641 c.none("'");
642 }
643 _ => {
644 c.none(" one or more of the other specified arguments");
645 }
646 }
647 true
648 } else {
649 false
650 }
651 }
652 ErrorKind::EmptyValue => {
653 let invalid_arg = self.get_context(ContextKind::InvalidArg);
654 if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
655 c.none("The argument '");
656 c.warning(invalid_arg);
657 c.none("' requires a value but none was supplied");
658
659 let possible_values = self.get_context(ContextKind::ValidValue);
660 if let Some(ContextValue::Strings(possible_values)) = possible_values {
661 c.none("\n\t[possible values: ");
662 if let Some((last, elements)) = possible_values.split_last() {
663 for v in elements {
664 c.good(escape(v));
665 c.none(", ");
666 }
667 c.good(escape(last));
668 }
669 c.none("]");
670 }
671 true
672 } else {
673 false
674 }
675 }
676 ErrorKind::NoEquals => {
677 let invalid_arg = self.get_context(ContextKind::InvalidArg);
678 if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
679 c.none("Equal sign is needed when assigning values to '");
680 c.warning(invalid_arg);
681 c.none("'.");
682 true
683 } else {
684 false
685 }
686 }
687 ErrorKind::InvalidValue => {
688 let invalid_arg = self.get_context(ContextKind::InvalidArg);
689 let invalid_value = self.get_context(ContextKind::InvalidValue);
690 if let (
691 Some(ContextValue::String(invalid_arg)),
692 Some(ContextValue::String(invalid_value)),
693 ) = (invalid_arg, invalid_value)
694 {
695 c.none(quote(invalid_value));
696 c.none(" isn't a valid value for '");
697 c.warning(invalid_arg);
698 c.none("'");
699
700 let possible_values = self.get_context(ContextKind::ValidValue);
701 if let Some(ContextValue::Strings(possible_values)) = possible_values {
702 c.none("\n\t[possible values: ");
703 if let Some((last, elements)) = possible_values.split_last() {
704 for v in elements {
705 c.good(escape(v));
706 c.none(", ");
707 }
708 c.good(escape(last));
709 }
710 c.none("]");
711 }
712
713 let suggestion = self.get_context(ContextKind::SuggestedValue);
714 if let Some(ContextValue::String(suggestion)) = suggestion {
715 c.none("\n\n\tDid you mean ");
716 c.good(quote(suggestion));
717 c.none("?");
718 }
719 true
720 } else {
721 false
722 }
723 }
724 ErrorKind::InvalidSubcommand => {
725 let invalid_sub = self.get_context(ContextKind::InvalidSubcommand);
726 if let Some(ContextValue::String(invalid_sub)) = invalid_sub {
727 c.none("The subcommand '");
728 c.warning(invalid_sub);
729 c.none("' wasn't recognized");
730
731 let valid_sub = self.get_context(ContextKind::SuggestedSubcommand);
732 if let Some(ContextValue::String(valid_sub)) = valid_sub {
733 c.none("\n\n\tDid you mean ");
734 c.good(valid_sub);
735 c.none("?");
736 }
737
738 let suggestion = self.get_context(ContextKind::SuggestedCommand);
739 if let Some(ContextValue::String(suggestion)) = suggestion {
740 c.none(
741 "\n\nIf you believe you received this message in error, try re-running with '",
742 );
743 c.good(suggestion);
744 c.none("'");
745 }
746 true
747 } else {
748 false
749 }
750 }
751 ErrorKind::UnrecognizedSubcommand => {
752 let invalid_sub = self.get_context(ContextKind::InvalidSubcommand);
753 if let Some(ContextValue::String(invalid_sub)) = invalid_sub {
754 c.none("The subcommand '");
755 c.warning(invalid_sub);
756 c.none("' wasn't recognized");
757 true
758 } else {
759 false
760 }
761 }
762 ErrorKind::MissingRequiredArgument => {
763 let invalid_arg = self.get_context(ContextKind::InvalidArg);
764 if let Some(ContextValue::Strings(invalid_arg)) = invalid_arg {
765 c.none("The following required arguments were not provided:");
766 for v in invalid_arg {
767 c.none("\n ");
768 c.good(&**v);
769 }
770 true
771 } else {
772 false
773 }
774 }
775 ErrorKind::MissingSubcommand => {
776 let invalid_sub = self.get_context(ContextKind::InvalidSubcommand);
777 if let Some(ContextValue::String(invalid_sub)) = invalid_sub {
778 c.none("'");
779 c.warning(invalid_sub);
780 c.none("' requires a subcommand but one was not provided");
781 true
782 } else {
783 false
784 }
785 }
786 ErrorKind::InvalidUtf8 => false,
787 ErrorKind::TooManyOccurrences => {
788 let invalid_arg = self.get_context(ContextKind::InvalidArg);
789 let actual_num_occurs = self.get_context(ContextKind::ActualNumOccurrences);
790 let max_occurs = self.get_context(ContextKind::MaxOccurrences);
791 if let (
792 Some(ContextValue::String(invalid_arg)),
793 Some(ContextValue::Number(actual_num_occurs)),
794 Some(ContextValue::Number(max_occurs)),
795 ) = (invalid_arg, actual_num_occurs, max_occurs)
796 {
797 let were_provided = Error::singular_or_plural(*actual_num_occurs as usize);
798 c.none("The argument '");
799 c.warning(invalid_arg);
800 c.none("' allows at most ");
801 c.warning(max_occurs.to_string());
802 c.none(" occurrences but ");
803 c.warning(actual_num_occurs.to_string());
804 c.none(were_provided);
805 true
806 } else {
807 false
808 }
809 }
810 ErrorKind::TooManyValues => {
811 let invalid_arg = self.get_context(ContextKind::InvalidArg);
812 let invalid_value = self.get_context(ContextKind::InvalidValue);
813 if let (
814 Some(ContextValue::String(invalid_arg)),
815 Some(ContextValue::String(invalid_value)),
816 ) = (invalid_arg, invalid_value)
817 {
818 c.none("The value '");
819 c.warning(invalid_value);
820 c.none("' was provided to '");
821 c.warning(invalid_arg);
822 c.none("' but it wasn't expecting any more values");
823 true
824 } else {
825 false
826 }
827 }
828 ErrorKind::TooFewValues => {
829 let invalid_arg = self.get_context(ContextKind::InvalidArg);
830 let actual_num_values = self.get_context(ContextKind::ActualNumValues);
831 let min_values = self.get_context(ContextKind::MinValues);
832 if let (
833 Some(ContextValue::String(invalid_arg)),
834 Some(ContextValue::Number(actual_num_values)),
835 Some(ContextValue::Number(min_values)),
836 ) = (invalid_arg, actual_num_values, min_values)
837 {
838 let were_provided = Error::singular_or_plural(*actual_num_values as usize);
839 c.none("The argument '");
840 c.warning(invalid_arg);
841 c.none("' requires at least ");
842 c.warning(min_values.to_string());
843 c.none(" values but only ");
844 c.warning(actual_num_values.to_string());
845 c.none(were_provided);
846 true
847 } else {
848 false
849 }
850 }
851 ErrorKind::ValueValidation => {
852 let invalid_arg = self.get_context(ContextKind::InvalidArg);
853 let invalid_value = self.get_context(ContextKind::InvalidValue);
854 if let (
855 Some(ContextValue::String(invalid_arg)),
856 Some(ContextValue::String(invalid_value)),
857 ) = (invalid_arg, invalid_value)
858 {
859 c.none("Invalid value ");
860 c.warning(quote(invalid_value));
861 c.none(" for '");
862 c.warning(invalid_arg);
863 if let Some(source) = self.inner.source.as_deref() {
864 c.none("': ");
865 c.none(source.to_string());
866 } else {
867 c.none("'");
868 }
869 true
870 } else {
871 false
872 }
873 }
874 ErrorKind::WrongNumberOfValues => {
875 let invalid_arg = self.get_context(ContextKind::InvalidArg);
876 let actual_num_values = self.get_context(ContextKind::ActualNumValues);
877 let num_values = self.get_context(ContextKind::ExpectedNumValues);
878 if let (
879 Some(ContextValue::String(invalid_arg)),
880 Some(ContextValue::Number(actual_num_values)),
881 Some(ContextValue::Number(num_values)),
882 ) = (invalid_arg, actual_num_values, num_values)
883 {
884 let were_provided = Error::singular_or_plural(*actual_num_values as usize);
885 c.none("The argument '");
886 c.warning(invalid_arg);
887 c.none("' requires ");
888 c.warning(num_values.to_string());
889 c.none(" values, but ");
890 c.warning(actual_num_values.to_string());
891 c.none(were_provided);
892 true
893 } else {
894 false
895 }
896 }
897 ErrorKind::UnexpectedMultipleUsage => {
898 let invalid_arg = self.get_context(ContextKind::InvalidArg);
899 if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
900 c.none("The argument '");
901 c.warning(invalid_arg.to_string());
902 c.none("' was provided more than once, but cannot be used multiple times");
903 true
904 } else {
905 false
906 }
907 }
908 ErrorKind::UnknownArgument => {
909 let invalid_arg = self.get_context(ContextKind::InvalidArg);
910 if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
911 c.none("Found argument '");
912 c.warning(invalid_arg.to_string());
913 c.none("' which wasn't expected, or isn't valid in this context");
914
915 let valid_sub = self.get_context(ContextKind::SuggestedSubcommand);
916 let valid_arg = self.get_context(ContextKind::SuggestedArg);
917 match (valid_sub, valid_arg) {
918 (
919 Some(ContextValue::String(valid_sub)),
920 Some(ContextValue::String(valid_arg)),
921 ) => {
922 c.none("\n\n\tDid you mean ");
923 c.none("to put '");
924 c.good(valid_arg);
925 c.none("' after the subcommand '");
926 c.good(valid_sub);
927 c.none("'?");
928 }
929 (None, Some(ContextValue::String(valid_arg))) => {
930 c.none("\n\n\tDid you mean '");
931 c.good(valid_arg);
932 c.none("'?");
933 }
934 (_, _) => {}
935 }
936
937 let invalid_arg = self.get_context(ContextKind::InvalidArg);
938 if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
939 if invalid_arg.starts_with('-') {
940 c.none(format!(
941 "\n\n\tIf you tried to supply `{}` as a value rather than a flag, use `-- {}`",
942 invalid_arg, invalid_arg
943 ));
944 }
945
946 let trailing_arg = self.get_context(ContextKind::TrailingArg);
947 if trailing_arg == Some(&ContextValue::Bool(true)) {
948 c.none(format!(
949 "\n\n\tIf you tried to supply `{}` as a subcommand, remove the '--' before it.",
950 invalid_arg
951 ));
952 }
953 }
954 true
955 } else {
956 false
957 }
958 }
959 ErrorKind::ArgumentNotFound => {
960 let invalid_arg = self.get_context(ContextKind::InvalidArg);
961 if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
962 c.none("The argument '");
963 c.warning(invalid_arg.to_string());
964 c.none("' wasn't found");
965 true
966 } else {
967 false
968 }
969 }
970 ErrorKind::DisplayHelp
971 | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
972 | ErrorKind::DisplayVersion
973 | ErrorKind::Io
974 | ErrorKind::Format => false,
975 }
976 }
977
978 /// Returns the singular or plural form on the verb to be based on the argument's value.
979 fn singular_or_plural(n: usize) -> &'static str {
980 if n > 1 {
981 " were provided"
982 } else {
983 " was provided"
984 }
985 }
986 }
987
988 impl From<io::Error> for Error {
989 fn from(e: io::Error) -> Self {
990 Error::raw(ErrorKind::Io, e)
991 }
992 }
993
994 impl From<fmt::Error> for Error {
995 fn from(e: fmt::Error) -> Self {
996 Error::raw(ErrorKind::Format, e)
997 }
998 }
999
1000 impl error::Error for Error {
1001 #[allow(trivial_casts)]
1002 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
1003 self.inner.source.as_ref().map(|e| e.as_ref() as _)
1004 }
1005 }
1006
1007 impl Display for Error {
1008 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1009 // Assuming `self.message` already has a trailing newline, from `try_help` or similar
1010 write!(f, "{}", self.formatted())?;
1011 if let Some(backtrace) = self.inner.backtrace.as_ref() {
1012 writeln!(f)?;
1013 writeln!(f, "Backtrace:")?;
1014 writeln!(f, "{}", backtrace)?;
1015 }
1016 Ok(())
1017 }
1018 }
1019
1020 fn start_error(c: &mut Colorizer) {
1021 c.error("error:");
1022 c.none(" ");
1023 }
1024
1025 fn put_usage(c: &mut Colorizer, usage: impl Into<String>) {
1026 c.none("\n\n");
1027 c.none(usage);
1028 }
1029
1030 fn get_help_flag(cmd: &Command) -> Option<&'static str> {
1031 if !cmd.is_disable_help_flag_set() {
1032 Some("--help")
1033 } else if cmd.has_subcommands() && !cmd.is_disable_help_subcommand_set() {
1034 Some("help")
1035 } else {
1036 None
1037 }
1038 }
1039
1040 fn try_help(c: &mut Colorizer, help: Option<&str>) {
1041 if let Some(help) = help {
1042 c.none("\n\nFor more information try ");
1043 c.good(help);
1044 c.none("\n");
1045 } else {
1046 c.none("\n");
1047 }
1048 }
1049
1050 fn quote(s: impl AsRef<str>) -> String {
1051 let s = s.as_ref();
1052 format!("{:?}", s)
1053 }
1054
1055 fn escape(s: impl AsRef<str>) -> String {
1056 let s = s.as_ref();
1057 if s.contains(char::is_whitespace) {
1058 quote(s)
1059 } else {
1060 s.to_owned()
1061 }
1062 }
1063
1064 #[derive(Clone, Debug)]
1065 pub(crate) enum Message {
1066 Raw(String),
1067 Formatted(Colorizer),
1068 }
1069
1070 impl Message {
1071 fn format(&mut self, cmd: &Command, usage: String) {
1072 match self {
1073 Message::Raw(s) => {
1074 let mut c = Colorizer::new(Stream::Stderr, cmd.get_color());
1075
1076 let mut message = String::new();
1077 std::mem::swap(s, &mut message);
1078 start_error(&mut c);
1079 c.none(message);
1080 put_usage(&mut c, usage);
1081 try_help(&mut c, get_help_flag(cmd));
1082 *self = Self::Formatted(c);
1083 }
1084 Message::Formatted(_) => {}
1085 }
1086 }
1087
1088 fn formatted(&self) -> Cow<Colorizer> {
1089 match self {
1090 Message::Raw(s) => {
1091 let mut c = Colorizer::new(Stream::Stderr, ColorChoice::Never);
1092 start_error(&mut c);
1093 c.none(s);
1094 Cow::Owned(c)
1095 }
1096 Message::Formatted(c) => Cow::Borrowed(c),
1097 }
1098 }
1099 }
1100
1101 impl From<String> for Message {
1102 fn from(inner: String) -> Self {
1103 Self::Raw(inner)
1104 }
1105 }
1106
1107 impl From<Colorizer> for Message {
1108 fn from(inner: Colorizer) -> Self {
1109 Self::Formatted(inner)
1110 }
1111 }
1112
1113 #[cfg(feature = "debug")]
1114 #[derive(Debug)]
1115 struct Backtrace(backtrace::Backtrace);
1116
1117 #[cfg(feature = "debug")]
1118 impl Backtrace {
1119 fn new() -> Option<Self> {
1120 Some(Self(backtrace::Backtrace::new()))
1121 }
1122 }
1123
1124 #[cfg(feature = "debug")]
1125 impl Display for Backtrace {
1126 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1127 // `backtrace::Backtrace` uses `Debug` instead of `Display`
1128 write!(f, "{:?}", self.0)
1129 }
1130 }
1131
1132 #[cfg(not(feature = "debug"))]
1133 #[derive(Debug)]
1134 struct Backtrace;
1135
1136 #[cfg(not(feature = "debug"))]
1137 impl Backtrace {
1138 fn new() -> Option<Self> {
1139 None
1140 }
1141 }
1142
1143 #[cfg(not(feature = "debug"))]
1144 impl Display for Backtrace {
1145 fn fmt(&self, _: &mut Formatter) -> fmt::Result {
1146 Ok(())
1147 }
1148 }
1149
1150 #[cfg(test)]
1151 mod tests {
1152 /// Check `clap::Error` impls Send and Sync.
1153 mod clap_error_impl_send_sync {
1154 use crate::Error;
1155 trait Foo: std::error::Error + Send + Sync + 'static {}
1156 impl Foo for Error {}
1157 }
1158 }