use core::any::Any;
use core::error::Error;
use core::fmt;
use core::marker::PhantomData;
use core::ops::Deref;
use crate::context::Contextable;
use crate::generic_error::AnyError;
use crate::type_set::{
Contains, DebugFold, DisplayFold, ErrorFold, IsFold, Narrow, SupersetOf, TupleForm, TypeSet,
};
use crate::{Cons, End, TracedError};
pub struct ErrorUnion<E: TypeSet> {
pub(crate) value: Box<dyn Contextable>,
_pd: PhantomData<E>,
}
impl<T> Deref for ErrorUnion<(T,)>
where
T: 'static,
{
type Target = T;
fn deref(&self) -> &T {
(self.value.as_ref() as &dyn Any)
.downcast_ref::<T>()
.unwrap()
}
}
impl<T: Contextable> From<T> for ErrorUnion<(T,)>
where
T: 'static,
{
fn from(t: T) -> ErrorUnion<(T,)> {
ErrorUnion::new(t)
}
}
impl<E> fmt::Debug for ErrorUnion<E>
where
E: TypeSet,
E::Variants: fmt::Debug + DebugFold,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
E::Variants::debug_fold(self.value.as_ref() as &dyn Any, formatter)?;
Ok(())
}
}
impl<E> fmt::Display for ErrorUnion<E>
where
E: TypeSet,
E::Variants: fmt::Display + DisplayFold,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
E::Variants::display_fold(self.value.as_ref() as &dyn Any, formatter)?;
Ok(())
}
}
fn _send_sync_error_assert() {
use std::io;
fn is_send<T: Send>(_: &T) {}
fn is_sync<T: Sync>(_: &T) {}
fn is_error<T: Error>(_: &T) {}
let error_union: ErrorUnion<(io::Error, fmt::Error)> =
ErrorUnion::new(io::Error::new(io::ErrorKind::Other, "yooo"));
is_send(&error_union);
is_sync(&error_union);
let error_union: ErrorUnion<(io::Error, TracedError<fmt::Error>)> =
ErrorUnion::new(io::Error::new(io::ErrorKind::Other, "yooo"));
is_send(&error_union);
is_sync(&error_union);
}
unsafe impl<T> Send for ErrorUnion<T> where T: TypeSet + Send {}
unsafe impl<T> Sync for ErrorUnion<T> where T: TypeSet + Sync {}
impl<E> Error for ErrorUnion<E>
where
E: TypeSet,
E::Variants: Error + DebugFold + DisplayFold + ErrorFold,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
E::Variants::source_fold(self.value.as_ref() as &dyn Any)
}
}
impl<E> ErrorUnion<E>
where
E: TypeSet,
{
pub fn new<T, Index>(t: T) -> ErrorUnion<E>
where
T: Contextable,
E::Variants: Contains<T, Index>,
{
ErrorUnion {
value: Box::new(t),
_pd: PhantomData,
}
}
pub fn narrow<Target, Index>(
self,
) -> Result<
Target,
ErrorUnion<<<E::Variants as Narrow<Target, Index>>::Remainder as TupleForm>::Tuple>,
>
where
Target: 'static,
E::Variants: Narrow<Target, Index>,
{
if (self.value.as_ref() as &dyn Any).is::<Target>() {
Ok(*(self.value as Box<dyn Any>).downcast::<Target>().unwrap())
} else {
Err(ErrorUnion {
value: self.value,
_pd: PhantomData,
})
}
}
pub fn widen<Other, Index>(self) -> ErrorUnion<Other>
where
Other: TypeSet,
Other::Variants: SupersetOf<E::Variants, Index>,
{
ErrorUnion {
value: self.value,
_pd: PhantomData,
}
}
pub fn subset<TargetList, Index>(
self,
) -> Result<
ErrorUnion<TargetList>,
ErrorUnion<<<E::Variants as SupersetOf<TargetList::Variants, Index>>::Remainder as TupleForm>::Tuple>,
>
where
TargetList: TypeSet,
E::Variants: IsFold + SupersetOf<TargetList::Variants, Index>,
{
if E::Variants::is_fold(self.value.as_ref() as &dyn Any) {
Ok(ErrorUnion {
value: self.value,
_pd: PhantomData,
})
} else {
Err(ErrorUnion {
value: self.value,
_pd: PhantomData,
})
}
}
pub fn take<Target>(self) -> Target
where
Target: 'static,
E: TypeSet<Variants = Cons<Target, End>>,
{
*(self.value as Box<dyn Any>).downcast::<Target>().unwrap()
}
pub fn to_enum(self) -> E::Enum
where
E::Enum: From<Self>,
{
E::Enum::from(self)
}
pub fn as_enum<'a>(&'a self) -> E::EnumRef<'a>
where
E::EnumRef<'a>: From<&'a Self>,
{
E::EnumRef::from(&self)
}
}
impl<T: 'static> ErrorUnion<(T,)> {
pub fn into_inner(self) -> T {
match self.to_enum() {
crate::E1::A(inner) => inner,
}
}
}
impl<T: AnyError> ErrorUnion<(TracedError<T>,)> {
pub fn traced(self) -> TracedError<T> {
self.into_inner()
}
}
impl ErrorUnion<(TracedError,)> {
pub fn traced_dyn(self) -> TracedError {
self.into_inner()
}
}
pub trait ReshapeUnion<S, E>
where
E: TypeSet,
{
fn widen<Other, Index>(self) -> Result<S, ErrorUnion<Other>>
where
Other: TypeSet,
Other::Variants: SupersetOf<E::Variants, Index>;
fn narrow<Target, Index>(
self,
) -> Result<
Target,
Result<
S,
ErrorUnion<<<E::Variants as Narrow<Target, Index>>::Remainder as TupleForm>::Tuple>,
>,
>
where
Target: 'static,
E::Variants: Narrow<Target, Index>;
}
impl<S, E> ReshapeUnion<S, E> for Result<S, ErrorUnion<E>>
where
E: TypeSet,
{
fn widen<Other, Index>(self) -> Result<S, ErrorUnion<Other>>
where
Other: TypeSet,
Other::Variants: SupersetOf<E::Variants, Index>,
{
self.map_err(|e| e.widen())
}
fn narrow<Target, Index>(
self,
) -> Result<
Target,
Result<
S,
ErrorUnion<<<E::Variants as Narrow<Target, Index>>::Remainder as TupleForm>::Tuple>,
>,
>
where
Target: 'static,
E::Variants: Narrow<Target, Index>,
{
match self {
Ok(value) => Err(Ok(value)),
Err(err) => match err.narrow() {
Ok(value) => return Ok(value),
Err(err) => Err(Err(err)),
},
}
}
}
pub trait Union<S, F> {
fn union<Index, Other>(self) -> Result<S, ErrorUnion<Other>>
where
Other: TypeSet,
Other::Variants: Contains<F, Index>;
}
impl<S, F: 'static> Union<S, F> for Result<S, F> {
fn union<Index, Other>(self) -> Result<S, ErrorUnion<Other>>
where
Other: TypeSet,
Other::Variants: Contains<F, Index>,
{
self.map_err(ErrorUnion::new)
}
}
pub trait IntoUnion<S, F> {
fn into_union<Index, Other>(self) -> Result<S, ErrorUnion<Other>>
where
Other: TypeSet,
Other::Variants: Contains<F, Index>;
}
impl<S, F1: 'static, F2: 'static> IntoUnion<S, F2> for Result<S, F1>
where
F1: Into<F2>,
{
fn into_union<Index, Other>(self) -> Result<S, ErrorUnion<Other>>
where
Other: TypeSet,
Other::Variants: Contains<F2, Index>,
{
self.map_err(|e| ErrorUnion::new(e.into()))
}
}