Struct mnn::prelude::Report

source ·
pub struct Report<C> { /* private fields */ }
Expand description

Contains a Frame stack consisting of Contexts and attachments.

Attachments can be added by using attach(). The Frame stack can be iterated by using frames().

When creating a Report by using new(), the passed Context is used to set the current context on the Report. To provide a new one, use change_context().

Attachments, and objects provided by a Context, are directly retrievable by calling request_ref() or request_value().

§Formatting

Report implements Display and Debug. When utilizing the Display implementation, the current context of the Report is printed, e.g. println!("{report}"). For the alternate Display output ("{:#}"), all Contexts are printed. To print the full stack of Contexts and attachments, use the Debug implementation ("{:?}"). To customize the output of the attachments in the Debug output, please see the error_stack::fmt module.

Please see the examples below for more information.

§Multiple Errors

Report is able to represent multiple errors that have occurred. Errors can be combined using the extend_one(), which will add the Frame stack of the other error as an additional source to the current report.

§Backtrace and SpanTrace

Report is able to provide a Backtrace and a [SpanTrace], which can be retrieved by calling request_ref::<Backtrace>() or request_ref::<SpanTrace>() (downcast_ref::<SpanTrace>() on stable) respectively. If the root context provides a Backtrace or a [SpanTrace], those are returned, otherwise, if configured, an attempt is made to capture them when creating a Report. To enable capturing of the backtrace, make sure RUST_BACKTRACE or RUST_LIB_BACKTRACE is set according to the Backtrace documentation. To enable capturing of the span trace, an ErrorLayer has to be enabled. Please also see the Feature Flags section. A single Report can have multiple Backtraces and [SpanTrace]s, depending on the amount of related errors the Report consists of. Therefore it isn’t guaranteed that request_ref() will only ever return a single Backtrace or [SpanTrace].

§Examples

§Provide a context for an error

use error_stack::{ResultExt, Result};

let config_path = "./path/to/config.file";
let content = std::fs::read_to_string(config_path)
    .attach_printable_lazy(|| format!("failed to read config file {config_path:?}"))?;

...

§Enforce a context for an error

use std::{fmt, path::{Path, PathBuf}};

use error_stack::{Context, Report, ResultExt};

#[derive(Debug)]
enum RuntimeError {
    InvalidConfig(PathBuf),
    ...
}

#[derive(Debug)]
enum ConfigError {
    IoError,
    ...
}

impl fmt::Display for RuntimeError {
    ...
}
impl fmt::Display for ConfigError {
    ...
}

impl Context for RuntimeError {}
impl Context for ConfigError {}

fn read_config(path: impl AsRef<Path>) -> Result<String, Report<ConfigError>> {
    std::fs::read_to_string(path.as_ref()).change_context(ConfigError::IoError)
}

fn main() -> Result<(), Report<RuntimeError>> {
    let config_path = "./path/to/config.file";
    let config = read_config(config_path)
            .change_context_lazy(|| RuntimeError::InvalidConfig(PathBuf::from(config_path)))?;

    ...
}

§Formatting

For the example from above, the report could be formatted as follows:

If the Display implementation of Report will be invoked, this will print something like:

could not parse "./path/to/config.file"

