use crate::borrow::Cow;
use crate::cell;
use crate::char;
-use crate::fmt::{self, Debug, Display};
+use crate::fmt::{self, Debug, Display, Write};
use crate::mem::transmute;
use crate::num;
use crate::str;
///
/// #[derive(Debug)]
/// struct SuperError {
- /// side: SuperErrorSideKick,
+ /// source: SuperErrorSideKick,
/// }
///
/// impl fmt::Display for SuperError {
///
/// impl Error for SuperError {
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
- /// Some(&self.side)
+ /// Some(&self.source)
/// }
/// }
///
/// impl Error for SuperErrorSideKick {}
///
/// fn get_super_error() -> Result<(), SuperError> {
- /// Err(SuperError { side: SuperErrorSideKick })
+ /// Err(SuperError { source: SuperErrorSideKick })
/// }
///
/// fn main() {
})
}
}
+
+/// An error reporter that print's an error and its sources.
+///
+/// Report also exposes configuration options for formatting the error chain, either entirely on a
+/// single line, or in multi-line format with each cause in the error chain on a new line.
+///
+/// `Report` only requires that the wrapped error implements `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 'called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!', src/error.rs:34:40
+/// 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> {
+/// 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> {
+/// 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>,
+{
+ /// Create 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
+ /// chain, `SuperErrorSideKick`.
+ ///
+ /// ```rust
+ /// #![feature(error_reporter)]
+ /// #![feature(backtrace)]
+ /// # use std::error::Error;
+ /// # use std::fmt;
+ /// 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 backtrace(&self) -> Option<&Backtrace> {
+ /// Some(&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_common::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 = self.error.backtrace();
+ let backtrace = backtrace.or_else(|| {
+ self.error
+ .source()
+ .map(|source| source.chain().find_map(|source| source.backtrace()))
+ .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>::chain);
+
+ 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.chain().enumerate() {
+ writeln!(f)?;
+ let mut indented = Indented { inner: f };
+ if multiple {
+ write!(indented, "{: >4}: {}", ind, error)?;
+ } else {
+ write!(indented, " {}", error)?;
+ }
+ }
+ }
+
+ if self.show_backtrace {
+ let backtrace = self.backtrace();
+
+ if let Some(backtrace) = backtrace {
+ let backtrace = backtrace.to_string();
+
+ f.write_str("\n\nStack backtrace:\n")?;
+ f.write_str(backtrace.trim_end())?;
+ }
+ }
+
+ Ok(())
+ }
+}
+
+impl Report<Box<dyn 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 = self.error.backtrace();
+ let backtrace = backtrace.or_else(|| {
+ self.error
+ .source()
+ .map(|source| source.chain().find_map(|source| source.backtrace()))
+ .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>::chain);
+
+ 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.chain().enumerate() {
+ writeln!(f)?;
+ let mut indented = Indented { inner: f };
+ if multiple {
+ write!(indented, "{: >4}: {}", ind, error)?;
+ } else {
+ write!(indented, " {}", error)?;
+ }
+ }
+ }
+
+ if self.show_backtrace {
+ let backtrace = self.backtrace();
+
+ if let Some(backtrace) = backtrace {
+ let backtrace = backtrace.to_string();
+
+ f.write_str("\n\nStack backtrace:\n")?;
+ f.write_str(backtrace.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<'a, E> From<E> for Report<Box<dyn Error + 'a>>
+where
+ E: Error + 'a,
+{
+ fn from(error: E) -> Self {
+ let error = box error;
+ 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) }
+ }
+}
+
+#[unstable(feature = "error_reporter", issue = "90172")]
+impl fmt::Display for Report<Box<dyn 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(())
+ }
+}
Err(e) => assert_eq!(*e.downcast::<A>().unwrap(), A),
}
}
+
+use crate::backtrace::Backtrace;
+use crate::error::Report;
+
+#[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 {}
+
+#[test]
+fn single_line_formatting() {
+ let error = SuperError { source: SuperErrorSideKick };
+ let report = Report::new(&error);
+ let actual = report.to_string();
+ let expected = String::from("SuperError is here!: SuperErrorSideKick is here!");
+
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn multi_line_formatting() {
+ let error = SuperError { source: SuperErrorSideKick };
+ let report = Report::new(&error).pretty(true);
+ let actual = report.to_string();
+ let expected = String::from(
+ "\
+SuperError is here!
+
+Caused by:
+ SuperErrorSideKick is here!",
+ );
+
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn error_with_no_sources_formats_single_line_correctly() {
+ let report = Report::new(SuperErrorSideKick);
+ let actual = report.to_string();
+ let expected = String::from("SuperErrorSideKick is here!");
+
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn error_with_no_sources_formats_multi_line_correctly() {
+ let report = Report::new(SuperErrorSideKick).pretty(true);
+ let actual = report.to_string();
+ let expected = String::from("SuperErrorSideKick is here!");
+
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn error_with_backtrace_outputs_correctly_with_one_source() {
+ let trace = Backtrace::force_capture();
+ let expected = format!(
+ "\
+The source of the error
+
+Caused by:
+ Error with backtrace
+
+Stack backtrace:
+{}",
+ trace
+ );
+ let error = GenericError::new("Error with backtrace");
+ let mut error = GenericError::new_with_source("The source of the error", error);
+ error.backtrace = Some(trace);
+ let report = Report::new(error).pretty(true).show_backtrace(true);
+
+ println!("Error: {}", report);
+ assert_eq!(expected.trim_end(), report.to_string());
+}
+
+#[test]
+fn error_with_backtrace_outputs_correctly_with_two_sources() {
+ let trace = Backtrace::force_capture();
+ let expected = format!(
+ "\
+Error with two sources
+
+Caused by:
+ 0: The source of the error
+ 1: Error with backtrace
+
+Stack backtrace:
+{}",
+ trace
+ );
+ let mut error = GenericError::new("Error with backtrace");
+ error.backtrace = Some(trace);
+ let error = GenericError::new_with_source("The source of the error", error);
+ let error = GenericError::new_with_source("Error with two sources", error);
+ let report = Report::new(error).pretty(true).show_backtrace(true);
+
+ println!("Error: {}", report);
+ assert_eq!(expected.trim_end(), report.to_string());
+}
+
+#[derive(Debug)]
+struct GenericError<D> {
+ message: D,
+ backtrace: Option<Backtrace>,
+ source: Option<Box<dyn Error + 'static>>,
+}
+
+impl<D> GenericError<D> {
+ fn new(message: D) -> GenericError<D> {
+ Self { message, backtrace: None, source: None }
+ }
+
+ fn new_with_source<E>(message: D, source: E) -> GenericError<D>
+ where
+ E: Error + 'static,
+ {
+ let source: Box<dyn Error + 'static> = Box::new(source);
+ let source = Some(source);
+ GenericError { message, backtrace: None, source }
+ }
+}
+
+impl<D> fmt::Display for GenericError<D>
+where
+ D: fmt::Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(&self.message, f)
+ }
+}
+
+impl<D> Error for GenericError<D>
+where
+ D: fmt::Debug + fmt::Display,
+{
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ self.source.as_deref()
+ }
+
+ fn backtrace(&self) -> Option<&Backtrace> {
+ self.backtrace.as_ref()
+ }
+}
+
+#[test]
+fn error_formats_single_line_with_rude_display_impl() {
+ #[derive(Debug)]
+ struct MyMessage;
+
+ impl fmt::Display for MyMessage {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("line 1\nline 2")?;
+ f.write_str("\nline 3\nline 4\n")?;
+ f.write_str("line 5\nline 6")?;
+ Ok(())
+ }
+ }
+
+ let error = GenericError::new(MyMessage);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let report = Report::new(error);
+ let expected = "\
+line 1
+line 2
+line 3
+line 4
+line 5
+line 6: line 1
+line 2
+line 3
+line 4
+line 5
+line 6: line 1
+line 2
+line 3
+line 4
+line 5
+line 6: line 1
+line 2
+line 3
+line 4
+line 5
+line 6";
+
+ let actual = report.to_string();
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn error_formats_multi_line_with_rude_display_impl() {
+ #[derive(Debug)]
+ struct MyMessage;
+
+ impl fmt::Display for MyMessage {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("line 1\nline 2")?;
+ f.write_str("\nline 3\nline 4\n")?;
+ f.write_str("line 5\nline 6")?;
+ Ok(())
+ }
+ }
+
+ let error = GenericError::new(MyMessage);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let report = Report::new(error).pretty(true);
+ let expected = "line 1
+line 2
+line 3
+line 4
+line 5
+line 6
+
+Caused by:
+ 0: line 1
+ line 2
+ line 3
+ line 4
+ line 5
+ line 6
+ 1: line 1
+ line 2
+ line 3
+ line 4
+ line 5
+ line 6
+ 2: line 1
+ line 2
+ line 3
+ line 4
+ line 5
+ line 6";
+
+ let actual = report.to_string();
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn errors_that_start_with_newline_formats_correctly() {
+ #[derive(Debug)]
+ struct MyMessage;
+
+ impl fmt::Display for MyMessage {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("\nThe message\n")
+ }
+ }
+
+ let error = GenericError::new(MyMessage);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let report = Report::new(error).pretty(true);
+ let expected = "
+The message
+
+
+Caused by:
+ 0: \
+\n The message
+ \
+\n 1: \
+\n The message
+ ";
+
+ let actual = report.to_string();
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn errors_with_multiple_writes_on_same_line_dont_insert_erroneous_newlines() {
+ #[derive(Debug)]
+ struct MyMessage;
+
+ impl fmt::Display for MyMessage {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("The message")?;
+ f.write_str(" goes on")?;
+ f.write_str(" and on.")
+ }
+ }
+
+ let error = GenericError::new(MyMessage);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let report = Report::new(error).pretty(true);
+ let expected = "\
+The message goes on and on.
+
+Caused by:
+ 0: The message goes on and on.
+ 1: The message goes on and on.";
+
+ let actual = report.to_string();
+ println!("{}", actual);
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn errors_with_string_interpolation_formats_correctly() {
+ #[derive(Debug)]
+ struct MyMessage(usize);
+
+ impl fmt::Display for MyMessage {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Got an error code: ({}). ", self.0)?;
+ write!(f, "What would you like to do in response?")
+ }
+ }
+
+ let error = GenericError::new(MyMessage(10));
+ let error = GenericError::new_with_source(MyMessage(20), error);
+ let report = Report::new(error).pretty(true);
+ let expected = "\
+Got an error code: (20). What would you like to do in response?
+
+Caused by:
+ Got an error code: (10). What would you like to do in response?";
+ let actual = report.to_string();
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn empty_lines_mid_message() {
+ #[derive(Debug)]
+ struct MyMessage;
+
+ impl fmt::Display for MyMessage {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("line 1\n\nline 2")
+ }
+ }
+
+ let error = GenericError::new(MyMessage);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let report = Report::new(error).pretty(true);
+ let expected = "\
+line 1
+
+line 2
+
+Caused by:
+ 0: line 1
+ \
+\n line 2
+ 1: line 1
+ \
+\n line 2";
+
+ let actual = report.to_string();
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn only_one_source() {
+ #[derive(Debug)]
+ struct MyMessage;
+
+ impl fmt::Display for MyMessage {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("line 1\nline 2")
+ }
+ }
+
+ let error = GenericError::new(MyMessage);
+ let error = GenericError::new_with_source(MyMessage, error);
+ let report = Report::new(error).pretty(true);
+ let expected = "\
+line 1
+line 2
+
+Caused by:
+ line 1
+ line 2";
+
+ let actual = report.to_string();
+ assert_eq!(expected, actual);
+}