use crate::{
global_context::{current_scope_id, try_consume_context},
innerlude::provide_context,
use_hook, Element, IntoDynNode, Properties, ScopeId, Template, TemplateAttribute, TemplateNode,
VNode,
};
use std::{
any::{Any, TypeId},
backtrace::Backtrace,
cell::RefCell,
error::Error,
fmt::{Debug, Display},
rc::Rc,
};
pub struct CapturedPanic {
pub error: Box<dyn Any + 'static>,
}
impl Debug for CapturedPanic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CapturedPanic").finish()
}
}
pub fn use_error_boundary() -> ErrorBoundary {
use_hook(|| provide_context(ErrorBoundary::new()))
}
#[derive(Debug, Clone, Default)]
pub struct ErrorBoundary {
inner: Rc<ErrorBoundaryInner>,
}
pub struct ErrorBoundaryInner {
error: RefCell<Option<CapturedError>>,
_id: ScopeId,
}
impl Debug for ErrorBoundaryInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ErrorBoundaryInner")
.field("error", &self.error)
.finish()
}
}
pub trait AnyDebug: Any + Debug {
fn as_any(&self) -> &dyn Any;
}
impl<T: Any + Debug> AnyDebug for T {
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Debug)]
pub struct CapturedError {
pub error: Box<dyn AnyDebug + 'static>,
pub backtrace: Backtrace,
pub scope: ScopeId,
}
impl Display for CapturedError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"Encountered error: {:?}\nIn scope: {:?}\nBacktrace: {}",
self.error, self.scope, self.backtrace
))
}
}
impl Error for CapturedError {}
impl CapturedError {
pub fn downcast<T: 'static>(&self) -> Option<&T> {
if TypeId::of::<T>() == self.error.type_id() {
self.error.as_any().downcast_ref::<T>()
} else {
None
}
}
}
impl Default for ErrorBoundaryInner {
fn default() -> Self {
Self {
error: RefCell::new(None),
_id: current_scope_id()
.expect("Cannot create an error boundary outside of a component's scope."),
}
}
}
impl ErrorBoundary {
pub fn new() -> Self {
Self::default()
}
pub(crate) fn new_in_scope(scope: ScopeId) -> Self {
Self {
inner: Rc::new(ErrorBoundaryInner {
error: RefCell::new(None),
_id: scope,
}),
}
}
pub fn insert_error(&self, scope: ScopeId, error: impl Debug + 'static, backtrace: Backtrace) {
self.inner.error.replace(Some(CapturedError {
error: Box::new(error),
scope,
backtrace,
}));
if self.inner._id != ScopeId::ROOT {
self.inner._id.needs_update();
}
}
pub fn take_error(&self) -> Option<CapturedError> {
self.inner.error.take()
}
}
pub trait Throw<S = ()>: Sized {
type Out;
fn throw(self) -> Option<Self::Out>;
fn throw_with<D: Debug + 'static>(self, e: impl FnOnce() -> D) -> Option<Self::Out> {
self.throw().or_else(|| throw_error(e()))
}
}
pub(crate) fn throw_error<T>(e: impl Debug + 'static) -> Option<T> {
if let Some(cx) = try_consume_context::<ErrorBoundary>() {
match current_scope_id() {
Some(id) => cx.insert_error(id, Box::new(e), Backtrace::capture()),
None => {
tracing::error!("Cannot throw error outside of a component's scope.")
}
}
}
None
}
impl<'a, T, O: Debug + 'static, E: ToOwned<Owned = O>> Throw for &'a Result<T, E> {
type Out = &'a T;
fn throw(self) -> Option<Self::Out> {
match self {
Ok(t) => Some(t),
Err(e) => throw_error(e.to_owned()),
}
}
fn throw_with<D: Debug + 'static>(self, err: impl FnOnce() -> D) -> Option<Self::Out> {
match self {
Ok(t) => Some(t),
Err(_e) => throw_error(err()),
}
}
}
impl<T, E: Debug + 'static> Throw for Result<T, E> {
type Out = T;
fn throw(self) -> Option<T> {
match self {
Ok(t) => Some(t),
Err(e) => throw_error(e),
}
}
fn throw_with<D: Debug + 'static>(self, error: impl FnOnce() -> D) -> Option<Self::Out> {
self.ok().or_else(|| throw_error(error()))
}
}
impl<T> Throw for Option<T> {
type Out = T;
fn throw(self) -> Option<T> {
self.or_else(|| throw_error("Attempted to unwrap a None value."))
}
fn throw_with<D: Debug + 'static>(self, error: impl FnOnce() -> D) -> Option<Self::Out> {
self.or_else(|| throw_error(error()))
}
}
#[derive(Clone)]
pub struct ErrorHandler(Rc<dyn Fn(CapturedError) -> Element>);
impl<F: Fn(CapturedError) -> Element + 'static> From<F> for ErrorHandler {
fn from(value: F) -> Self {
Self(Rc::new(value))
}
}
fn default_handler(error: CapturedError) -> Element {
static TEMPLATE: Template = Template {
name: "error_handle.rs:42:5:884",
roots: &[TemplateNode::Element {
tag: "pre",
namespace: None,
attrs: &[TemplateAttribute::Static {
name: "color",
namespace: Some("style"),
value: "red",
}],
children: &[TemplateNode::DynamicText { id: 0usize }],
}],
node_paths: &[&[0u8, 0u8]],
attr_paths: &[],
};
Some(VNode::new(
None,
TEMPLATE,
Box::new([error.to_string().into_dyn_node()]),
Default::default(),
))
}
#[derive(Clone)]
pub struct ErrorBoundaryProps {
children: Element,
handle_error: ErrorHandler,
}
impl ErrorBoundaryProps {
#[allow(dead_code)]
pub fn builder() -> ErrorBoundaryPropsBuilder<((), ())> {
ErrorBoundaryPropsBuilder { fields: ((), ()) }
}
}
#[must_use]
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
pub struct ErrorBoundaryPropsBuilder<TypedBuilderFields> {
fields: TypedBuilderFields,
}
impl<TypedBuilderFields> Clone for ErrorBoundaryPropsBuilder<TypedBuilderFields>
where
TypedBuilderFields: Clone,
{
fn clone(&self) -> Self {
Self {
fields: self.fields.clone(),
}
}
}
impl Properties for ErrorBoundaryProps {
type Builder = ErrorBoundaryPropsBuilder<((), ())>;
fn builder() -> Self::Builder {
ErrorBoundaryProps::builder()
}
fn memoize(&mut self, _: &Self) -> bool {
false
}
}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
pub trait ErrorBoundaryPropsBuilder_Optional<T> {
fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
}
impl<T> ErrorBoundaryPropsBuilder_Optional<T> for () {
fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
default()
}
}
impl<T> ErrorBoundaryPropsBuilder_Optional<T> for (T,) {
fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
self.0
}
}
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl<__handle_error> ErrorBoundaryPropsBuilder<((), __handle_error)> {
pub fn children(
self,
children: Element,
) -> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
let children = (children,);
let (_, handle_error) = self.fields;
ErrorBoundaryPropsBuilder {
fields: (children, handle_error),
}
}
}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
pub enum ErrorBoundaryPropsBuilder_Error_Repeated_field_children {}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl<__handle_error> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
#[deprecated(note = "Repeated field children")]
pub fn children(
self,
_: ErrorBoundaryPropsBuilder_Error_Repeated_field_children,
) -> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
self
}
}
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl<__children> ErrorBoundaryPropsBuilder<(__children, ())> {
pub fn handle_error(
self,
handle_error: impl ::core::convert::Into<ErrorHandler>,
) -> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
let handle_error = (handle_error.into(),);
let (children, _) = self.fields;
ErrorBoundaryPropsBuilder {
fields: (children, handle_error),
}
}
}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
pub enum ErrorBoundaryPropsBuilder_Error_Repeated_field_handle_error {}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl<__children> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
#[deprecated(note = "Repeated field handle_error")]
pub fn handle_error(
self,
_: ErrorBoundaryPropsBuilder_Error_Repeated_field_handle_error,
) -> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
self
}
}
#[allow(dead_code, non_camel_case_types, missing_docs)]
impl<
__handle_error: ErrorBoundaryPropsBuilder_Optional<ErrorHandler>,
__children: ErrorBoundaryPropsBuilder_Optional<Element>,
> ErrorBoundaryPropsBuilder<(__children, __handle_error)>
{
pub fn build(self) -> ErrorBoundaryProps {
let (children, handle_error) = self.fields;
let children = ErrorBoundaryPropsBuilder_Optional::into_value(children, || {
::core::default::Default::default()
});
let handle_error = ErrorBoundaryPropsBuilder_Optional::into_value(handle_error, || {
ErrorHandler(Rc::new(default_handler))
});
ErrorBoundaryProps {
children,
handle_error,
}
}
}
#[allow(non_upper_case_globals, non_snake_case)]
pub fn ErrorBoundary(props: ErrorBoundaryProps) -> Element {
let error_boundary = use_error_boundary();
match error_boundary.take_error() {
Some(error) => (props.handle_error.0)(error),
None => Some({
static TEMPLATE: Template = Template {
name: "examples/error_handle.rs:81:17:2342",
roots: &[TemplateNode::Dynamic { id: 0usize }],
node_paths: &[&[0u8]],
attr_paths: &[],
};
VNode::new(
None,
TEMPLATE,
Box::new([(props.children).into_dyn_node()]),
Default::default(),
)
}),
}
}