If the alternate Display implementation of Report is invoked ({report:#}), this will print something like:

could not parse "./path/to/config.file": config file is invalid: No such file or directory (os error 2)

The Debug implementation of Report will print something like:

could not parse "./path/to/config.file"
├╴at libs/error-stack/src/report.rs:58:14
│
├─▶ config file is invalid
│   ╰╴at libs/error-stack/src/report.rs:50:44
│
╰─▶ No such file or directory (os error 2)
    ├╴at libs/error-stack/src/report.rs:50:44
    ╰╴backtrace (1)

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

backtrace no. 1
  [redacted]

§Get the attached Backtrace and [SpanTrace]:

use error_stack::{ResultExt, Result};

let config_path = "./path/to/config.file";
let content = std::fs::read_to_string(config_path)
    .attach_printable_lazy(|| format!("failed to read config file {config_path:?}"));

let content = match content {
    Err(err) => {
        for backtrace in err.request_ref::<std::backtrace::Backtrace>() {
            println!("backtrace: {backtrace}");
        }

        for span_trace in err.request_ref::<tracing_error::SpanTrace>() {
            println!("span trace: {span_trace}")
        }

        return Err(err)
    }

    Ok(ok) => ok
};

...

Implementations§

source§

impl<C> Report<C>

source

pub fn new(context: C) -> Report<C>
where C: Context,

Creates a new Report<Context> from a provided scope.

If context does not provide Backtrace/[SpanTrace] then this attempts to capture them directly. Please see the Backtrace and SpanTrace section of the Report documentation for more information.

source

pub fn extend_one(&mut self, report: Report<C>)

Merge two Reports together

This function appends the current_frames() of the other Report to the current_frames() of this report. Meaning A.extend_one(B) -> A.current_frames() = A.current_frames() + B.current_frames()

use std::{
    fmt::{Display, Formatter},
    path::Path,
};

use error_stack::{Context, Report, ResultExt};

#[derive(Debug)]
struct IoError;

impl Display for IoError {
            ...
}


fn read_config(path: impl AsRef<Path>) -> Result<String, Report<IoError>> {
    std::fs::read_to_string(path.as_ref())
        .change_context(IoError)
}

let mut error1 = read_config("config.txt").unwrap_err();
let error2 = read_config("config2.txt").unwrap_err();
let mut error3 = read_config("config3.txt").unwrap_err();

error1.extend_one(error2);
error3.extend_one(error1);

// ^ This is equivalent to:
// error3.extend_one(error1);
// error3.extend_one(error2);

This function implements the same functionality as Extend::extend_one (#7261). Once stabilised this function will be removed in favor of Extend.

source

pub fn attach<A>(self, attachment: A) -> Report<C>
where A: Send + Sync + 'static,

Adds additional information to the Frame stack.

This behaves like attach_printable() but will not be shown when printing the Report. To benefit from seeing attachments in normal error outputs, use attach_printable()

Note: attach_printable() will be deprecated when specialization is stabilized and it becomes possible to merge these two methods.

source

pub fn attach_printable<A>(self, attachment: A) -> Report<C>
where A: Display + Debug + Send + Sync + 'static,

Adds additional (printable) information to the Frame stack.

This behaves like attach() but the display implementation will be called when printing the Report.

Note: This will be deprecated in favor of attach() when specialization is stabilized it becomes possible to merge these two methods.

§Example
use core::fmt;
use std::fs;

use error_stack::ResultExt;

#[derive(Debug)]
pub struct Suggestion(&'static str);

impl fmt::Display for Suggestion {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.write_str(self.0)
    }
}

let error = fs::read_to_string("config.txt")
    .attach(Suggestion("better use a file which exists next time!"));
let report = error.unwrap_err();
let suggestion = report.request_ref::<Suggestion>().next().unwrap();

assert_eq!(suggestion.0, "better use a file which exists next time!");
source

pub fn change_context<T>(self, context: T) -> Report<T>
where T: Context,

Add a new Context object to the top of the Frame stack, changing the type of the Report.

Please see the Context documentation for more information.

source

pub fn current_frames(&self) -> &[Frame]

Return the direct current frames of this report, to get an iterator over the topological sorting of all frames refer to frames()

This is not the same as Report::current_context, this function gets the underlying frames that make up this report, while Report::current_context traverses the stack of frames to find the current context. A Report and be made up of multiple Frames, which stack on top of each other. Considering PrintableA<PrintableA<Context>>, Report::current_frames will return the “outer” layer PrintableA, while Report::current_context will return the underlying Context (the current type parameter of this Report)

Using Extend and extend_one(), a Report can additionally be made up of multiple stacks of frames and builds a “group” of them, but a Report can only ever have a single Context, therefore this function returns a slice instead, while Report::current_context only returns a single reference.

source

pub fn frames(&self) -> Frames<'_>

Returns an iterator over the Frame stack of the report.

source

pub fn frames_mut(&mut self) -> FramesMut<'_>

Returns an iterator over the Frame stack of the report with mutable elements.

source

pub fn contains<T>(&self) -> bool
where T: Send + Sync + 'static,

Returns if T is the type held by any frame inside of the report.

T could either be an attachment or a Context.

§Example
fn read_file(path: impl AsRef<Path>) -> Result<String, Report<io::Error>> {
    ...
}

let report = read_file("test.txt").unwrap_err();
assert!(report.contains::<io::Error>());
source

pub fn downcast_ref<T>(&self) -> Option<&T>
where T: Send + Sync + 'static,

Searches the frame stack for a context provider T and returns the most recent context found.

T can either be an attachment or a Context.

§Example
use std::io;

fn read_file(path: impl AsRef<Path>) -> Result<String, Report<io::Error>> {
    ...
}

let report = read_file("test.txt").unwrap_err();
let io_error = report.downcast_ref::<io::Error>().unwrap();
assert_eq!(io_error.kind(), io::ErrorKind::NotFound);
source

pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
where T: Send + Sync + 'static,

Searches the frame stack for an instance of type T, returning the most recent one found.

T can either be an attachment or a Context.

source

pub fn current_context(&self) -> &C
where C: Send + Sync + 'static,

Returns the current context of the Report.

If the user want to get the latest context, current_context can be called. If the user wants to handle the error, the context can then be used to directly access the context’s type. This is only possible for the latest context as the Report does not have multiple generics as this would either require variadic generics or a workaround like tuple-list.

This is one disadvantage of the library in comparison to plain Errors, as in these cases, all context types are known.

§Example
use std::io;

fn read_file(path: impl AsRef<Path>) -> Result<String, Report<io::Error>> {
    ...
}

let report = read_file("test.txt").unwrap_err();
let io_error = report.current_context();
assert_eq!(io_error.kind(), io::ErrorKind::NotFound);
source

pub fn into_error(self) -> impl Error + Send + Sync + 'static
where C: 'static,

Converts this Report to an Error.

source

pub fn as_error(&self) -> &(impl Error + Send + Sync + 'static)
where C: 'static,

Returns this Report as an Error.

source§

impl Report<()>

source

pub fn set_charset(charset: Charset)

Set the charset preference

The value defaults to Charset::Utf8.

§Example
use std::io::{Error, ErrorKind};

use error_stack::{report, Report};
use error_stack::fmt::{Charset};

struct Suggestion(&'static str);

Report::install_debug_hook::<Suggestion>(|Suggestion(value), context| {
    match context.charset() {
        Charset::Utf8 => context.push_body(format!("📝 {value}")),
        Charset::Ascii => context.push_body(format!("suggestion: {value}"))
    };
});

let report =
    report!(Error::from(ErrorKind::InvalidInput)).attach(Suggestion("oh no, try again"));

Report::set_charset(Charset::Utf8);
println!("{report:?}");

Report::set_charset(Charset::Ascii);
println!("{report:?}");

Which will result in something like:

invalid input parameter
├╴at libs/error-stack/src/fmt/charset.rs:22:5
├╴backtrace (1)
╰╴📝 oh no, try again

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

backtrace no. 1
  [redacted]
invalid input parameter
|-at libs/error-stack/src/fmt/charset.rs:22:5
|-backtrace (1)
|-suggestion: oh no, try again

========================================

backtrace no. 1
  [redacted]
source§

impl Report<()>

source

pub fn set_color_mode(mode: ColorMode)

Set the color mode preference

If no ColorMode is set, it defaults to ColorMode::Emphasis.

§Example
use std::io::{Error, ErrorKind};
use owo_colors::OwoColorize;

use error_stack::{report, Report};
use error_stack::fmt::ColorMode;

struct Suggestion(&'static str);

Report::install_debug_hook::<Suggestion>(|Suggestion(value), context| {
    let body = format!("suggestion: {value}");
    match context.color_mode() {
        ColorMode::Color => context.push_body(body.green().to_string()),
        ColorMode::Emphasis => context.push_body(body.italic().to_string()),
        ColorMode::None => context.push_body(body)
    };
});

let report =
    report!(Error::from(ErrorKind::InvalidInput)).attach(Suggestion("oh no, try again"));

Report::set_color_mode(ColorMode::None);
println!("{report:?}");

Report::set_color_mode(ColorMode::Emphasis);
println!("{report:?}");

Report::set_color_mode(ColorMode::Color);
println!("{report:?}");

Which will result in something like:

invalid input parameter
├╴at libs/error-stack/src/fmt/color.rs:25:5
├╴backtrace (1)
╰╴suggestion: oh no, try again

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

backtrace no. 1
  [redacted]
invalid input parameter
├╴at libs/error-stack/src/fmt/color.rs:25:5
├╴backtrace (1)
╰╴suggestion: oh no, try again

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

backtrace no. 1
  [redacted]
invalid input parameter
├╴at libs/error-stack/src/fmt/color.rs:25:5
├╴backtrace (1)
╰╴suggestion: oh no, try again

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

backtrace no. 1
  [redacted]
source§

impl Report<()>

source

pub fn install_debug_hook<T>( hook: impl Fn(&T, &mut HookContext<T>) + Send + Sync + 'static, )
where T: Send + Sync + 'static,

Can be used to globally set a Debug format hook, for a specific type T.

This hook will be called on every Debug call, if an attachment with the same type has been found.

§Examples
use std::io::{Error, ErrorKind};

use error_stack::{
    report, Report,
};

struct Suggestion(&'static str);

Report::install_debug_hook::<Suggestion>(|value, context| {
    context.push_body(format!("suggestion: {}", value.0));
});

let report =
    report!(Error::from(ErrorKind::InvalidInput)).attach(Suggestion("oh no, try again"));

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

Which will result in something like:

invalid input parameter
├╴at libs/error-stack/src/hook/mod.rs:20:5
├╴backtrace (1)
╰╴suggestion: oh no, try again

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

backtrace no. 1
  [redacted]

This example showcases the ability of hooks to be invoked for values provided via the Provider API using Error::provide.

#![feature(error_generic_member_access)]

use core::error::{Request, Error};
use core::fmt::{Display, Formatter};
use error_stack::{Report, report};

struct Suggestion(&'static str);

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


#[derive(Debug)]
struct UserError {
    code: ErrorCode
}

impl Display for UserError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str("invalid user input")
    }
}

impl Error for UserError {
 fn provide<'a>(&'a self, req: &mut Request<'a>) {
   req.provide_value(Suggestion("try better next time!"));
   req.provide_ref(&self.code);
 }
}

Report::install_debug_hook::<Suggestion>(|Suggestion(value), context| {
    context.push_body(format!("suggestion: {value}"));
});
Report::install_debug_hook::<ErrorCode>(|ErrorCode(value), context| {
    context.push_body(format!("error code: {value}"));
});

let report = report!(UserError {code: ErrorCode(420)});

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

Which will result in something like:

invalid user input
├╴suggestion: try better next time!
├╴error code: 420
├╴at libs/error-stack/src/hook/mod.rs:49:14
╰╴backtrace (1)

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

backtrace no. 1
  [redacted]

error-stack comes with some built-in hooks which can be overwritten. This is useful if you want to change the output of the built-in hooks, or if you want to add additional information to the output. For example, you can override the built-in hook for Location to hide the file path:

use std::{
    io::{Error, ErrorKind},
    panic::Location,
};

error_stack::Report::install_debug_hook::<Location>(|_location, _context| {
    // Intentionally left empty so nothing will be printed
});

let report = error_stack::report!(Error::from(ErrorKind::InvalidInput));

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

Which will result in something like:

invalid input parameter
╰╴backtrace (1)

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

backtrace no. 1
  [redacted]

Trait Implementations§

source§

impl<C> Debug for Report<C>

source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
source§

impl<Context> Display for Report<Context>

source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
source§

impl<Context> Extend<Report<Context>> for Report<Context>

source§

fn extend<T>(&mut self, iter: T)
where T: IntoIterator<Item = Report<Context>>,

Extends a collection with the contents of an iterator. Read more
source§

fn extend_one(&mut self, item: A)

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
source§

impl<C> From<C> for Report<C>
where C: Context,

source§

fn from(context: C) -> Report<C>

Converts to this type from the input type.
source§

impl<C> From<Report<C>> for Box<dyn Error>
where C: 'static,

source§

fn from(report: Report<C>) -> Box<dyn Error>

Converts to this type from the input type.
source§

impl<C> From<Report<C>> for Box<dyn Error + Send>
where C: 'static,

source§

fn from(report: Report<C>) -> Box<dyn Error + Send>

Converts to this type from the input type.
source§

impl<C> From<Report<C>> for Box<dyn Error + Send + Sync>
where C: 'static,

source§

fn from(report: Report<C>) -> Box<dyn Error + Send + Sync>

Converts to this type from the input type.
source§

impl<C> From<Report<C>> for Box<dyn Error + Sync>
where C: 'static,

source§

fn from(report: Report<C>) -> Box<dyn Error + Sync>

Converts to this type from the input type.
source§

impl From<Report<ErrorKind>> for MNNError

source§

fn from(report: Report<ErrorKind>) -> Self

Converts to this type from the input type.
source§

impl<Context> Termination for Report<Context>

source§

fn report(self) -> ExitCode

Is called to get the representation of the value as status code. This status code is returned to the operating system.

Auto Trait Implementations§

§

impl<C> Freeze for Report<C>

§

impl<C> !RefUnwindSafe for Report<C>

§

impl<C> Send for Report<C>

§

impl<C> Sync for Report<C>

§

impl<C> Unpin for Report<C>

§

impl<C> !UnwindSafe for Report<C>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<!> for T

source§

fn from(t: !) -> T

Converts to this type from the input type.
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToString for T
where T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.