use crate::{ScopeId, ScopeState};
use std::{
any::{Any, TypeId},
cell::RefCell,
fmt::Debug,
};
pub struct ErrorBoundary {
error: RefCell<Option<CapturedError>>,
_id: ScopeId,
}
pub struct CapturedError {
pub error: Box<dyn Debug + 'static>,
pub scope: ScopeId,
}
impl CapturedError {
pub fn downcast<T: 'static>(&self) -> Option<&T> {
if TypeId::of::<T>() == self.error.type_id() {
let raw = self.error.as_ref() as *const _ as *const T;
Some(unsafe { &*raw })
} else {
None
}
}
}
impl ErrorBoundary {
pub fn new(id: ScopeId) -> Self {
Self {
error: RefCell::new(None),
_id: id,
}
}
pub fn insert_error(&self, scope: ScopeId, error: Box<dyn Debug + 'static>) {
self.error.replace(Some(CapturedError { error, scope }));
}
}
pub trait Throw<S = ()>: Sized {
type Out;
fn throw(self, cx: &ScopeState) -> Option<Self::Out>;
fn throw_with<D: Debug + 'static>(
self,
cx: &ScopeState,
e: impl FnOnce() -> D,
) -> Option<Self::Out>;
}
impl<'a, T, O: Debug + 'static, E: ToOwned<Owned = O>> Throw for &'a Result<T, E> {
type Out = &'a T;
fn throw(self, cx: &ScopeState) -> Option<Self::Out> {
match self {
Ok(t) => Some(t),
Err(e) => {
cx.throw(e.to_owned());
None
}
}
}
fn throw_with<D: Debug + 'static>(
self,
cx: &ScopeState,
err: impl FnOnce() -> D,
) -> Option<Self::Out> {
match self {
Ok(t) => Some(t),
Err(_e) => {
cx.throw(err());
None
}
}
}
}
impl<T, E: Debug + 'static> Throw for Result<T, E> {
type Out = T;
fn throw(self, cx: &ScopeState) -> Option<T> {
match self {
Ok(t) => Some(t),
Err(e) => {
cx.throw(e);
None
}
}
}
fn throw_with<D: Debug + 'static>(
self,
cx: &ScopeState,
error: impl FnOnce() -> D,
) -> Option<Self::Out> {
self.ok().or_else(|| {
cx.throw(error());
None
})
}
}
impl<T> Throw for Option<T> {
type Out = T;
fn throw(self, cx: &ScopeState) -> Option<T> {
self.or_else(|| {
cx.throw("None error.");
None
})
}
fn throw_with<D: Debug + 'static>(
self,
cx: &ScopeState,
error: impl FnOnce() -> D,
) -> Option<Self::Out> {
self.or_else(|| {
cx.throw(error());
None
})
}
}