use std::panic::Location;
pub use derive_more::Error;
pub use infinite_errors_macros::err_context;
#[macro_export]
macro_rules! declare_error_type {
($error_kind:ident) => {
#[derive(::std::fmt::Debug, ::infinite_errors::Error)]
pub struct Error {
kind: $error_kind,
cause: ::std::option::Option<::std::boxed::Box<Error>>,
location: &'static ::std::panic::Location<'static>,
}
impl Error {
pub fn new(
kind: $error_kind,
location: &'static ::std::panic::Location<'static>,
) -> Self {
Self {
kind,
cause: ::std::option::Option::None,
location,
}
}
pub fn kind(&self) -> &$error_kind {
&self.kind
}
pub fn cause(&self) -> ::std::option::Option<&Self> {
self.cause.as_deref()
}
pub fn location(&self) -> &'static ::std::panic::Location<'static> {
self.location
}
}
impl ::infinite_errors::ErrorType for Error {
type ErrorKind = $error_kind;
fn new(
kind: Self::ErrorKind,
cause: ::std::option::Option<::std::boxed::Box<Self>>,
location: &'static ::std::panic::Location<'static>,
) -> Self {
Self {
kind,
cause,
location,
}
}
}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.kind)?;
if let Some(cause) = &self.cause {
write!(f, ": {cause}")?;
}
Ok(())
}
}
impl<T> ::std::convert::From<T> for Error
where
T: ::std::convert::Into<$error_kind>,
{
#[track_caller]
fn from(kind: T) -> Self {
Self::new(kind.into(), ::std::panic::Location::caller())
}
}
pub trait ErrorContext<T> {
#[track_caller]
fn err_context(self, kind: $error_kind) -> ::std::result::Result<T, Error>;
#[track_caller]
fn err_context_with(
self,
kind: impl FnOnce() -> $error_kind,
) -> ::std::result::Result<T, Error>;
}
impl<T, OE> ErrorContext<T> for ::std::result::Result<T, OE>
where
OE: Into<Error>,
{
fn err_context(self, kind: $error_kind) -> ::std::result::Result<T, Error> {
self.map_err(|x| Error {
kind,
cause: ::std::option::Option::Some(::std::boxed::Box::new(x.into())),
location: ::std::panic::Location::caller(),
})
}
fn err_context_with(
self,
f: impl FnOnce() -> $error_kind,
) -> ::std::result::Result<T, Error> {
self.map_err(|x| Error {
kind: f(),
cause: ::std::option::Option::Some(::std::boxed::Box::new(x.into())),
location: ::std::panic::Location::caller(),
})
}
}
};
}
pub trait ErrorType {
type ErrorKind;
fn new(
kind: Self::ErrorKind,
cause: Option<Box<Self>>,
location: &'static Location<'static>,
) -> Self;
}
pub trait ErrorContext<T, K, E> {
#[track_caller]
fn err_context(self, kind: K) -> Result<T, E>;
#[track_caller]
fn err_context_with(self, kind: impl FnOnce() -> K) -> Result<T, E>;
}
impl<T, K, E, OE> ErrorContext<T, K, E> for Result<T, OE>
where
OE: Into<E>,
E: ErrorType<ErrorKind = K>,
{
fn err_context(self, kind: K) -> Result<T, E> {
self.map_err(|x| E::new(kind, Some(Box::new(x.into())), Location::caller()))
}
fn err_context_with(self, f: impl FnOnce() -> K) -> Result<T, E> {
self.map_err(|x| E::new(f(), Some(Box::new(x.into())), Location::caller()))
}
}