Skip to content
This repository was archived by the owner on Aug 16, 2021. It is now read-only.

chain_display with alternate format for linebreaks and backtraces #244

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
55 changes: 55 additions & 0 deletions examples/custom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
extern crate failure;

use std::fmt;

use failure::{Fail, chain_display};

#[derive(Debug)]
struct InsideError;

impl fmt::Display for InsideError {
fn fmt(&self, fmr: &mut fmt::Formatter) -> fmt::Result {
write!(fmr, "Inside Error")
}
}

impl Fail for InsideError {}

#[derive(Debug)]
struct OutsideError(InsideError);

impl fmt::Display for OutsideError {
fn fmt(&self, fmr: &mut fmt::Formatter) -> fmt::Result {
write!(fmr, "Outside Error: {}", self.0)
}
}

impl Fail for OutsideError {
fn cause(&self) -> Option<&Fail> {
Some(&self.0)
}
}

fn bad_function() -> Result<(), failure::Error> {
Err(OutsideError(InsideError).into())
}

fn main() {
println!("——— line ({{}}) chain_display ———");
if let Err(ref e) = bad_function() {
println!("{}", chain_display(e.as_fail()));
}
println!();

println!("——— block ({{:#}}) chain_display ———");
if let Err(ref e) = bad_function() {
println!("{:#}", chain_display(e.as_fail()));
}
println!();

println!("——— block ({{:#?}}) (Debug) chain_display ———");
if let Err(ref e) = bad_function() {
println!("{:#?}", chain_display(e.as_fail()));
}
println!();
}
40 changes: 37 additions & 3 deletions examples/simple.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[macro_use]
extern crate failure;

use failure::Fail;
use failure::chain_display;

#[derive(Debug, Fail)]
#[fail(display = "my error")]
Expand All @@ -16,7 +16,41 @@ fn bad_function() -> Result<(), WrappingError> {
}

fn main() {
for cause in Fail::iter_causes(&bad_function().unwrap_err()) {
println!("{}", cause);
println!("——— default fail(display = \"...\") ———");
if let Err(ref e) = bad_function() {
println!("{}", e);
println!("{:#} (with {{:#}})", e);
}
println!();

println!("——— default fmt::Debug ———");
if let Err(ref e) = bad_function() {
println!("{:?} (with {{:?}}) ", e);
println!("{:#?} (with {{:#?}})", e);
}
println!();

println!("——— line ({{}}) chain_display ———");
if let Err(ref e) = bad_function() {
println!("{}", chain_display(e));
}
println!();

println!("——— block ({{:#}}) chain_display ———");
if let Err(ref e) = bad_function() {
println!("{:#}", chain_display(e));
}
println!();

println!("——— line ({{:?}}) fmt::Debug for chain_display ———");
if let Err(ref e) = bad_function() {
println!("{:?}", chain_display(e));
}
println!();

println!("——— block ({{:#?}}) fmt::Debug for chain_display ———");
if let Err(ref e) = bad_function() {
println!("{:#?}", chain_display(e));
}
println!();
}
5 changes: 5 additions & 0 deletions src/backtrace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ with_backtrace! {
Backtrace { internal: InternalBacktrace::new() }
}

/// Checks if the backtrace is empty.
pub fn is_empty(&self) -> bool {
self.internal.is_none()
}

pub(crate) fn none() -> Backtrace {
Backtrace { internal: InternalBacktrace::none() }
}
Expand Down
86 changes: 86 additions & 0 deletions src/display.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use _core::fmt;

use Fail;
use backtrace::Backtrace;

/// A custom `Display` adapter for a `Fail` and an optional top-level
/// Backtrace. Displays the entire chain from the `Fail` through all causes.
/// Uses single line formatting with `{}` or multiple-lines incl. backtrace
/// via the (alternate) `{:#}`
pub struct ChainDisplay<'a>(pub(crate) &'a Fail, pub(crate) Option<&'a Backtrace>);

impl<'a> fmt::Display for ChainDisplay<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut next = Some(self.0);
let mut idx = 0;
let mut was_backtrace = false;

while let Some(fail) = next {
if was_backtrace {
writeln!(f)?;
}
was_backtrace = false;
if idx == 0 && f.alternate() {
write!(f, "error: {:#}", fail)?;
} else if idx == 0 {
write!(f, "error: {}", fail)?;
} else if f.alternate() {
write!(f, "\n caused by: {:#}", fail)?;
} else {
write!(f, "; caused by: {}", fail)?;
}
if f.alternate() {
let backtrace = if idx == 0 {
self.1.or_else(|| fail.backtrace())
} else {
fail.backtrace()
};
if let Some(backtrace) = backtrace {
write!(f, ", backtrace:\n{:#}", backtrace)?;
was_backtrace = true;
}
}
next = fail.cause();
idx += 1;
}
Ok(())
}
}

