1use crate::{
2 innerlude::{provide_context, CapturedError},
3 try_consume_context, use_hook, Element, IntoDynNode, Properties, ReactiveContext, Subscribers,
4 Template, TemplateAttribute, TemplateNode, VNode,
5};
6use std::{
7 any::Any,
8 cell::RefCell,
9 fmt::{Debug, Display},
10 rc::Rc,
11};
12
13#[macro_export]
15macro_rules! bail {
16 ($msg:literal $(,)?) => {
17 return $crate::internal::Err($crate::internal::__anyhow!($msg).into())
18 };
19 ($err:expr $(,)?) => {
20 return $crate::internal::Err($crate::internal::__anyhow!($err).into())
21 };
22 ($fmt:expr, $($arg:tt)*) => {
23 return $crate::internal::Err($crate::internal::__anyhow!($fmt, $($arg)*).into())
24 };
25}
26
27pub(crate) struct CapturedPanic(pub(crate) Box<dyn Any + Send + 'static>);
35unsafe impl Sync for CapturedPanic {}
36impl std::error::Error for CapturedPanic {}
37impl Debug for CapturedPanic {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 f.debug_struct("CapturedPanic").finish()
40 }
41}
42
43impl Display for CapturedPanic {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 f.write_fmt(format_args!("Encountered panic: {:?}", self.0))
46 }
47}
48
49#[derive(Clone, Copy)]
53struct CreateErrorBoundary(fn() -> ErrorContext);
54
55impl Default for CreateErrorBoundary {
56 fn default() -> Self {
57 Self(|| ErrorContext::new(None))
58 }
59}
60
61#[doc(hidden)]
64pub fn provide_create_error_boundary(create_error_boundary: fn() -> ErrorContext) {
65 provide_context(CreateErrorBoundary(create_error_boundary));
66}
67
68fn create_error_boundary() -> ErrorContext {
70 let create_error_boundary = try_consume_context::<CreateErrorBoundary>().unwrap_or_default();
71 (create_error_boundary.0)()
72}
73
74pub fn use_error_boundary_provider() -> ErrorContext {
77 use_hook(|| provide_context(create_error_boundary()))
78}
79
80#[derive(Clone)]
82pub struct ErrorContext {
83 error: Rc<RefCell<Option<CapturedError>>>,
84 subscribers: Subscribers,
85}
86
87impl Debug for ErrorContext {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 f.debug_struct("ErrorContext")
90 .field("error", &self.error)
91 .finish()
92 }
93}
94
95impl PartialEq for ErrorContext {
96 fn eq(&self, other: &Self) -> bool {
97 Rc::ptr_eq(&self.error, &other.error)
98 }
99}
100
101impl ErrorContext {
102 pub fn new(error: Option<CapturedError>) -> Self {
104 Self {
105 error: Rc::new(RefCell::new(error)),
106 subscribers: Subscribers::new(),
107 }
108 }
109
110 pub fn error(&self) -> Option<CapturedError> {
113 if let Some(rc) = ReactiveContext::current() {
116 self.subscribers.add(rc);
117 }
118
119 self.error.borrow().clone()
120 }
121
122 pub fn insert_error(&self, error: CapturedError) {
124 self.error.borrow_mut().replace(error);
125 self.mark_dirty()
126 }
127
128 pub fn clear_errors(&self) {
130 self.error.borrow_mut().take();
131 self.mark_dirty();
132 }
133
134 fn mark_dirty(&self) {
136 let mut this_subscribers_vec = Vec::new();
137 self.subscribers
138 .visit(|subscriber| this_subscribers_vec.push(*subscriber));
139 for subscriber in this_subscribers_vec {
140 self.subscribers.remove(&subscriber);
141 subscriber.mark_dirty();
142 }
143 }
144}
145
146#[allow(clippy::type_complexity)]
147#[derive(Clone)]
148pub struct ErrorHandler(Rc<dyn Fn(ErrorContext) -> Element>);
149impl<F: Fn(ErrorContext) -> Element + 'static> From<F> for ErrorHandler {
150 fn from(value: F) -> Self {
151 Self(Rc::new(value))
152 }
153}
154
155fn default_handler(errors: ErrorContext) -> Element {
156 static TEMPLATE: Template = Template {
157 roots: &[TemplateNode::Element {
158 tag: "div",
159 namespace: None,
160 attrs: &[TemplateAttribute::Static {
161 name: "color",
162 namespace: Some("style"),
163 value: "red",
164 }],
165 children: &[TemplateNode::Dynamic { id: 0usize }],
166 }],
167 node_paths: &[&[0u8, 0u8]],
168 attr_paths: &[],
169 };
170 std::result::Result::Ok(VNode::new(
171 None,
172 TEMPLATE,
173 Box::new([errors
174 .error()
175 .iter()
176 .map(|e| {
177 static TEMPLATE: Template = Template {
178 roots: &[TemplateNode::Element {
179 tag: "pre",
180 namespace: None,
181 attrs: &[],
182 children: &[TemplateNode::Dynamic { id: 0usize }],
183 }],
184 node_paths: &[&[0u8, 0u8]],
185 attr_paths: &[],
186 };
187 VNode::new(
188 None,
189 TEMPLATE,
190 Box::new([e.to_string().into_dyn_node()]),
191 Default::default(),
192 )
193 })
194 .into_dyn_node()]),
195 Default::default(),
196 ))
197}
198
199#[derive(Clone)]
200pub struct ErrorBoundaryProps {
201 children: Element,
202 handle_error: ErrorHandler,
203}
204
205#[allow(non_upper_case_globals, non_snake_case)]
311pub fn ErrorBoundary(props: ErrorBoundaryProps) -> Element {
312 let error_boundary = use_error_boundary_provider();
313 let errors = error_boundary.error();
314 let has_errors = errors.is_some();
315
316 drop(errors);
318
319 if has_errors {
320 (props.handle_error.0)(error_boundary.clone())
321 } else {
322 std::result::Result::Ok({
323 static TEMPLATE: Template = Template {
324 roots: &[TemplateNode::Dynamic { id: 0usize }],
325 node_paths: &[&[0u8]],
326 attr_paths: &[],
327 };
328 VNode::new(
329 None,
330 TEMPLATE,
331 Box::new([(props.children).into_dyn_node()]),
332 Default::default(),
333 )
334 })
335 }
336}
337
338impl ErrorBoundaryProps {
339 #[allow(dead_code)]
345 pub fn builder() -> ErrorBoundaryPropsBuilder<((), ())> {
346 ErrorBoundaryPropsBuilder { fields: ((), ()) }
347 }
348}
349
350#[must_use]
351#[doc(hidden)]
352#[allow(dead_code, non_camel_case_types, non_snake_case)]
353pub struct ErrorBoundaryPropsBuilder<TypedBuilderFields> {
354 fields: TypedBuilderFields,
355}
356impl<TypedBuilderFields> Clone for ErrorBoundaryPropsBuilder<TypedBuilderFields>
357where
358 TypedBuilderFields: Clone,
359{
360 fn clone(&self) -> Self {
361 Self {
362 fields: self.fields.clone(),
363 }
364 }
365}
366impl Properties for ErrorBoundaryProps {
367 type Builder = ErrorBoundaryPropsBuilder<((), ())>;
368 fn builder() -> Self::Builder {
369 ErrorBoundaryProps::builder()
370 }
371 fn memoize(&mut self, other: &Self) -> bool {
372 *self = other.clone();
373 false
374 }
375}
376#[doc(hidden)]
377#[allow(dead_code, non_camel_case_types, non_snake_case)]
378pub trait ErrorBoundaryPropsBuilder_Optional<T> {
379 fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
380}
381impl<T> ErrorBoundaryPropsBuilder_Optional<T> for () {
382 fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
383 default()
384 }
385}
386impl<T> ErrorBoundaryPropsBuilder_Optional<T> for (T,) {
387 fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
388 self.0
389 }
390}
391#[allow(dead_code, non_camel_case_types, missing_docs)]
392impl<__handle_error> ErrorBoundaryPropsBuilder<((), __handle_error)> {
393 pub fn children(
394 self,
395 children: Element,
396 ) -> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
397 let children = (children,);
398 let (_, handle_error) = self.fields;
399 ErrorBoundaryPropsBuilder {
400 fields: (children, handle_error),
401 }
402 }
403}
404#[doc(hidden)]
405#[allow(dead_code, non_camel_case_types, non_snake_case)]
406pub enum ErrorBoundaryPropsBuilder_Error_Repeated_field_children {}
407#[doc(hidden)]
408#[allow(dead_code, non_camel_case_types, missing_docs)]
409impl<__handle_error> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
410 #[deprecated(note = "Repeated field children")]
411 pub fn children(
412 self,
413 _: ErrorBoundaryPropsBuilder_Error_Repeated_field_children,
414 ) -> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
415 self
416 }
417}
418#[allow(dead_code, non_camel_case_types, missing_docs)]
419impl<__children> ErrorBoundaryPropsBuilder<(__children, ())> {
420 pub fn handle_error(
421 self,
422 handle_error: impl ::core::convert::Into<ErrorHandler>,
423 ) -> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
424 let handle_error = (handle_error.into(),);
425 let (children, _) = self.fields;
426 ErrorBoundaryPropsBuilder {
427 fields: (children, handle_error),
428 }
429 }
430}
431#[doc(hidden)]
432#[allow(dead_code, non_camel_case_types, non_snake_case)]
433pub enum ErrorBoundaryPropsBuilder_Error_Repeated_field_handle_error {}
434#[doc(hidden)]
435#[allow(dead_code, non_camel_case_types, missing_docs)]
436impl<__children> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
437 #[deprecated(note = "Repeated field handle_error")]
438 pub fn handle_error(
439 self,
440 _: ErrorBoundaryPropsBuilder_Error_Repeated_field_handle_error,
441 ) -> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
442 self
443 }
444}
445#[allow(dead_code, non_camel_case_types, missing_docs)]
446impl<
447 __handle_error: ErrorBoundaryPropsBuilder_Optional<ErrorHandler>,
448 __children: ErrorBoundaryPropsBuilder_Optional<Element>,
449 > ErrorBoundaryPropsBuilder<(__children, __handle_error)>
450{
451 pub fn build(self) -> ErrorBoundaryProps {
452 let (children, handle_error) = self.fields;
453 let children = ErrorBoundaryPropsBuilder_Optional::into_value(children, VNode::empty);
454 let handle_error = ErrorBoundaryPropsBuilder_Optional::into_value(handle_error, || {
455 ErrorHandler(Rc::new(default_handler))
456 });
457 ErrorBoundaryProps {
458 children,
459 handle_error,
460 }
461 }
462}