Update report output and fix examples

This commit is contained in:
Jane Lusby 2021-12-16 14:06:28 -08:00
parent 1386a15529
commit 4420cc33d6
2 changed files with 245 additions and 82 deletions

View File

@ -808,9 +808,11 @@ impl dyn Error + Send + Sync {
} }
} }
/// An error reporter that exposes the entire error chain for printing. /// An error reporter that print's an error and its sources.
/// 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 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 /// `Report` only requires that the wrapped error implements `Error`. It doesn't require that the
/// wrapped error be `Send`, `Sync`, or `'static`. /// wrapped error be `Send`, `Sync`, or `'static`.
/// ///
@ -818,33 +820,51 @@ impl dyn Error + Send + Sync {
/// ///
/// ```rust /// ```rust
/// #![feature(error_reporter)] /// #![feature(error_reporter)]
/// #![feature(negative_impls)]
///
/// use std::error::{Error, Report}; /// use std::error::{Error, Report};
/// use std::fmt; /// use std::fmt;
/// ///
/// #[derive(Debug)] /// #[derive(Debug)]
/// struct SuperError<'a> { /// struct SuperError {
/// side: &'a str, /// source: SuperErrorSideKick,
/// } /// }
/// ///
/// impl<'a> fmt::Display for SuperError<'a> { /// impl fmt::Display for SuperError {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "SuperError is here: {}", self.side) /// write!(f, "SuperError is here!")
/// } /// }
/// } /// }
/// ///
/// impl<'a> Error for SuperError<'a> {} /// 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() { /// fn main() {
/// let msg = String::from("Huzzah!"); /// match get_super_error() {
/// let error = SuperError { side: &msg }; /// Err(e) => {
/// let report = Report::new(&error).pretty(true); /// let report = Report::new(e).pretty(true);
/// /// println!("Error: {}", report);
/// println!("{}", report); /// }
/// _ => println!("No error"),
/// }
/// } /// }
/// ``` /// ```
#[unstable(feature = "error_reporter", issue = "90172")] #[unstable(feature = "error_reporter", issue = "90172")]
pub struct Report<E> { pub struct Report<E> {
/// The error being reported. /// The error being reported.
@ -865,14 +885,129 @@ where
Report { error, show_backtrace: false, pretty: false } Report { error, show_backtrace: false, pretty: false }
} }
/// Enable pretty-printing the report. /// 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!
/// ```
#[unstable(feature = "error_reporter", issue = "90172")] #[unstable(feature = "error_reporter", issue = "90172")]
pub fn pretty(mut self, pretty: bool) -> Self { pub fn pretty(mut self, pretty: bool) -> Self {
self.pretty = pretty; self.pretty = pretty;
self self
} }
/// Enable showing a backtrace for the report. /// Display backtrace if available when using pretty output format.
///
/// # Examples
///
/// ```rust
/// #![feature(error_reporter)]
/// #![feature(backtrace)]
/// use std::error::{Error, Report};
/// use std::backtrace::Backtrace;
/// 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 {
/// backtrace: Backtrace,
/// }
///
/// impl fmt::Display for SuperErrorSideKick {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "SuperErrorSideKick is here!")
/// }
/// }
///
/// impl Error for SuperErrorSideKick {
/// fn backtrace(&self) -> Option<&Backtrace> {
/// Some(&self.backtrace)
/// }
/// }
///
/// let source = SuperErrorSideKick { backtrace: Backtrace::force_capture() };
/// 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_943_0
/// 1: rust_out::main
/// 2: core::ops::function::FnOnce::call_once
/// 3: std::sys_common::backtrace::__rust_begin_short_backtrace
/// 4: std::rt::lang_start::{{closure}}
/// 5: std::panicking::try
/// 6: std::rt::lang_start_internal
/// 7: std::rt::lang_start
/// 8: main
/// 9: __libc_start_main
/// 10: _start
/// ```
#[unstable(feature = "error_reporter", issue = "90172")] #[unstable(feature = "error_reporter", issue = "90172")]
pub fn show_backtrace(mut self, show_backtrace: bool) -> Self { pub fn show_backtrace(mut self, show_backtrace: bool) -> Self {
self.show_backtrace = show_backtrace; self.show_backtrace = show_backtrace;
@ -922,10 +1057,12 @@ where
writeln!(f)?; writeln!(f)?;
let mut indented = Indented { let mut indented = Indented {
inner: f, inner: f,
number: if multiple { Some(ind) } else { None },
started: false,
}; };
write!(indented, "{}", error)?; if multiple {
write!(indented, "{: >4}: {}", ind, error)?;
} else {
write!(indented, " {}", error)?;
}
} }
} }
@ -979,8 +1116,6 @@ where
/// Wrapper type for indenting the inner source. /// Wrapper type for indenting the inner source.
struct Indented<'a, D> { struct Indented<'a, D> {
inner: &'a mut D, inner: &'a mut D,
number: Option<usize>,
started: bool,
} }
impl<T> Write for Indented<'_, T> impl<T> Write for Indented<'_, T>
@ -989,19 +1124,9 @@ where
{ {
fn write_str(&mut self, s: &str) -> fmt::Result { fn write_str(&mut self, s: &str) -> fmt::Result {
for (i, line) in s.split('\n').enumerate() { for (i, line) in s.split('\n').enumerate() {
if !self.started { if i > 0 {
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')?; self.inner.write_char('\n')?;
if self.number.is_some() { self.inner.write_str(" ")?;
self.inner.write_str(" ")?;
} else {
self.inner.write_str(" ")?;
}
} }
self.inner.write_str(line)?; self.inner.write_str(line)?;

View File

@ -82,8 +82,11 @@ fn multi_line_formatting() {
let error = SuperError { source: SuperErrorSideKick }; let error = SuperError { source: SuperErrorSideKick };
let report = Report::new(&error).pretty(true); let report = Report::new(&error).pretty(true);
let actual = report.to_string(); let actual = report.to_string();
let expected = let expected = String::from("\
String::from("SuperError is here!\n\nCaused by:\n SuperErrorSideKick is here!"); SuperError is here!
Caused by:
SuperErrorSideKick is here!");
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }
@ -109,10 +112,11 @@ fn error_with_no_sources_formats_multi_line_correctly() {
#[test] #[test]
fn error_with_backtrace_outputs_correctly_with_one_source() { fn error_with_backtrace_outputs_correctly_with_one_source() {
let trace = Backtrace::force_capture(); let trace = Backtrace::force_capture();
let expected = format!("The source of the error let expected = format!("\
The source of the error
Caused by: Caused by:
Error with backtrace Error with backtrace
Stack backtrace: Stack backtrace:
{}", trace); {}", trace);
@ -129,11 +133,12 @@ Stack backtrace:
#[test] #[test]
fn error_with_backtrace_outputs_correctly_with_two_sources() { fn error_with_backtrace_outputs_correctly_with_two_sources() {
let trace = Backtrace::force_capture(); let trace = Backtrace::force_capture();
let expected = format!("Error with two sources let expected = format!("\
Error with two sources
Caused by: Caused by:
0: The source of the error 0: The source of the error
1: Error with backtrace 1: Error with backtrace
Stack backtrace: Stack backtrace:
{}", trace); {}", trace);
@ -211,7 +216,8 @@ fn error_formats_single_line_with_rude_display_impl() {
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 error = GenericError::new_with_source(MyMessage, error);
let report = Report::new(error); let report = Report::new(error);
let expected = r#"line 1 let expected = "\
line 1
line 2 line 2
line 3 line 3
line 4 line 4
@ -231,7 +237,7 @@ line 2
line 3 line 3
line 4 line 4
line 5 line 5
line 6"#; line 6";
let actual = report.to_string(); let actual = report.to_string();
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -256,7 +262,7 @@ fn error_formats_multi_line_with_rude_display_impl() {
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 error = GenericError::new_with_source(MyMessage, error);
let report = Report::new(error).pretty(true); let report = Report::new(error).pretty(true);
let expected = r#"line 1 let expected = "line 1
line 2 line 2
line 3 line 3
line 4 line 4
@ -264,24 +270,24 @@ line 5
line 6 line 6
Caused by: Caused by:
0: line 1 0: line 1
line 2 line 2
line 3 line 3
line 4 line 4
line 5 line 5
line 6 line 6
1: line 1 1: line 1
line 2 line 2
line 3 line 3
line 4 line 4
line 5 line 5
line 6 line 6
2: line 1 2: line 1
line 2 line 2
line 3 line 3
line 4 line 4
line 5 line 5
line 6"#; line 6";
let actual = report.to_string(); let actual = report.to_string();
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -302,22 +308,51 @@ fn errors_that_start_with_newline_formats_correctly() {
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 error = GenericError::new_with_source(MyMessage, error);
let report = Report::new(error).pretty(true); let report = Report::new(error).pretty(true);
let expected = r#" let expected = "
The message The message
Caused by: Caused by:
0: 0:
The message The message
1: 1:
The message The message
"#; ";
let actual = report.to_string(); let actual = report.to_string();
assert_eq!(expected, actual); 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] #[test]
fn errors_with_string_interpolation_formats_correctly() { fn errors_with_string_interpolation_formats_correctly() {
#[derive(Debug)] #[derive(Debug)]
@ -333,10 +368,11 @@ fn errors_with_string_interpolation_formats_correctly() {
let error = GenericError::new(MyMessage(10)); let error = GenericError::new(MyMessage(10));
let error = GenericError::new_with_source(MyMessage(20), error); let error = GenericError::new_with_source(MyMessage(20), error);
let report = Report::new(error).pretty(true); let report = Report::new(error).pretty(true);
let expected = r#"Got an error code: (20). What would you like to do in response? let expected = "\
Got an error code: (20). What would you like to do in response?
Caused by: Caused by:
Got an error code: (10). What would you like to do in response?"#; Got an error code: (10). What would you like to do in response?";
let actual = report.to_string(); let actual = report.to_string();
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }
@ -356,17 +392,18 @@ fn empty_lines_mid_message() {
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 error = GenericError::new_with_source(MyMessage, error);
let report = Report::new(error).pretty(true); let report = Report::new(error).pretty(true);
let expected = r#"line 1 let expected = "\
line 1
line 2 line 2
Caused by: Caused by:
0: line 1 0: line 1
line 2 line 2
1: line 1 1: line 1
line 2"#; line 2";
let actual = report.to_string(); let actual = report.to_string();
assert_eq!(expected, actual); assert_eq!(expected, actual);
@ -386,12 +423,13 @@ fn only_one_source() {
let error = GenericError::new(MyMessage); 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 report = Report::new(error).pretty(true);
let expected = r#"line 1 let expected = "\
line 1
line 2 line 2
Caused by: Caused by:
line 1 line 1
line 2"#; line 2";
let actual = report.to_string(); let actual = report.to_string();
assert_eq!(expected, actual); assert_eq!(expected, actual);