use std::{any::type_name, borrow::Cow, error, fmt};
use thiserror::Error;
#[macro_export]
macro_rules! ensure {
($Type:ty, $assumption:expr, $fmt:expr, $($arg:tt)*) => {
if !$assumption {
return ::std::result::Result::Err(
$crate::Error::Assumption {
ty: ::std::any::type_name::<$Type>(),
assumption: ::std::stringify!($assumption),
message: ::std::format!($fmt, $($arg)*),
}
);
}
};
}
#[derive(Debug, Error)]
pub enum Error {
#[error("Invalid size for {ty}: expected {expected} bytes but received {received} bytes")]
InvalidSize {
ty: &'static str,
received: usize,
expected: usize,
},
#[error("Assumption `{assumption}` failed for {ty}: {message}")]
Assumption {
ty: &'static str,
assumption: &'static str,
message: String,
},
#[error(
"Invalid discriminant for {ty}, received {found:?} while expecting one of: [ {options}]"
)]
InvalidDiscriminant {
ty: &'static str,
options: &'static str,
found: Box<dyn fmt::Debug + Send + Sync>,
},
#[error("Field {field} of {ty} is not valid")]
InvalidField {
ty: &'static str,
field: &'static str,
},
#[error("Tuple entry {ty}.{index} is not valid")]
InvalidTuple {
ty: &'static str,
index: usize,
},
#[error("{0}")]
Message(Cow<'static, str>),
#[error(transparent)]
Custom(#[from] Box<dyn error::Error + Send + Sync>),
#[error("{ty}: {error}")]
Context {
ty: &'static str,
error: Box<Self>,
#[source]
cause: Box<Self>,
},
}
impl From<&'static str> for Error {
fn from(error: &'static str) -> Self {
Error::Message(Cow::Borrowed(error))
}
}
impl Error {
#[inline]
pub fn invalid_size<T: ?Sized>(received: usize, expected: usize) -> Self {
Self::InvalidSize {
ty: type_name::<T>(),
received,
expected,
}
}
#[inline]
pub fn invalid_discriminant<T, V>(found: V, options: &'static str) -> Self
where
T: ?Sized,
V: fmt::Debug + Send + Sync + 'static,
{
Self::InvalidDiscriminant {
ty: type_name::<T>(),
found: Box::new(found),
options,
}
}
#[inline]
pub fn invalid_field<T>(field: &'static str) -> Self
where
T: ?Sized,
{
Self::InvalidField {
ty: type_name::<T>(),
field,
}
}
#[inline]
pub fn invalid_tuple<T>(index: usize) -> Self
where
T: ?Sized,
{
Self::InvalidTuple {
ty: type_name::<T>(),
index,
}
}
}
mod private {
pub trait Sealed {}
impl<T, E> Sealed for Result<T, E> where E: ::std::error::Error + 'static {}
}
pub trait Context<T, E>: private::Sealed {
fn context<C>(self, context: C) -> Result<T, Error>
where
C: Into<Error>;
}
impl<T, E> Context<T, E> for Result<T, E>
where
E: error::Error + Send + Sync + 'static,
{
fn context<C>(self, context: C) -> Result<T, Error>
where
C: Into<Error>,
{
match self {
Ok(t) => Ok(t),
Err(cause) => Err(Error::Context {
ty: type_name::<T>(),
error: Box::new(context.into()),
cause: Box::new(Error::Custom(Box::new(cause))),
}),
}
}
}