1//! Error reporting 2 3#![cfg_attr(not(feature = "error-context"), allow(dead_code))] 4#![cfg_attr(not(feature = "error-context"), allow(unused_imports))] 5#![cfg_attr(not(feature = "error-context"), allow(unused_variables))] 6#![cfg_attr(not(feature = "error-context"), allow(unused_mut))] 7#![cfg_attr(not(feature = "error-context"), allow(clippy::let_and_return))] 8 9// Std 10use std::{ 11 borrow::Cow, 12 convert::From, 13 error, 14 fmt::{self, Debug, Display, Formatter}, 15 io::{self}, 16 result::Result as StdResult, 17}; 18 19// Internal 20use crate::builder::StyledStr; 21use crate::output::fmt::Colorizer; 22use crate::output::fmt::Stream; 23use crate::parser::features::suggestions; 24use crate::util::FlatMap; 25use crate::util::{color::ColorChoice, safe_exit, SUCCESS_CODE, USAGE_CODE}; 26use crate::Command; 27 28#[cfg(feature = "error-context")] 29mod context; 30mod format; 31mod kind; 32 33pub use format::ErrorFormatter; 34pub use format::KindFormatter; 35pub use kind::ErrorKind; 36 37#[cfg(feature = "error-context")] 38pub use context::ContextKind; 39#[cfg(feature = "error-context")] 40pub use context::ContextValue; 41#[cfg(feature = "error-context")] 42pub use format::RichFormatter; 43 44#[cfg(not(feature = "error-context"))] 45pub use KindFormatter as DefaultFormatter; 46#[cfg(feature = "error-context")] 47pub use RichFormatter as DefaultFormatter; 48 49/// Short hand for [`Result`] type 50/// 51/// [`Result`]: std::result::Result 52pub 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 59pub struct Error<F: ErrorFormatter = DefaultFormatter> { 60 inner: Box<ErrorInner>, 61 phantom: std::marker::PhantomData<F>, 62} 63 64#[derive(Debug)] 65struct 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 77impl<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 86 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] 92 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 /// ``` 124 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`] 144 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 /// ``` 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 173 pub fn kind(&self) -> ErrorKind { 174 self.inner.kind 175 } 176 177 /// Additional information to further qualify the error 178 #[cfg(feature = "error-context")] 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")] 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")] 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] 199 pub fn use_stderr(&self) -> bool { 200 self.stream() == Stream::Stderr 201 } 202 203 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`. 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 /// ``` 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 /// ``` 274 pub fn render(&self) -> StyledStr { 275 self.formatted().into_owned() 276 } 277 278 #[inline(never)] 279 fn for_app(kind: ErrorKind, cmd: &Command, styled: StyledStr) -> Self { 280 Self::new(kind).set_message(styled).with_cmd(cmd) 281 } 282 283 pub(crate) fn set_message(mut self, message: impl Into<Message>) -> Self { 284 self.inner.message = Some(message.into()); 285 self 286 } 287 288 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 293 pub(crate) fn set_color(mut self, color_when: ColorChoice) -> Self { 294 self.inner.color_when = color_when; 295 self 296 } 297 298 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 303 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")] 311 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")] 323 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 331 pub(crate) fn display_help(cmd: &Command, styled: StyledStr) -> Self { 332 Self::for_app(ErrorKind::DisplayHelp, cmd, styled) 333 } 334 335 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 343 pub(crate) fn display_version(cmd: &Command, styled: StyledStr) -> Self { 344 Self::for_app(ErrorKind::DisplayVersion, cmd, styled) 345 } 346 347 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 375 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 379 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 395 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 425 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 465 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 487 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 509 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 535 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 549 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 572 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 603 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 621 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 652 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 711 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 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 753impl<F: ErrorFormatter> From<io::Error> for Error<F> { 754 fn from(e: io::Error) -> Self { 755 Error::raw(ErrorKind::Io, e) 756 } 757} 758 759impl<F: ErrorFormatter> From<fmt::Error> for Error<F> { 760 fn from(e: fmt::Error) -> Self { 761 Error::raw(ErrorKind::Format, e) 762 } 763} 764 765impl<F: ErrorFormatter> std::fmt::Debug for Error<F> { 766 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { 767 self.inner.fmt(f) 768 } 769} 770 771impl<F: ErrorFormatter> error::Error for Error<F> { 772 #[allow(trivial_casts)] 773 fn source(&self) -> Option<&(dyn error::Error + 'static)> { 774 self.inner.source.as_ref().map(|e| e.as_ref() as _) 775 } 776} 777 778impl<F: ErrorFormatter> Display for Error<F> { 779 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)] 792pub(crate) enum Message { 793 Raw(String), 794 Formatted(StyledStr), 795} 796 797impl Message { 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 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 824impl From<String> for Message { 825 fn from(inner: String) -> Self { 826 Self::Raw(inner) 827 } 828} 829 830impl From<StyledStr> for Message { 831 fn from(inner: StyledStr) -> Self { 832 Self::Formatted(inner) 833 } 834} 835 836#[cfg(feature = "debug")] 837#[derive(Debug)] 838struct Backtrace(backtrace::Backtrace); 839 840#[cfg(feature = "debug")] 841impl Backtrace { 842 fn new() -> Option<Self> { 843 Some(Self(backtrace::Backtrace::new())) 844 } 845} 846 847#[cfg(feature = "debug")] 848impl Display for Backtrace { 849 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)] 857struct Backtrace; 858 859#[cfg(not(feature = "debug"))] 860impl Backtrace { 861 fn new() -> Option<Self> { 862 None 863 } 864} 865 866#[cfg(not(feature = "debug"))] 867impl Display for Backtrace { 868 fn fmt(&self, _: &mut Formatter) -> fmt::Result { 869 Ok(()) 870 } 871} 872 873#[test] 874fn check_auto_traits() { 875 static_assertions::assert_impl_all!(Error: Send, Sync, Unpin); 876} 877