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}
204impl ErrorBoundaryProps {
205 #[allow(dead_code)]
211 pub fn builder() -> ErrorBoundaryPropsBuilder<((), ())> {
212 ErrorBoundaryPropsBuilder { fields: ((), ()) }
213 }
214}
215
216#[must_use]
217#[doc(hidden)]
218#[allow(dead_code, non_camel_case_types, non_snake_case)]
219pub struct ErrorBoundaryPropsBuilder<TypedBuilderFields> {
220 fields: TypedBuilderFields,
221}
222impl<TypedBuilderFields> Clone for ErrorBoundaryPropsBuilder<TypedBuilderFields>
223where
224 TypedBuilderFields: Clone,
225{
226 fn clone(&self) -> Self {
227 Self {
228 fields: self.fields.clone(),
229 }
230 }
231}
232impl Properties for ErrorBoundaryProps {
233 type Builder = ErrorBoundaryPropsBuilder<((), ())>;
234 fn builder() -> Self::Builder {
235 ErrorBoundaryProps::builder()
236 }
237 fn memoize(&mut self, other: &Self) -> bool {
238 *self = other.clone();
239 false
240 }
241}
242#[doc(hidden)]
243#[allow(dead_code, non_camel_case_types, non_snake_case)]
244pub trait ErrorBoundaryPropsBuilder_Optional<T> {
245 fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
246}
247impl<T> ErrorBoundaryPropsBuilder_Optional<T> for () {
248 fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
249 default()
250 }
251}
252impl<T> ErrorBoundaryPropsBuilder_Optional<T> for (T,) {
253 fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
254 self.0
255 }
256}
257#[allow(dead_code, non_camel_case_types, missing_docs)]
258impl<__handle_error> ErrorBoundaryPropsBuilder<((), __handle_error)> {
259 pub fn children(
260 self,
261 children: Element,
262 ) -> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
263 let children = (children,);
264 let (_, handle_error) = self.fields;
265 ErrorBoundaryPropsBuilder {
266 fields: (children, handle_error),
267 }
268 }
269}
270#[doc(hidden)]
271#[allow(dead_code, non_camel_case_types, non_snake_case)]
272pub enum ErrorBoundaryPropsBuilder_Error_Repeated_field_children {}
273#[doc(hidden)]
274#[allow(dead_code, non_camel_case_types, missing_docs)]
275impl<__handle_error> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
276 #[deprecated(note = "Repeated field children")]
277 pub fn children(
278 self,
279 _: ErrorBoundaryPropsBuilder_Error_Repeated_field_children,
280 ) -> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
281 self
282 }
283}
284#[allow(dead_code, non_camel_case_types, missing_docs)]
285impl<__children> ErrorBoundaryPropsBuilder<(__children, ())> {
286 pub fn handle_error(
287 self,
288 handle_error: impl ::core::convert::Into<ErrorHandler>,
289 ) -> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
290 let handle_error = (handle_error.into(),);
291 let (children, _) = self.fields;
292 ErrorBoundaryPropsBuilder {
293 fields: (children, handle_error),
294 }
295 }
296}
297#[doc(hidden)]
298#[allow(dead_code, non_camel_case_types, non_snake_case)]
299pub enum ErrorBoundaryPropsBuilder_Error_Repeated_field_handle_error {}
300#[doc(hidden)]
301#[allow(dead_code, non_camel_case_types, missing_docs)]
302impl<__children> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
303 #[deprecated(note = "Repeated field handle_error")]
304 pub fn handle_error(
305 self,
306 _: ErrorBoundaryPropsBuilder_Error_Repeated_field_handle_error,
307 ) -> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
308 self
309 }
310}
311#[allow(dead_code, non_camel_case_types, missing_docs)]
312impl<
313 __handle_error: ErrorBoundaryPropsBuilder_Optional<ErrorHandler>,
314 __children: ErrorBoundaryPropsBuilder_Optional<Element>,
315 > ErrorBoundaryPropsBuilder<(__children, __handle_error)>
316{
317 pub fn build(self) -> ErrorBoundaryProps {
318 let (children, handle_error) = self.fields;
319 let children = ErrorBoundaryPropsBuilder_Optional::into_value(children, VNode::empty);
320 let handle_error = ErrorBoundaryPropsBuilder_Optional::into_value(handle_error, || {
321 ErrorHandler(Rc::new(default_handler))
322 });
323 ErrorBoundaryProps {
324 children,
325 handle_error,
326 }
327 }
328}
329
330#[allow(non_upper_case_globals, non_snake_case)]
436pub fn ErrorBoundary(props: ErrorBoundaryProps) -> Element {
437 let error_boundary = use_error_boundary_provider();
438 let errors = error_boundary.error();
439 let has_errors = errors.is_some();
440
441 drop(errors);
443
444 if has_errors {
445 (props.handle_error.0)(error_boundary.clone())
446 } else {
447 std::result::Result::Ok({
448 static TEMPLATE: Template = Template {
449 roots: &[TemplateNode::Dynamic { id: 0usize }],
450 node_paths: &[&[0u8]],
451 attr_paths: &[],
452 };
453 VNode::new(
454 None,
455 TEMPLATE,
456 Box::new([(props.children).into_dyn_node()]),
457 Default::default(),
458 )
459 })
460 }
461}