xref: /aosp_15_r20/external/cronet/third_party/rust/chromium_crates_io/vendor/clap_builder-4.5.2/src/error/mod.rs (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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::builder::Styles;
22 use crate::output::fmt::Colorizer;
23 use crate::output::fmt::Stream;
24 use crate::parser::features::suggestions;
25 use crate::util::FlatMap;
26 use crate::util::{color::ColorChoice, safe_exit, SUCCESS_CODE, USAGE_CODE};
27 use crate::Command;
28 
29 #[cfg(feature = "error-context")]
30 mod context;
31 mod format;
32 mod kind;
33 
34 pub use format::ErrorFormatter;
35 pub use format::KindFormatter;
36 pub use kind::ErrorKind;
37 
38 #[cfg(feature = "error-context")]
39 pub use context::ContextKind;
40 #[cfg(feature = "error-context")]
41 pub use context::ContextValue;
42 #[cfg(feature = "error-context")]
43 pub use format::RichFormatter;
44 
45 #[cfg(not(feature = "error-context"))]
46 pub use KindFormatter as DefaultFormatter;
47 #[cfg(feature = "error-context")]
48 pub use RichFormatter as DefaultFormatter;
49 
50 /// Short hand for [`Result`] type
51 ///
52 /// [`Result`]: std::result::Result
53 pub 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
60 pub struct Error<F: ErrorFormatter = DefaultFormatter> {
61     inner: Box<ErrorInner>,
62     phantom: std::marker::PhantomData<F>,
63 }
64 
65 #[derive(Debug)]
66 struct 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 
79 impl<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
raw(kind: ErrorKind, message: impl std::fmt::Display) -> Self88     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]
format(mut self, cmd: &mut Command) -> Self94     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     /// ```
new(kind: ErrorKind) -> Self128     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`]
with_cmd(self, cmd: &Command) -> Self149     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     /// ```
apply<EF: ErrorFormatter>(self) -> Error<EF>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
kind(&self) -> ErrorKind180     pub fn kind(&self) -> ErrorKind {
181         self.inner.kind
182     }
183 
184     /// Additional information to further qualify the error
185     #[cfg(feature = "error-context")]
context(&self) -> impl Iterator<Item = (ContextKind, &ContextValue)>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")]
get(&self, kind: ContextKind) -> Option<&ContextValue>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")]
insert(&mut self, kind: ContextKind, value: ContextValue) -> Option<ContextValue>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]
use_stderr(&self) -> bool206     pub fn use_stderr(&self) -> bool {
207         self.stream() == Stream::Stderr
208     }
209 
stream(&self) -> Stream210     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`.
exit_code(&self) -> i32221     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`.
exit(&self) -> !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     /// ```
print(&self) -> io::Result<()>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     /// ```
render(&self) -> StyledStr288     pub fn render(&self) -> StyledStr {
289         self.formatted().into_owned()
290     }
291 
292     #[inline(never)]
for_app(kind: ErrorKind, cmd: &Command, styled: StyledStr) -> Self293     fn for_app(kind: ErrorKind, cmd: &Command, styled: StyledStr) -> Self {
294         Self::new(kind).set_message(styled).with_cmd(cmd)
295     }
296 
set_message(mut self, message: impl Into<Message>) -> Self297     pub(crate) fn set_message(mut self, message: impl Into<Message>) -> Self {
298         self.inner.message = Some(message.into());
299         self
300     }
301 
set_source(mut self, source: Box<dyn error::Error + Send + Sync>) -> Self302     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 
set_styles(mut self, styles: Styles) -> Self307     pub(crate) fn set_styles(mut self, styles: Styles) -> Self {
308         self.inner.styles = styles;
309         self
310     }
311 
set_color(mut self, color_when: ColorChoice) -> Self312     pub(crate) fn set_color(mut self, color_when: ColorChoice) -> Self {
313         self.inner.color_when = color_when;
314         self
315     }
316 
set_colored_help(mut self, color_help_when: ColorChoice) -> Self317     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 
set_help_flag(mut self, help_flag: Option<&'static str>) -> Self322     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")]
insert_context_unchecked( mut self, kind: ContextKind, value: ContextValue, ) -> Self330     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")]
extend_context_unchecked<const N: usize>( mut self, context: [(ContextKind, ContextValue); N], ) -> Self342     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 
display_help(cmd: &Command, styled: StyledStr) -> Self350     pub(crate) fn display_help(cmd: &Command, styled: StyledStr) -> Self {
351         Self::for_app(ErrorKind::DisplayHelp, cmd, styled)
352     }
353 
display_help_error(cmd: &Command, styled: StyledStr) -> Self354     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 
display_version(cmd: &Command, styled: StyledStr) -> Self362     pub(crate) fn display_version(cmd: &Command, styled: StyledStr) -> Self {
363         Self::for_app(ErrorKind::DisplayVersion, cmd, styled)
364     }
365 
argument_conflict( cmd: &Command, arg: String, mut others: Vec<String>, usage: Option<StyledStr>, ) -> Self366     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 
subcommand_conflict( cmd: &Command, sub: String, mut others: Vec<String>, usage: Option<StyledStr>, ) -> Self394     pub(crate) fn subcommand_conflict(
395         cmd: &Command,
396         sub: String,
397         mut others: Vec<String>,
398         usage: Option<StyledStr>,
399     ) -> Self {
400         let mut err = Self::new(ErrorKind::ArgumentConflict).with_cmd(cmd);
401 
402         #[cfg(feature = "error-context")]
403         {
404             let others = match others.len() {
405                 0 => ContextValue::None,
406                 1 => ContextValue::String(others.pop().unwrap()),
407                 _ => ContextValue::Strings(others),
408             };
409             err = err.extend_context_unchecked([
410                 (ContextKind::InvalidSubcommand, ContextValue::String(sub)),
411                 (ContextKind::PriorArg, others),
412             ]);
413             if let Some(usage) = usage {
414                 err = err
415                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
416             }
417         }
418 
419         err
420     }
421 
empty_value(cmd: &Command, good_vals: &[String], arg: String) -> Self422     pub(crate) fn empty_value(cmd: &Command, good_vals: &[String], arg: String) -> Self {
423         Self::invalid_value(cmd, "".to_owned(), good_vals, arg)
424     }
425 
no_equals(cmd: &Command, arg: String, usage: Option<StyledStr>) -> Self426     pub(crate) fn no_equals(cmd: &Command, arg: String, usage: Option<StyledStr>) -> Self {
427         let mut err = Self::new(ErrorKind::NoEquals).with_cmd(cmd);
428 
429         #[cfg(feature = "error-context")]
430         {
431             err = err
432                 .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
433             if let Some(usage) = usage {
434                 err = err
435                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
436             }
437         }
438 
439         err
440     }
441 
invalid_value( cmd: &Command, bad_val: String, good_vals: &[String], arg: String, ) -> Self442     pub(crate) fn invalid_value(
443         cmd: &Command,
444         bad_val: String,
445         good_vals: &[String],
446         arg: String,
447     ) -> Self {
448         let suggestion = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop();
449         let mut err = Self::new(ErrorKind::InvalidValue).with_cmd(cmd);
450 
451         #[cfg(feature = "error-context")]
452         {
453             err = err.extend_context_unchecked([
454                 (ContextKind::InvalidArg, ContextValue::String(arg)),
455                 (ContextKind::InvalidValue, ContextValue::String(bad_val)),
456                 (
457                     ContextKind::ValidValue,
458                     ContextValue::Strings(good_vals.iter().map(|s| (*s).to_owned()).collect()),
459                 ),
460             ]);
461             if let Some(suggestion) = suggestion {
462                 err = err.insert_context_unchecked(
463                     ContextKind::SuggestedValue,
464                     ContextValue::String(suggestion),
465                 );
466             }
467         }
468 
469         err
470     }
471 
invalid_subcommand( cmd: &Command, subcmd: String, did_you_mean: Vec<String>, name: String, suggested_trailing_arg: bool, usage: Option<StyledStr>, ) -> Self472     pub(crate) fn invalid_subcommand(
473         cmd: &Command,
474         subcmd: String,
475         did_you_mean: Vec<String>,
476         name: String,
477         suggested_trailing_arg: bool,
478         usage: Option<StyledStr>,
479     ) -> Self {
480         use std::fmt::Write as _;
481         let styles = cmd.get_styles();
482         let invalid = &styles.get_invalid();
483         let valid = &styles.get_valid();
484         let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
485 
486         #[cfg(feature = "error-context")]
487         {
488             let mut suggestions = vec![];
489             if suggested_trailing_arg {
490                 let mut styled_suggestion = StyledStr::new();
491                 let _ = write!(
492                     styled_suggestion,
493                     "to pass '{}{subcmd}{}' as a value, use '{}{name} -- {subcmd}{}'",
494                     invalid.render(),
495                     invalid.render_reset(),
496                     valid.render(),
497                     valid.render_reset()
498                 );
499                 suggestions.push(styled_suggestion);
500             }
501 
502             err = err.extend_context_unchecked([
503                 (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)),
504                 (
505                     ContextKind::SuggestedSubcommand,
506                     ContextValue::Strings(did_you_mean),
507                 ),
508                 (
509                     ContextKind::Suggested,
510                     ContextValue::StyledStrs(suggestions),
511                 ),
512             ]);
513             if let Some(usage) = usage {
514                 err = err
515                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
516             }
517         }
518 
519         err
520     }
521 
unrecognized_subcommand( cmd: &Command, subcmd: String, usage: Option<StyledStr>, ) -> Self522     pub(crate) fn unrecognized_subcommand(
523         cmd: &Command,
524         subcmd: String,
525         usage: Option<StyledStr>,
526     ) -> Self {
527         let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
528 
529         #[cfg(feature = "error-context")]
530         {
531             err = err.extend_context_unchecked([(
532                 ContextKind::InvalidSubcommand,
533                 ContextValue::String(subcmd),
534             )]);
535             if let Some(usage) = usage {
536                 err = err
537                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
538             }
539         }
540 
541         err
542     }
543 
missing_required_argument( cmd: &Command, required: Vec<String>, usage: Option<StyledStr>, ) -> Self544     pub(crate) fn missing_required_argument(
545         cmd: &Command,
546         required: Vec<String>,
547         usage: Option<StyledStr>,
548     ) -> Self {
549         let mut err = Self::new(ErrorKind::MissingRequiredArgument).with_cmd(cmd);
550 
551         #[cfg(feature = "error-context")]
552         {
553             err = err.extend_context_unchecked([(
554                 ContextKind::InvalidArg,
555                 ContextValue::Strings(required),
556             )]);
557             if let Some(usage) = usage {
558                 err = err
559                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
560             }
561         }
562 
563         err
564     }
565 
missing_subcommand( cmd: &Command, parent: String, available: Vec<String>, usage: Option<StyledStr>, ) -> Self566     pub(crate) fn missing_subcommand(
567         cmd: &Command,
568         parent: String,
569         available: Vec<String>,
570         usage: Option<StyledStr>,
571     ) -> Self {
572         let mut err = Self::new(ErrorKind::MissingSubcommand).with_cmd(cmd);
573 
574         #[cfg(feature = "error-context")]
575         {
576             err = err.extend_context_unchecked([
577                 (ContextKind::InvalidSubcommand, ContextValue::String(parent)),
578                 (
579                     ContextKind::ValidSubcommand,
580                     ContextValue::Strings(available),
581                 ),
582             ]);
583             if let Some(usage) = usage {
584                 err = err
585                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
586             }
587         }
588 
589         err
590     }
591 
invalid_utf8(cmd: &Command, usage: Option<StyledStr>) -> Self592     pub(crate) fn invalid_utf8(cmd: &Command, usage: Option<StyledStr>) -> Self {
593         let mut err = Self::new(ErrorKind::InvalidUtf8).with_cmd(cmd);
594 
595         #[cfg(feature = "error-context")]
596         {
597             if let Some(usage) = usage {
598                 err = err
599                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
600             }
601         }
602 
603         err
604     }
605 
too_many_values( cmd: &Command, val: String, arg: String, usage: Option<StyledStr>, ) -> Self606     pub(crate) fn too_many_values(
607         cmd: &Command,
608         val: String,
609         arg: String,
610         usage: Option<StyledStr>,
611     ) -> Self {
612         let mut err = Self::new(ErrorKind::TooManyValues).with_cmd(cmd);
613 
614         #[cfg(feature = "error-context")]
615         {
616             err = err.extend_context_unchecked([
617                 (ContextKind::InvalidArg, ContextValue::String(arg)),
618                 (ContextKind::InvalidValue, ContextValue::String(val)),
619             ]);
620             if let Some(usage) = usage {
621                 err = err
622                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
623             }
624         }
625 
626         err
627     }
628 
too_few_values( cmd: &Command, arg: String, min_vals: usize, curr_vals: usize, usage: Option<StyledStr>, ) -> Self629     pub(crate) fn too_few_values(
630         cmd: &Command,
631         arg: String,
632         min_vals: usize,
633         curr_vals: usize,
634         usage: Option<StyledStr>,
635     ) -> Self {
636         let mut err = Self::new(ErrorKind::TooFewValues).with_cmd(cmd);
637 
638         #[cfg(feature = "error-context")]
639         {
640             err = err.extend_context_unchecked([
641                 (ContextKind::InvalidArg, ContextValue::String(arg)),
642                 (
643                     ContextKind::MinValues,
644                     ContextValue::Number(min_vals as isize),
645                 ),
646                 (
647                     ContextKind::ActualNumValues,
648                     ContextValue::Number(curr_vals as isize),
649                 ),
650             ]);
651             if let Some(usage) = usage {
652                 err = err
653                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
654             }
655         }
656 
657         err
658     }
659 
value_validation( arg: String, val: String, err: Box<dyn error::Error + Send + Sync>, ) -> Self660     pub(crate) fn value_validation(
661         arg: String,
662         val: String,
663         err: Box<dyn error::Error + Send + Sync>,
664     ) -> Self {
665         let mut err = Self::new(ErrorKind::ValueValidation).set_source(err);
666 
667         #[cfg(feature = "error-context")]
668         {
669             err = err.extend_context_unchecked([
670                 (ContextKind::InvalidArg, ContextValue::String(arg)),
671                 (ContextKind::InvalidValue, ContextValue::String(val)),
672             ]);
673         }
674 
675         err
676     }
677 
wrong_number_of_values( cmd: &Command, arg: String, num_vals: usize, curr_vals: usize, usage: Option<StyledStr>, ) -> Self678     pub(crate) fn wrong_number_of_values(
679         cmd: &Command,
680         arg: String,
681         num_vals: usize,
682         curr_vals: usize,
683         usage: Option<StyledStr>,
684     ) -> Self {
685         let mut err = Self::new(ErrorKind::WrongNumberOfValues).with_cmd(cmd);
686 
687         #[cfg(feature = "error-context")]
688         {
689             err = err.extend_context_unchecked([
690                 (ContextKind::InvalidArg, ContextValue::String(arg)),
691                 (
692                     ContextKind::ExpectedNumValues,
693                     ContextValue::Number(num_vals as isize),
694                 ),
695                 (
696                     ContextKind::ActualNumValues,
697                     ContextValue::Number(curr_vals as isize),
698                 ),
699             ]);
700             if let Some(usage) = usage {
701                 err = err
702                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
703             }
704         }
705 
706         err
707     }
708 
unknown_argument( cmd: &Command, arg: String, did_you_mean: Option<(String, Option<String>)>, suggested_trailing_arg: bool, usage: Option<StyledStr>, ) -> Self709     pub(crate) fn unknown_argument(
710         cmd: &Command,
711         arg: String,
712         did_you_mean: Option<(String, Option<String>)>,
713         suggested_trailing_arg: bool,
714         usage: Option<StyledStr>,
715     ) -> Self {
716         use std::fmt::Write as _;
717         let styles = cmd.get_styles();
718         let invalid = &styles.get_invalid();
719         let valid = &styles.get_valid();
720         let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
721 
722         #[cfg(feature = "error-context")]
723         {
724             let mut suggestions = vec![];
725             if suggested_trailing_arg {
726                 let mut styled_suggestion = StyledStr::new();
727                 let _ = write!(
728                     styled_suggestion,
729                     "to pass '{}{arg}{}' as a value, use '{}-- {arg}{}'",
730                     invalid.render(),
731                     invalid.render_reset(),
732                     valid.render(),
733                     valid.render_reset()
734                 );
735                 suggestions.push(styled_suggestion);
736             }
737 
738             err = err
739                 .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
740             if let Some(usage) = usage {
741                 err = err
742                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
743             }
744             match did_you_mean {
745                 Some((flag, Some(sub))) => {
746                     let mut styled_suggestion = StyledStr::new();
747                     let _ = write!(
748                         styled_suggestion,
749                         "'{}{sub} {flag}{}' exists",
750                         valid.render(),
751                         valid.render_reset()
752                     );
753                     suggestions.push(styled_suggestion);
754                 }
755                 Some((flag, None)) => {
756                     err = err.insert_context_unchecked(
757                         ContextKind::SuggestedArg,
758                         ContextValue::String(flag),
759                     );
760                 }
761                 None => {}
762             }
763             if !suggestions.is_empty() {
764                 err = err.insert_context_unchecked(
765                     ContextKind::Suggested,
766                     ContextValue::StyledStrs(suggestions),
767                 );
768             }
769         }
770 
771         err
772     }
773 
unnecessary_double_dash( cmd: &Command, arg: String, usage: Option<StyledStr>, ) -> Self774     pub(crate) fn unnecessary_double_dash(
775         cmd: &Command,
776         arg: String,
777         usage: Option<StyledStr>,
778     ) -> Self {
779         use std::fmt::Write as _;
780         let styles = cmd.get_styles();
781         let invalid = &styles.get_invalid();
782         let valid = &styles.get_valid();
783         let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
784 
785         #[cfg(feature = "error-context")]
786         {
787             let mut styled_suggestion = StyledStr::new();
788             let _ = write!(
789                 styled_suggestion,
790                 "subcommand '{}{arg}{}' exists; to use it, remove the '{}--{}' before it",
791                 valid.render(),
792                 valid.render_reset(),
793                 invalid.render(),
794                 invalid.render_reset()
795             );
796 
797             err = err.extend_context_unchecked([
798                 (ContextKind::InvalidArg, ContextValue::String(arg)),
799                 (
800                     ContextKind::Suggested,
801                     ContextValue::StyledStrs(vec![styled_suggestion]),
802                 ),
803             ]);
804             if let Some(usage) = usage {
805                 err = err
806                     .insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
807             }
808         }
809 
810         err
811     }
812 
formatted(&self) -> Cow<'_, StyledStr>813     fn formatted(&self) -> Cow<'_, StyledStr> {
814         if let Some(message) = self.inner.message.as_ref() {
815             message.formatted(&self.inner.styles)
816         } else {
817             let styled = F::format_error(self);
818             Cow::Owned(styled)
819         }
820     }
821 }
822 
823 impl<F: ErrorFormatter> From<io::Error> for Error<F> {
from(e: io::Error) -> Self824     fn from(e: io::Error) -> Self {
825         Error::raw(ErrorKind::Io, e)
826     }
827 }
828 
829 impl<F: ErrorFormatter> From<fmt::Error> for Error<F> {
from(e: fmt::Error) -> Self830     fn from(e: fmt::Error) -> Self {
831         Error::raw(ErrorKind::Format, e)
832     }
833 }
834 
835 impl<F: ErrorFormatter> std::fmt::Debug for Error<F> {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error>836     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
837         self.inner.fmt(f)
838     }
839 }
840 
841 impl<F: ErrorFormatter> error::Error for Error<F> {
842     #[allow(trivial_casts)]
source(&self) -> Option<&(dyn error::Error + 'static)>843     fn source(&self) -> Option<&(dyn error::Error + 'static)> {
844         self.inner.source.as_ref().map(|e| e.as_ref() as _)
845     }
846 }
847 
848 impl<F: ErrorFormatter> Display for Error<F> {
fmt(&self, f: &mut Formatter) -> fmt::Result849     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
850         // Assuming `self.message` already has a trailing newline, from `try_help` or similar
851         ok!(write!(f, "{}", self.formatted()));
852         if let Some(backtrace) = self.inner.backtrace.as_ref() {
853             ok!(writeln!(f));
854             ok!(writeln!(f, "Backtrace:"));
855             ok!(writeln!(f, "{backtrace}"));
856         }
857         Ok(())
858     }
859 }
860 
861 #[derive(Clone, Debug)]
862 pub(crate) enum Message {
863     Raw(String),
864     Formatted(StyledStr),
865 }
866 
867 impl Message {
format(&mut self, cmd: &Command, usage: Option<StyledStr>)868     fn format(&mut self, cmd: &Command, usage: Option<StyledStr>) {
869         match self {
870             Message::Raw(s) => {
871                 let mut message = String::new();
872                 std::mem::swap(s, &mut message);
873 
874                 let styled = format::format_error_message(
875                     &message,
876                     cmd.get_styles(),
877                     Some(cmd),
878                     usage.as_ref(),
879                 );
880 
881                 *self = Self::Formatted(styled);
882             }
883             Message::Formatted(_) => {}
884         }
885     }
886 
formatted(&self, styles: &Styles) -> Cow<StyledStr>887     fn formatted(&self, styles: &Styles) -> Cow<StyledStr> {
888         match self {
889             Message::Raw(s) => {
890                 let styled = format::format_error_message(s, styles, None, None);
891 
892                 Cow::Owned(styled)
893             }
894             Message::Formatted(s) => Cow::Borrowed(s),
895         }
896     }
897 }
898 
899 impl From<String> for Message {
from(inner: String) -> Self900     fn from(inner: String) -> Self {
901         Self::Raw(inner)
902     }
903 }
904 
905 impl From<StyledStr> for Message {
from(inner: StyledStr) -> Self906     fn from(inner: StyledStr) -> Self {
907         Self::Formatted(inner)
908     }
909 }
910 
911 #[cfg(feature = "debug")]
912 #[derive(Debug)]
913 struct Backtrace(backtrace::Backtrace);
914 
915 #[cfg(feature = "debug")]
916 impl Backtrace {
new() -> Option<Self>917     fn new() -> Option<Self> {
918         Some(Self(backtrace::Backtrace::new()))
919     }
920 }
921 
922 #[cfg(feature = "debug")]
923 impl Display for Backtrace {
fmt(&self, f: &mut Formatter) -> fmt::Result924     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
925         // `backtrace::Backtrace` uses `Debug` instead of `Display`
926         write!(f, "{:?}", self.0)
927     }
928 }
929 
930 #[cfg(not(feature = "debug"))]
931 #[derive(Debug)]
932 struct Backtrace;
933 
934 #[cfg(not(feature = "debug"))]
935 impl Backtrace {
new() -> Option<Self>936     fn new() -> Option<Self> {
937         None
938     }
939 }
940 
941 #[cfg(not(feature = "debug"))]
942 impl Display for Backtrace {
fmt(&self, _: &mut Formatter) -> fmt::Result943     fn fmt(&self, _: &mut Formatter) -> fmt::Result {
944         Ok(())
945     }
946 }
947 
948 #[test]
check_auto_traits()949 fn check_auto_traits() {
950     static_assertions::assert_impl_all!(Error: Send, Sync, Unpin);
951 }
952