use alloc::{borrow::Cow, boxed::Box};
use core::{
any::{Any, type_name, type_name_of_val},
error::Error,
fmt::{Debug, Display},
};
use crate::error::UniError;
use crate::kind::{UniKind, UniKindCode, UniKindCodes};
pub trait UniStdError: Any + Error + Send + Sync {
fn type_name(&self) -> &'static str {
type_name::<Self>()
}
}
impl<E> UniStdError for E where E: Error + Any + Send + Sync {}
pub trait UniDisplay: Display + Debug + Any + Send + Sync {
fn type_name(&self) -> &'static str {
type_name::<Self>()
}
}
impl<E> UniDisplay for E where E: Display + Debug + Any + Send + Sync {}
#[derive(Debug, PartialEq)]
pub struct FakeError;
impl Display for FakeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "FakeError")
}
}
impl Error for FakeError {}
pub enum Downcast<'e, A: 'static, E: Error + 'static, K: UniKind> {
DisplayRef(&'e A),
ErrorRef(&'e E),
UniError(UniError<K>),
}
#[derive(Copy, Clone, Debug)]
pub enum Cause<'e> {
UniError(&'e UniError<dyn UniKind>),
UniStdError(&'e dyn UniStdError),
StdError(&'e (dyn Error + 'static)),
UniDisplay(&'e dyn UniDisplay),
}
impl<'e> Cause<'e> {
pub(crate) fn from_inner(inner: &'e CauseInner) -> Cause<'e> {
match inner {
CauseInner::UniError(err) => Cause::UniError(err),
CauseInner::UniStdError(err) => Cause::UniStdError(&**err),
CauseInner::BoxedStdError(err) => Cause::StdError(&**err),
CauseInner::UniDisplay(err) => Cause::UniDisplay(&**err),
}
}
fn any_downcast_ref<A: 'static>(err: &'e dyn Any) -> Option<&'e A> {
err.downcast_ref::<A>()
}
fn error_downcast_ref<E: Error + 'static>(err: &'e (dyn Error + 'static)) -> Option<&'e E> {
err.downcast_ref::<E>()
}
pub fn downcast<A: 'static, E: Error + 'static, K: UniKind>(
self,
) -> Option<Downcast<'e, A, E, K>> {
match self {
Cause::UniError(err) => err.to_typed_kind::<K>().map(Downcast::UniError),
Cause::UniStdError(err) => Self::error_downcast_ref(err).map(Downcast::ErrorRef),
Cause::StdError(err) => Self::error_downcast_ref(err).map(Downcast::ErrorRef),
Cause::UniDisplay(err) => Self::any_downcast_ref(err).map(Downcast::DisplayRef),
}
}
pub fn downcast_uni<K: UniKind>(self) -> Option<UniError<K>> {
match self {
Cause::UniError(err) => err.to_typed_kind::<K>(),
_ => None,
}
}
pub fn downcast_ref_disp<A: 'static>(self) -> Option<&'e A> {
match self.downcast::<A, FakeError, ()>() {
Some(Downcast::DisplayRef(err)) => Some(err),
_ => None,
}
}
pub fn downcast_ref_err<E: Error + 'static>(self) -> Option<&'e E> {
match self.downcast::<(), E, ()>() {
Some(Downcast::ErrorRef(err)) => Some(err),
_ => None,
}
}
pub fn type_name(self) -> Cow<'static, str> {
match self {
Cause::UniError(err) => err.type_name().into(),
Cause::UniStdError(err) => UniStdError::type_name(err).into(),
Cause::StdError(err) => type_name_of_val(err).into(),
Cause::UniDisplay(err) => err.type_name().into(),
}
}
pub fn next(self) -> Option<Cause<'e>> {
match self {
Cause::UniError(err) => err.prev_cause().map(|cause| match cause {
Cause::UniError(err) => Cause::UniError(err),
Cause::UniStdError(err) => Cause::UniStdError(err),
Cause::StdError(err) => Cause::StdError(err),
Cause::UniDisplay(err) => Cause::UniDisplay(err),
}),
Cause::UniStdError(err) => err.source().map(Cause::StdError),
Cause::StdError(err) => err.source().map(Cause::StdError),
Cause::UniDisplay(_) => None,
}
}
}
impl<'e> Display for Cause<'e> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match *self {
Cause::UniError(err) => <UniError<dyn UniKind> as Display>::fmt(err, f),
Cause::UniStdError(err) => <dyn UniStdError as Display>::fmt(err, f),
Cause::StdError(err) => <dyn Error as Display>::fmt(err, f),
Cause::UniDisplay(err) => <dyn UniDisplay as Display>::fmt(err, f),
}
}
}
impl<'e> Error for Cause<'e> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Cause::UniError(err) => err.source(),
Cause::UniStdError(err) => err.source(),
Cause::StdError(err) => err.source(),
Cause::UniDisplay(_) => None,
}
}
}
pub struct Chain<'e> {
curr: Option<Cause<'e>>,
}
impl<'e> Chain<'e> {
pub(crate) fn new(curr: Option<Cause<'e>>) -> Self {
Self { curr }
}
}
impl<'e> Iterator for Chain<'e> {
type Item = Cause<'e>;
fn next(&mut self) -> Option<Self::Item> {
let curr = self.curr?;
self.curr = curr.next();
Some(curr)
}
}
#[derive(Debug)]
pub(crate) enum CauseInner {
UniError(UniError<dyn UniKind>),
UniStdError(Box<dyn UniStdError + Send + Sync>),
BoxedStdError(Box<dyn Error + Send + Sync>),
UniDisplay(Box<dyn UniDisplay + Send + Sync>),
}
impl CauseInner {
pub fn from_display(cause: impl UniDisplay) -> CauseInner {
CauseInner::UniDisplay(Box::new(cause))
}
pub fn from_error(cause: impl UniStdError) -> CauseInner {
CauseInner::UniStdError(Box::new(cause))
}
pub fn from_boxed_error(cause: Box<dyn Error + Send + Sync>) -> CauseInner {
CauseInner::BoxedStdError(cause)
}
pub fn from_uni_error<K: UniKind>(cause: UniError<K>) -> CauseInner {
CauseInner::UniError(cause.into_dyn_kind())
}
pub fn from_dyn_error(cause: UniError<dyn UniKind>) -> CauseInner {
CauseInner::UniError(cause)
}
pub fn from_dyn_code_error<C: 'static>(
cause: UniError<dyn UniKindCode<Code = C>>,
) -> CauseInner {
CauseInner::UniError(cause.into_dyn_kind())
}
pub fn from_dyn_codes_error<C: 'static, C2: 'static>(
cause: UniError<dyn UniKindCodes<Code = C, Code2 = C2>>,
) -> CauseInner {
CauseInner::UniError(cause.into_dyn_kind())
}
}