use core::{fmt, marker::PhantomData, mem::transmute};
use thiserror::Error;
use crate::{
context::NoContext,
predicate::{Predicate, PredicateExpected},
types::TypeStr,
};
pub const ERROR: &str = "Error";
pub const EXPECTED: &str = "expected";
pub const CONTEXT: &str = "context";
#[derive(Error)]
#[error(
"{EXPECTED} {expected} ({context}) [{expected:?}]",
expected = P::expected(),
context = C::VALUE
)]
#[repr(transparent)]
pub struct Error<T: ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized = NoContext> {
predicate: PhantomData<P>,
context: PhantomData<C>,
value: T,
}
impl<T: ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Error<T, P, C> {
pub(crate) const fn new_ref(value: &T) -> &Self {
unsafe { transmute(value) }
}
pub const fn get_ref(&self) -> &T {
&self.value
}
}
impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Error<T, P, C> {
pub(crate) const fn new(value: T) -> Self {
Self {
predicate: PhantomData,
context: PhantomData,
value,
}
}
pub fn get(self) -> T {
self.value
}
}
pub type RecoverableRef<'a, T, P, C = NoContext, R = T> = Result<&'a T, &'a Error<R, P, C>>;
pub type Recoverable<T, P, C = NoContext, R = T> = Result<T, Error<R, P, C>>;
impl<T: ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> fmt::Debug for Error<T, P, C> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let expected = P::expected();
let context = C::VALUE;
formatter
.debug_struct(ERROR)
.field(EXPECTED, &expected)
.field(CONTEXT, &context)
.finish_non_exhaustive()
}
}