use std::{error, fmt, result};
pub type Error = Box<dyn error::Error + 'static>;
pub type Result<T, E = Error> = result::Result<T, E>;
#[derive(Debug)]
pub struct Context {
context: String,
source: Error,
}
impl fmt::Display for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.context.fmt(f)
}
}
impl error::Error for Context {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(self.source.as_ref())
}
}
pub trait ResultExt<T, E>: Sized {
fn context<C>(self, context: C) -> Result<T>
where
C: Into<String>,
{
self.with_context(|_| context.into())
}
fn with_context<C, F>(self, build_context: F) -> Result<T>
where
C: Into<String>,
F: FnOnce(&E) -> C;
}
impl<T, E: error::Error + 'static> ResultExt<T, E> for Result<T, E> {
fn with_context<C, F>(self, build_context: F) -> Result<T>
where
C: Into<String>,
F: FnOnce(&E) -> C,
{
self.map_err(|err| {
Box::new(Context {
context: build_context(&err).into(),
source: Box::new(err),
}) as Error
})
}
}
#[macro_export]
macro_rules! format_err {
($format_str:literal) => ({
let err: $crate::errors::Error = format!($format_str).into();
err
});
($format_str:literal, $($arg:expr),*) => ({
let err: $crate::errors::Error = format!($format_str, $($arg),*).into();
err
});
}