impl<'a> fmt::Debug for ChainDisplay<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut next = Some(self.0);
let mut idx = 0;
let mut was_backtrace = false;

while let Some(fail) = next {
if was_backtrace {
writeln!(f)?;
}
was_backtrace = false;
if idx == 0 && f.alternate() {
write!(f, "error: {:#?}", fail)?;
} else if idx == 0 {
write!(f, "error: {:?}", fail)?;
} else if f.alternate() {
write!(f, "\n caused by: {:#?}", fail)?;
} else {
write!(f, "; caused by: {:?}", fail)?;
}
if f.alternate() {
let backtrace = if idx == 0 {
self.1.or_else(|| fail.backtrace())
} else {
fail.backtrace()
};
if let Some(backtrace) = backtrace {
write!(f, ", backtrace:\n{:#?}", backtrace)?;
was_backtrace = true;
}
}
next = fail.cause();
idx += 1;
}
Ok(())
}
}
6 changes: 6 additions & 0 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use self::error_impl::ErrorImpl;
#[cfg(feature = "std")]
use std::error::Error as StdError;

use display::ChainDisplay;

/// The `Error` type, which can contain any failure.
///
Expand Down Expand Up @@ -166,6 +167,11 @@ impl Error {
self.imp.failure_mut().downcast_mut()
}

/// Return a custom `Display` adapter which show the entire chain of causes.
pub fn chain_display(&self) -> ChainDisplay {
ChainDisplay(self.as_fail(), Some(self.backtrace()))
}

/// Deprecated alias to `find_root_cause`.
#[deprecated(since = "0.1.2", note = "please use the 'find_root_cause()' method instead")]
pub fn root_cause(&self) -> &Fail {
Expand Down
15 changes: 15 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ mod box_std;
mod compat;
mod context;
mod result_ext;
mod display;

use core::any::TypeId;
use core::fmt::{Debug, Display};
Expand All @@ -47,6 +48,7 @@ pub use backtrace::Backtrace;
pub use compat::Compat;
pub use context::Context;
pub use result_ext::ResultExt;
pub use display::ChainDisplay;

#[cfg(feature = "failure_derive")]
#[allow(unused_imports)]
Expand Down Expand Up @@ -238,6 +240,11 @@ impl Fail {
Causes { fail: Some(self) }
}

/// Return a custom `Display` adapter which show the entire chain of causes.
pub fn chain_display(&self) -> ChainDisplay {
ChainDisplay(self, None)
}

/// Deprecated alias to `find_root_cause`.
#[deprecated(since = "0.1.2", note = "please use the 'find_root_cause()' method instead")]
pub fn root_cause(&self) -> &Fail {
Expand All @@ -251,6 +258,14 @@ impl Fail {
}
}

/// Return a custom `Display` adapter which shows the entire chain from the
/// provided `Fail` and all causes. The adapter will use single line
/// formatting with `{}` or multiple-lines incl. backtrace via the (alternate)
/// `{:#}`
pub fn chain_display(fail: &Fail) -> ChainDisplay {
ChainDisplay(fail, None)
}

#[cfg(feature = "std")]
impl<E: StdError + Send + Sync + 'static> Fail for E {}

Expand Down