use crate::{
DynamicValues, Element, IntoDynNode, ReactiveContext, Subscribers, Template, VNode,
innerlude::{CapturedError, provide_context},
try_consume_context, use_hook,
};
use dioxus_core_template::{TemplateRawTree, TemplateStorage};
use std::{
any::Any,
cell::RefCell,
fmt::{Debug, Display},
rc::Rc,
};
static ERROR_DYNAMIC_TREE: TemplateRawTree = TemplateRawTree::DynamicNode;
#[macro_export]
macro_rules! bail {
($msg:literal $(,)?) => {
return ::std::result::Result::Err($crate::internal::__anyhow!($msg).into())
};
($err:expr $(,)?) => {
return ::std::result::Result::Err($crate::internal::__anyhow!($err).into())
};
($fmt:expr, $($arg:tt)*) => {
return ::std::result::Result::Err($crate::internal::__anyhow!($fmt, $($arg)*).into())
};
}
pub(crate) struct CapturedPanic(pub(crate) Box<dyn Any + Send + 'static>);
unsafe impl Sync for CapturedPanic {}
impl std::error::Error for CapturedPanic {}
impl Debug for CapturedPanic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CapturedPanic").finish()
}
}
impl Display for CapturedPanic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("Encountered panic: {:?}", self.0))
}
}
#[derive(Clone, Copy)]
struct CreateErrorBoundary(fn() -> ErrorContext);
impl Default for CreateErrorBoundary {
fn default() -> Self {
Self(|| ErrorContext::new(None))
}
}
pub fn provide_create_error_boundary(create_error_boundary: fn() -> ErrorContext) {
provide_context(CreateErrorBoundary(create_error_boundary));
}
fn create_error_boundary() -> ErrorContext {
let create_error_boundary = try_consume_context::<CreateErrorBoundary>().unwrap_or_default();
(create_error_boundary.0)()
}
pub fn use_error_boundary_provider() -> ErrorContext {
use_hook(|| provide_context(create_error_boundary()))
}
#[derive(Clone)]
pub struct ErrorContext {
error: Rc<RefCell<Option<CapturedError>>>,
subscribers: Subscribers,
}
impl Debug for ErrorContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ErrorContext")
.field("error", &self.error)
.finish()
}
}
impl PartialEq for ErrorContext {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.error, &other.error)
}
}
impl ErrorContext {
pub fn new(error: Option<CapturedError>) -> Self {
Self {
error: Rc::new(RefCell::new(error)),
subscribers: Subscribers::new(),
}
}
pub fn error(&self) -> Option<CapturedError> {
if let Some(rc) = ReactiveContext::current() {
self.subscribers.add(rc);
}
self.error.borrow().clone()
}
pub fn insert_error(&self, error: CapturedError) {
self.error.borrow_mut().replace(error);
self.mark_dirty()
}
pub fn clear_errors(&self) {
self.error.borrow_mut().take();
self.mark_dirty();
}
fn mark_dirty(&self) {
let mut this_subscribers_vec = Vec::new();
self.subscribers
.visit(|subscriber| this_subscribers_vec.push(*subscriber));
for subscriber in this_subscribers_vec {
self.subscribers.remove(&subscriber);
subscriber.mark_dirty();
}
}
}
#[allow(clippy::type_complexity)]
#[derive(Clone)]
pub struct ErrorHandler(Rc<dyn Fn(ErrorContext) -> Element>);
impl<F: Fn(ErrorContext) -> Element + 'static> From<F> for ErrorHandler {
fn from(value: F) -> Self {
Self(Rc::new(value))
}
}
fn default_handler(errors: ErrorContext) -> Element {
static ATTRS: TemplateRawTree = TemplateRawTree::StaticAttr {
name: "color",
value: "red",
namespace: Some("style"),
};
static TREE: TemplateRawTree = TemplateRawTree::Element {
tag: "div",
namespace: None,
attrs: &ATTRS,
children: &ERROR_DYNAMIC_TREE,
};
static STORAGE: TemplateStorage<8, 4, 2> = TemplateStorage::build_from_tree(&TREE);
static TEMPLATE: Template = STORAGE.as_template();
std::result::Result::Ok(VNode::new(
TEMPLATE,
DynamicValues::from_parts(
None,
Box::new([errors
.error()
.iter()
.map(|e| {
static TREE: TemplateRawTree = TemplateRawTree::Element {
tag: "pre",
namespace: None,
attrs: &TemplateRawTree::Empty,
children: &ERROR_DYNAMIC_TREE,
};
static STORAGE: TemplateStorage<4, 1, 2> =
TemplateStorage::build_from_tree(&TREE);
static INNER_TEMPLATE: Template = STORAGE.as_template();
VNode::new(
INNER_TEMPLATE,
DynamicValues::from_parts(
None,
Box::new([e.to_string().into_dyn_node()]),
Box::new([]),
),
)
})
.into_dyn_node()]),
Box::new([]),
),
))
}
#[derive(dioxus_core_macro::Props, Clone)]
pub struct ErrorBoundaryProps {
children: Element,
#[props(into, default = ErrorHandler(Rc::new(default_handler)))]
handle_error: ErrorHandler,
}
impl PartialEq for ErrorBoundaryProps {
fn eq(&self, other: &Self) -> bool {
self.children == other.children && Rc::ptr_eq(&self.handle_error.0, &other.handle_error.0)
}
}
#[allow(non_upper_case_globals, non_snake_case)]
pub fn ErrorBoundary(props: ErrorBoundaryProps) -> Element {
let error_boundary = use_error_boundary_provider();
let errors = error_boundary.error();
let has_errors = errors.is_some();
drop(errors);
if has_errors {
(props.handle_error.0)(error_boundary.clone())
} else {
std::result::Result::Ok({
static STORAGE: TemplateStorage<1, 1, 1> =
TemplateStorage::build_from_tree(&ERROR_DYNAMIC_TREE);
static TEMPLATE: Template = STORAGE.as_template();
VNode::new(
TEMPLATE,
DynamicValues::from_parts(
None,
Box::new([(props.children).into_dyn_node()]),
Box::new([]),
),
)
})
}
}