#error #error-message #traits #error-context #backtrace #object #dyn

extracterr

helper type and traits for passing context through dyn Error trait objects

2 releases

0.1.1 May 18, 2020
0.1.0 May 18, 2020

#27 in #dyn

MIT/Apache

21KB
157 lines

extracterr

Build Status Latest Version Rust Documentation

Helpers for bundling context with errors and later extracting said context thru dyn Error trait objects.

The primary purpose of this crate is to pass context to error reporters through dyn Error trait objects. This is useful for information that may need to be captured by leaf errors but which doesn't belong in any single error's message. Backtraces, http status codes, and help messages are all examples of context that one might wish to pass directly to an error reporter rather than printing in the chain of errors.

Setup

[dependencies]
extracterr = "0.1"

TLDR Example

use backtrace::Backtrace;
use std::error::Error;
use extracterr::{Bundle, Bundled, Extract};

#[derive(Debug, thiserror::Error)]
#[error("Example Error")]
struct ExampleError;

let error = ExampleError;

// Capture a backtrace via `Default` and `From`
let error: Bundled<_, Backtrace> = error.into();

// Convert it to a trait object to throw away type information
let error: Box<dyn Error + Send + Sync + 'static> = error.into();

// first error in chain is the unerased version of the error `Bundled<ExampleError, Backtrace>`
assert!(error.downcast_ref::<Bundled<ExampleError, Backtrace>>().is_some());
assert_eq!("Example Error", error.to_string());
assert!(error.extract::<Backtrace>().is_none());

// Move to the next error in the chain
let error = error.source().unwrap();

// The second error in the chain is the erased version `Bundled<Erased, Backtrace>` which now
// works with downcasting, letting us access the bundled context
let backtrace = error.extract::<Backtrace>();
assert!(backtrace.is_some());

// The Display / Debug impls of the fake error that contains the bundled context print the
// context's type_name
assert_eq!(error.to_string(), std::any::type_name::<Backtrace>());

Details

The main type provided by this library is the Bundled type, which bundles an error and a context type into a new error. This type can be constructed either via From+Default or Bundle, for more details check out the docs on Bundled.

This type works best with the error kind pattern, as seen in std::io::Error:

use backtrace::Backtrace;
use extracterr::{Bundle, Bundled};

#[derive(Debug, thiserror::Error)]
#[error(transparent)]
struct Error {
    kind: Bundled<Kind, Backtrace>,
}

#[derive(Debug, thiserror::Error, Clone, Copy)]
enum Kind {
    #[error("could not enqueue item")]
    Queue,
    #[error("could not dequeue item")]
    Dequeue
}

impl Error {
    fn kind(&self) -> Kind {
        *self.kind.inner()
    }
}

impl From<Kind> for Error {
    fn from(kind: Kind) -> Self {
        Self { kind: Bundled::from(kind) }
    }
}

impl From<Bundled<Kind, Backtrace>> for Error {
    fn from(kind: Bundled<Kind, Backtrace>) -> Self {
        Self { kind }
    }
}

fn queue() -> Result<(), Error> {
    Err(Kind::Queue)?
}

fn dequeue() -> Result<(), Error> {
    Err(Kind::Dequeue).bundle(Backtrace::new())?
}

use extracterr::Extract;

let error = dequeue().unwrap_err();

// Convert it to a trait object to throw away type information
let error: Box<dyn std::error::Error + Send + Sync + 'static> = error.into();

assert!(error.downcast_ref::<Error>().is_some());
assert_eq!("could not dequeue item", error.to_string());
assert!(error.extract::<Backtrace>().is_none());

// Move to the next error in the chain
let error = error.source().unwrap();

let backtrace = error.extract::<Backtrace>();
assert!(backtrace.is_some());

Once context has been bundled into a chain of errors it can then be extracted back out via the Extract trait. Check out the source code of stable-eyre for a simple example of idiomatic usage of Extract in an error reporter.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

No runtime deps