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