Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add error::Report type #90174

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions library/std/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,3 +807,186 @@ 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,
yaahc marked this conversation as resolved.
Show resolved Hide resolved
/// 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,
yaahc marked this conversation as resolved.
Show resolved Hide resolved
/// }
///
/// 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> {}
///
/// // Note that the error doesn't need to be `Send` or `Sync`.
/// impl<'a> !Send for SuperError<'a> {}
/// impl<'a> !Sync for SuperError<'a> {}
yaahc marked this conversation as resolved.
Show resolved Hide resolved
///
/// 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
}

/// 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)?;

if multiple {
write!(f, "{: >4}: {}", ind, Indented { source: error })?;
} else {
write!(f, " {}", error)?;
}
}
}

if self.show_backtrace {
let backtrace = error.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
yaahc marked this conversation as resolved.
Show resolved Hide resolved
// 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<D> {
source: D,
}
yaahc marked this conversation as resolved.
Show resolved Hide resolved

impl<D> fmt::Display for Indented<D>
where
D: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let source = self.source.to_string();

for (ind, line) in source.trim().split('\n').filter(|l| !l.is_empty()).enumerate() {
yaahc marked this conversation as resolved.
Show resolved Hide resolved
if ind > 0 {
write!(f, "\n {}", line)?;
} else {
write!(f, "{}", line)?;
}
yaahc marked this conversation as resolved.
Show resolved Hide resolved
}

Ok(())
}
}
Loading