| #![doc = include_str!("../../core/src/error.md")] |
| #![stable(feature = "rust1", since = "1.0.0")] |
| |
| #[cfg(test)] |
| mod tests; |
| |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub use core::error::Error; |
| #[unstable(feature = "error_generic_member_access", issue = "99301")] |
| pub use core::error::{Request, request_ref, request_value}; |
| |
| use crate::backtrace::Backtrace; |
| use crate::fmt::{self, Write}; |
| |
| /// An error reporter that prints an error and its sources. |
| /// |
| /// Report also exposes configuration options for formatting the error sources, either entirely on a |
| /// single line, or in multi-line format with each source on a new line. |
| /// |
| /// `Report` only requires that the wrapped error implement `Error`. It doesn't require that the |
| /// wrapped error be `Send`, `Sync`, or `'static`. |
| /// |
| /// # Examples |
| /// |
| /// ```rust |
| /// #![feature(error_reporter)] |
| /// use std::error::{Error, Report}; |
| /// use std::fmt; |
| /// |
| /// #[derive(Debug)] |
| /// struct SuperError { |
| /// source: SuperErrorSideKick, |
| /// } |
| /// |
| /// impl fmt::Display for SuperError { |
| /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// write!(f, "SuperError is here!") |
| /// } |
| /// } |
| /// |
| /// impl Error for SuperError { |
| /// fn source(&self) -> Option<&(dyn Error + 'static)> { |
| /// Some(&self.source) |
| /// } |
| /// } |
| /// |
| /// #[derive(Debug)] |
| /// struct SuperErrorSideKick; |
| /// |
| /// impl fmt::Display for SuperErrorSideKick { |
| /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// write!(f, "SuperErrorSideKick is here!") |
| /// } |
| /// } |
| /// |
| /// impl Error for SuperErrorSideKick {} |
| /// |
| /// fn get_super_error() -> Result<(), SuperError> { |
| /// Err(SuperError { source: SuperErrorSideKick }) |
| /// } |
| /// |
| /// fn main() { |
| /// match get_super_error() { |
| /// Err(e) => println!("Error: {}", Report::new(e)), |
| /// _ => println!("No error"), |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// This example produces the following output: |
| /// |
| /// ```console |
| /// Error: SuperError is here!: SuperErrorSideKick is here! |
| /// ``` |
| /// |
| /// ## Output consistency |
| /// |
| /// Report prints the same output via `Display` and `Debug`, so it works well with |
| /// [`Result::unwrap`]/[`Result::expect`] which print their `Err` variant via `Debug`: |
| /// |
| /// ```should_panic |
| /// #![feature(error_reporter)] |
| /// use std::error::Report; |
| /// # use std::error::Error; |
| /// # use std::fmt; |
| /// # #[derive(Debug)] |
| /// # struct SuperError { |
| /// # source: SuperErrorSideKick, |
| /// # } |
| /// # impl fmt::Display for SuperError { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperError is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperError { |
| /// # fn source(&self) -> Option<&(dyn Error + 'static)> { |
| /// # Some(&self.source) |
| /// # } |
| /// # } |
| /// # #[derive(Debug)] |
| /// # struct SuperErrorSideKick; |
| /// # impl fmt::Display for SuperErrorSideKick { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperErrorSideKick is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperErrorSideKick {} |
| /// # fn get_super_error() -> Result<(), SuperError> { |
| /// # Err(SuperError { source: SuperErrorSideKick }) |
| /// # } |
| /// |
| /// get_super_error().map_err(Report::new).unwrap(); |
| /// ``` |
| /// |
| /// This example produces the following output: |
| /// |
| /// ```console |
| /// thread 'main' panicked at src/error.rs:34:40: |
| /// called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here! |
| /// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace |
| /// ``` |
| /// |
| /// ## Return from `main` |
| /// |
| /// `Report` also implements `From` for all types that implement [`Error`]; this when combined with |
| /// the `Debug` output means `Report` is an ideal starting place for formatting errors returned |
| /// from `main`. |
| /// |
| /// ```should_panic |
| /// #![feature(error_reporter)] |
| /// use std::error::Report; |
| /// # use std::error::Error; |
| /// # use std::fmt; |
| /// # #[derive(Debug)] |
| /// # struct SuperError { |
| /// # source: SuperErrorSideKick, |
| /// # } |
| /// # impl fmt::Display for SuperError { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperError is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperError { |
| /// # fn source(&self) -> Option<&(dyn Error + 'static)> { |
| /// # Some(&self.source) |
| /// # } |
| /// # } |
| /// # #[derive(Debug)] |
| /// # struct SuperErrorSideKick; |
| /// # impl fmt::Display for SuperErrorSideKick { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperErrorSideKick is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperErrorSideKick {} |
| /// # fn get_super_error() -> Result<(), SuperError> { |
| /// # Err(SuperError { source: SuperErrorSideKick }) |
| /// # } |
| /// |
| /// fn main() -> Result<(), Report<SuperError>> { |
| /// get_super_error()?; |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// This example produces the following output: |
| /// |
| /// ```console |
| /// Error: SuperError is here!: SuperErrorSideKick is here! |
| /// ``` |
| /// |
| /// **Note**: `Report`s constructed via `?` and `From` will be configured to use the single line |
| /// output format. If you want to make sure your `Report`s are pretty printed and include backtrace |
| /// you will need to manually convert and enable those flags. |
| /// |
| /// ```should_panic |
| /// #![feature(error_reporter)] |
| /// use std::error::Report; |
| /// # use std::error::Error; |
| /// # use std::fmt; |
| /// # #[derive(Debug)] |
| /// # struct SuperError { |
| /// # source: SuperErrorSideKick, |
| /// # } |
| /// # impl fmt::Display for SuperError { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperError is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperError { |
| /// # fn source(&self) -> Option<&(dyn Error + 'static)> { |
| /// # Some(&self.source) |
| /// # } |
| /// # } |
| /// # #[derive(Debug)] |
| /// # struct SuperErrorSideKick; |
| /// # impl fmt::Display for SuperErrorSideKick { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperErrorSideKick is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperErrorSideKick {} |
| /// # fn get_super_error() -> Result<(), SuperError> { |
| /// # Err(SuperError { source: SuperErrorSideKick }) |
| /// # } |
| /// |
| /// fn main() -> Result<(), Report<SuperError>> { |
| /// get_super_error() |
| /// .map_err(Report::from) |
| /// .map_err(|r| r.pretty(true).show_backtrace(true))?; |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// This example produces the following output: |
| /// |
| /// ```console |
| /// Error: SuperError is here! |
| /// |
| /// Caused by: |
| /// SuperErrorSideKick is here! |
| /// ``` |
| #[unstable(feature = "error_reporter", issue = "90172")] |
| pub struct Report<E = Box<dyn Error>> { |
| /// The error being reported. |
| error: E, |
| /// Whether a backtrace should be included as part of the report. |
| show_backtrace: bool, |
| /// Whether the report should be pretty-printed. |
| pretty: bool, |
| } |
| |
| impl<E> Report<E> |
| where |
| Report<E>: From<E>, |
| { |
| /// Creates a new `Report` from an input error. |
| #[unstable(feature = "error_reporter", issue = "90172")] |
| pub fn new(error: E) -> Report<E> { |
| Self::from(error) |
| } |
| } |
| |
| impl<E> Report<E> { |
| /// Enable pretty-printing the report across multiple lines. |
| /// |
| /// # Examples |
| /// |
| /// ```rust |
| /// #![feature(error_reporter)] |
| /// use std::error::Report; |
| /// # use std::error::Error; |
| /// # use std::fmt; |
| /// # #[derive(Debug)] |
| /// # struct SuperError { |
| /// # source: SuperErrorSideKick, |
| /// # } |
| /// # impl fmt::Display for SuperError { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperError is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperError { |
| /// # fn source(&self) -> Option<&(dyn Error + 'static)> { |
| /// # Some(&self.source) |
| /// # } |
| /// # } |
| /// # #[derive(Debug)] |
| /// # struct SuperErrorSideKick; |
| /// # impl fmt::Display for SuperErrorSideKick { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperErrorSideKick is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperErrorSideKick {} |
| /// |
| /// let error = SuperError { source: SuperErrorSideKick }; |
| /// let report = Report::new(error).pretty(true); |
| /// eprintln!("Error: {report:?}"); |
| /// ``` |
| /// |
| /// This example produces the following output: |
| /// |
| /// ```console |
| /// Error: SuperError is here! |
| /// |
| /// Caused by: |
| /// SuperErrorSideKick is here! |
| /// ``` |
| /// |
| /// When there are multiple source errors the causes will be numbered in order of iteration |
| /// starting from the outermost error. |
| /// |
| /// ```rust |
| /// #![feature(error_reporter)] |
| /// use std::error::Report; |
| /// # use std::error::Error; |
| /// # use std::fmt; |
| /// # #[derive(Debug)] |
| /// # struct SuperError { |
| /// # source: SuperErrorSideKick, |
| /// # } |
| /// # impl fmt::Display for SuperError { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperError is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperError { |
| /// # fn source(&self) -> Option<&(dyn Error + 'static)> { |
| /// # Some(&self.source) |
| /// # } |
| /// # } |
| /// # #[derive(Debug)] |
| /// # struct SuperErrorSideKick { |
| /// # source: SuperErrorSideKickSideKick, |
| /// # } |
| /// # impl fmt::Display for SuperErrorSideKick { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperErrorSideKick is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperErrorSideKick { |
| /// # fn source(&self) -> Option<&(dyn Error + 'static)> { |
| /// # Some(&self.source) |
| /// # } |
| /// # } |
| /// # #[derive(Debug)] |
| /// # struct SuperErrorSideKickSideKick; |
| /// # impl fmt::Display for SuperErrorSideKickSideKick { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperErrorSideKickSideKick is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperErrorSideKickSideKick { } |
| /// |
| /// let source = SuperErrorSideKickSideKick; |
| /// let source = SuperErrorSideKick { source }; |
| /// let error = SuperError { source }; |
| /// let report = Report::new(error).pretty(true); |
| /// eprintln!("Error: {report:?}"); |
| /// ``` |
| /// |
| /// This example produces the following output: |
| /// |
| /// ```console |
| /// Error: SuperError is here! |
| /// |
| /// Caused by: |
| /// 0: SuperErrorSideKick is here! |
| /// 1: SuperErrorSideKickSideKick is here! |
| /// ``` |
| #[unstable(feature = "error_reporter", issue = "90172")] |
| pub fn pretty(mut self, pretty: bool) -> Self { |
| self.pretty = pretty; |
| self |
| } |
| |
| /// Display backtrace if available when using pretty output format. |
| /// |
| /// # Examples |
| /// |
| /// **Note**: Report will search for the first `Backtrace` it can find starting from the |
| /// outermost error. In this example it will display the backtrace from the second error in the |
| /// sources, `SuperErrorSideKick`. |
| /// |
| /// ```rust |
| /// #![feature(error_reporter)] |
| /// #![feature(error_generic_member_access)] |
| /// # use std::error::Error; |
| /// # use std::fmt; |
| /// use std::error::Request; |
| /// use std::error::Report; |
| /// use std::backtrace::Backtrace; |
| /// |
| /// # #[derive(Debug)] |
| /// # struct SuperError { |
| /// # source: SuperErrorSideKick, |
| /// # } |
| /// # impl fmt::Display for SuperError { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperError is here!") |
| /// # } |
| /// # } |
| /// # impl Error for SuperError { |
| /// # fn source(&self) -> Option<&(dyn Error + 'static)> { |
| /// # Some(&self.source) |
| /// # } |
| /// # } |
| /// #[derive(Debug)] |
| /// struct SuperErrorSideKick { |
| /// backtrace: Backtrace, |
| /// } |
| /// |
| /// impl SuperErrorSideKick { |
| /// fn new() -> SuperErrorSideKick { |
| /// SuperErrorSideKick { backtrace: Backtrace::force_capture() } |
| /// } |
| /// } |
| /// |
| /// impl Error for SuperErrorSideKick { |
| /// fn provide<'a>(&'a self, request: &mut Request<'a>) { |
| /// request.provide_ref::<Backtrace>(&self.backtrace); |
| /// } |
| /// } |
| /// |
| /// // The rest of the example is unchanged ... |
| /// # impl fmt::Display for SuperErrorSideKick { |
| /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| /// # write!(f, "SuperErrorSideKick is here!") |
| /// # } |
| /// # } |
| /// |
| /// let source = SuperErrorSideKick::new(); |
| /// let error = SuperError { source }; |
| /// let report = Report::new(error).pretty(true).show_backtrace(true); |
| /// eprintln!("Error: {report:?}"); |
| /// ``` |
| /// |
| /// This example produces something similar to the following output: |
| /// |
| /// ```console |
| /// Error: SuperError is here! |
| /// |
| /// Caused by: |
| /// SuperErrorSideKick is here! |
| /// |
| /// Stack backtrace: |
| /// 0: rust_out::main::_doctest_main_src_error_rs_1158_0::SuperErrorSideKick::new |
| /// 1: rust_out::main::_doctest_main_src_error_rs_1158_0 |
| /// 2: rust_out::main |
| /// 3: core::ops::function::FnOnce::call_once |
| /// 4: std::sys::backtrace::__rust_begin_short_backtrace |
| /// 5: std::rt::lang_start::{{closure}} |
| /// 6: std::panicking::try |
| /// 7: std::rt::lang_start_internal |
| /// 8: std::rt::lang_start |
| /// 9: main |
| /// 10: __libc_start_main |
| /// 11: _start |
| /// ``` |
| #[unstable(feature = "error_reporter", issue = "90172")] |
| pub fn show_backtrace(mut self, show_backtrace: bool) -> Self { |
| self.show_backtrace = show_backtrace; |
| self |
| } |
| } |
| |
| impl<E> Report<E> |
| where |
| E: Error, |
| { |
| fn backtrace(&self) -> Option<&Backtrace> { |
| // have to grab the backtrace on the first error directly since that error may not be |
| // 'static |
| let backtrace = request_ref(&self.error); |
| let backtrace = backtrace.or_else(|| { |
| self.error |
| .source() |
| .map(|source| source.sources().find_map(|source| request_ref(source))) |
| .flatten() |
| }); |
| backtrace |
| } |
| |
| /// Format the report as a single line. |
| #[unstable(feature = "error_reporter", issue = "90172")] |
| fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "{}", self.error)?; |
| |
| let sources = self.error.source().into_iter().flat_map(<dyn Error>::sources); |
| |
| for cause in sources { |
| write!(f, ": {cause}")?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Format the report as multiple lines, with each error cause on its own line. |
| #[unstable(feature = "error_reporter", issue = "90172")] |
| fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| let error = &self.error; |
| |
| write!(f, "{error}")?; |
| |
| if let Some(cause) = error.source() { |
| write!(f, "\n\nCaused by:")?; |
| |
| let multiple = cause.source().is_some(); |
| |
| for (ind, error) in cause.sources().enumerate() { |
| writeln!(f)?; |
| let mut indented = Indented { inner: f }; |
| if multiple { |
| write!(indented, "{ind: >4}: {error}")?; |
| } else { |
| write!(indented, " {error}")?; |
| } |
| } |
| } |
| |
| if self.show_backtrace { |
| if let Some(backtrace) = self.backtrace() { |
| write!(f, "\n\nStack backtrace:\n{}", backtrace.to_string().trim_end())?; |
| } |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| #[unstable(feature = "error_reporter", issue = "90172")] |
| impl<E> From<E> for Report<E> |
| where |
| E: Error, |
| { |
| fn from(error: E) -> Self { |
| Report { error, show_backtrace: false, pretty: false } |
| } |
| } |
| |
| #[unstable(feature = "error_reporter", issue = "90172")] |
| impl<E> fmt::Display for Report<E> |
| where |
| E: Error, |
| { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) } |
| } |
| } |
| |
| // This type intentionally outputs the same format for `Display` and `Debug`for |
| // situations where you unwrap a `Report` or return it from main. |
| #[unstable(feature = "error_reporter", issue = "90172")] |
| impl<E> fmt::Debug for Report<E> |
| where |
| Report<E>: fmt::Display, |
| { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| fmt::Display::fmt(self, f) |
| } |
| } |
| |
| /// Wrapper type for indenting the inner source. |
| struct Indented<'a, D> { |
| inner: &'a mut D, |
| } |
| |
| impl<T> Write for Indented<'_, T> |
| where |
| T: Write, |
| { |
| fn write_str(&mut self, s: &str) -> fmt::Result { |
| for (i, line) in s.split('\n').enumerate() { |
| if i > 0 { |
| self.inner.write_char('\n')?; |
| self.inner.write_str(" ")?; |
| } |
| |
| self.inner.write_str(line)?; |
| } |
| |
| Ok(()) |
| } |
| } |