Available on crate feature std only.
Expand description

Implementation of formatting, to enable colors and the use of box-drawing characters use the pretty-print feature.

Hooks

The Debug implementation can be easily extended using hooks. Hooks are functions of the signature Fn(&T, &mut HookContext<T>), they provide an ergonomic way to partially modify the output format and enable custom output for types that are not necessarily added via Report::attach_printable or are unable to implement Display.

Hooks can be attached through the central hooking mechanism which error-stack provides via Report::install_debug_hook.

Hooks are called for contexts which provide additional values through Context::provide and attachments which are added via Report::attach or Report::attach_printable. The order of Report::install_debug_hook calls determines the order of the rendered output. Note, that Hooks get called on all values provided by Context::provide, but not on the Context object itself. Therefore if you want to call a hook on a Context to print in addition to its Display implementation, you may want to call demand.provide_ref(self) inside of Context::provide.

Hook functions need to be Fn and not FnMut, which means they are unable to directly mutate state outside of the closure. You can still achieve mutable state outside of the scope of your closure through interior mutability, e.g. by using the std::sync module like Mutex, RwLock, and atomics.

The type, a hook will be called for, is determined by the type of the first argument to the closure. This type can either be specified at the closure level or when calling Report::install_debug_hook. This type needs to be 'static, Send, and Sync.

You can then add additional entries to the body with HookContext::push_body, and entries to the appendix with HookContext::push_appendix, refer to the documentation of HookContext for further information.

Example

use std::fmt::{Display, Formatter};
use std::io::{Error, ErrorKind};
use error_stack::Report;

#[derive(Debug)]
struct ErrorCode(u64);

impl Display for ErrorCode {
  fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result {
    write!(fmt, "error: {}", self.0)
  }
}

struct Suggestion(&'static str);
struct Warning(&'static str);

// This hook will never be called, because a later invocation of `install_debug_hook` overwrites
// the hook for the type `ErrorCode`.
Report::install_debug_hook::<ErrorCode>(|_, _| {
    unreachable!("will never be called");
});

// `HookContext` always has a type parameter, which needs to be the same as the type of the
// value, we use `HookContext` here as storage, to store values specific to this hook.
// Here we make use of the auto-incrementing feature.
// The incrementation is type specific, meaning that `context.increment()` for the `Suggestion` hook
// will not influence the counter of the `ErrorCode` or `Warning` hook.
Report::install_debug_hook::<Suggestion>(|Suggestion(value), context| {
    let idx = context.increment_counter() + 1;
    context.push_body(format!("suggestion {idx}: {value}"));
});

// Even though we used `attach_printable`, we can still use hooks, `Display` of a type attached
// via `attach_printable` is only ever used when no hook was found.
Report::install_debug_hook::<ErrorCode>(|ErrorCode(value), context| {
    context.push_body(format!("error ({value})"));
});

Report::install_debug_hook::<Warning>(|Warning(value), context| {
    let idx = context.increment_counter() + 1;

    // we set a value, which will be removed on non-alternate views
    // and is going to be appended to the actual return value.
    if context.alternate() {
        context.push_appendix(format!("warning {idx}:\n  {value}"));
    }

    context.push_body(format!("warning ({idx}) occurred"));
 });


let report = Report::new(Error::from(ErrorKind::InvalidInput))
    .attach_printable(ErrorCode(404))
    .attach(Suggestion("try to be connected to the internet."))
    .attach(Suggestion("try better next time!"))
    .attach(Warning("unable to fetch resource"));

println!("{report:?}");

println!("{report:#?}");

The output of println!("{report:?}"):

invalid input parameter
src/fmt/mod.rs:57:14
backtrace (1)
error (404)
suggestion 1: try to be connected to the internet.
suggestion 2: try better next time!
warning (1) occurred

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

backtrace no. 1
  [redacted]

The output of println!("{report:#?}"):

invalid input parameter
src/fmt/mod.rs:57:14
backtrace (1)
error (404)
suggestion 1: try to be connected to the internet.
suggestion 2: try better next time!
warning (1) occurred

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

backtrace no. 1
  [redacted]

warning 1:
  unable to fetch resource

Structs

Carrier for contextual information used across hook invocations.