Skip to content
211 changes: 207 additions & 4 deletions library/std/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::backtrace::Backtrace;
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;
Expand Down Expand Up @@ -63,7 +63,7 @@ pub trait Error: Debug + Display {
///
/// #[derive(Debug)]
/// struct SuperError {
/// side: SuperErrorSideKick,
/// source: SuperErrorSideKick,
/// }
///
/// impl fmt::Display for SuperError {
Expand All @@ -74,7 +74,7 @@ pub trait Error: Debug + Display {
///
/// impl Error for SuperError {
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
/// Some(&self.side)
/// Some(&self.source)
/// }
/// }
///
Expand All @@ -90,7 +90,7 @@ pub trait Error: Debug + Display {
/// impl Error for SuperErrorSideKick {}
///
/// fn get_super_error() -> Result<(), SuperError> {
/// Err(SuperError { side: SuperErrorSideKick })
/// Err(SuperError { source: SuperErrorSideKick })
/// }
///
/// fn main() {
Expand Down Expand Up @@ -807,3 +807,206 @@ impl dyn Error + Send + Sync {
})
}
}

/// An error reporter that exposes the entire error chain for printing.
/// It also exposes 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)]
/// #![feature(negative_impls)]
///
/// use std::error::{Error, Report};
/// use std::fmt;
///
/// #[derive(Debug)]
/// struct SuperError<'a> {
/// side: &'a str,
/// }
///
/// impl<'a> fmt::Display for SuperError<'a> {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "SuperError is here: {}", self.side)
/// }
/// }
///
/// impl<'a> Error for SuperError<'a> {}
///
/// fn main() {
/// let msg = String::from("Huzzah!");
/// let error = SuperError { side: &msg };
/// let report = Report::new(&error).pretty(true);
///
/// println!("{}", report);
/// }
/// ```

#[unstable(feature = "error_reporter", issue = "90172")]
pub struct Report<E> {
/// 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
E: Error,
{
/// Create a new `Report` from an input error.
#[unstable(feature = "error_reporter", issue = "90172")]
pub fn new(error: E) -> Report<E> {
Report { error, show_backtrace: false, pretty: false }
}

/// Enable pretty-printing the report.
#[unstable(feature = "error_reporter", issue = "90172")]
pub fn pretty(mut self, pretty: bool) -> Self {
self.pretty = pretty;
self
}

/// Enable showing a backtrace for the report.
#[unstable(feature = "error_reporter", issue = "90172")]
pub fn show_backtrace(mut self, show_backtrace: bool) -> Self {
self.show_backtrace = show_backtrace;
self
}

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,
number: if multiple { Some(ind) } else { None },
started: false,
};
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::new(error)
}
}

#[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
E: Error,
{
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,
number: Option<usize>,
started: bool,
}

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 !self.started {
self.started = true;
match self.number {
Some(number) => write!(self.inner, "{: >5}: ", number)?,
None => self.inner.write_str(" ")?,
}
} else if i > 0 {
self.inner.write_char('\n')?;
if self.number.is_some() {
self.inner.write_str(" ")?;
} else {
self.inner.write_str(" ")?;
}
}

self.inner.write_str(line)?;
}

Ok(())
}
}
Loading