1use crate::{
2 global_context::current_scope_id, innerlude::provide_context, use_hook, Element, IntoDynNode,
3 Properties, ScopeId, Template, TemplateAttribute, TemplateNode, VNode,
4};
5use std::{
6 any::Any,
7 backtrace::Backtrace,
8 cell::{Ref, RefCell},
9 error::Error,
10 fmt::{Debug, Display},
11 rc::Rc,
12 str::FromStr,
13};
14
15pub struct CapturedPanic {
23 #[allow(dead_code)]
24 pub error: Box<dyn Any + 'static>,
26}
27
28impl Debug for CapturedPanic {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 f.debug_struct("CapturedPanic").finish()
31 }
32}
33
34impl Display for CapturedPanic {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 f.write_fmt(format_args!("Encountered panic: {:?}", self.error))
37 }
38}
39
40impl Error for CapturedPanic {}
41
42pub fn provide_error_boundary() -> ErrorContext {
44 provide_context(ErrorContext::new(
45 Vec::new(),
46 current_scope_id().unwrap_or_else(|e| panic!("{}", e)),
47 ))
48}
49
50pub trait AnyError {
52 fn as_any(&self) -> &dyn Any;
53 fn as_error(&self) -> &dyn Error;
54}
55
56struct DisplayError(DisplayErrorInner);
58
59impl<E: Display + 'static> From<E> for DisplayError {
60 fn from(e: E) -> Self {
61 Self(DisplayErrorInner(Box::new(e)))
62 }
63}
64
65struct DisplayErrorInner(Box<dyn Display>);
66impl Display for DisplayErrorInner {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 self.0.fmt(f)
69 }
70}
71
72impl Debug for DisplayErrorInner {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 self.0.fmt(f)
75 }
76}
77
78impl Error for DisplayErrorInner {}
79
80impl AnyError for DisplayError {
81 fn as_any(&self) -> &dyn Any {
82 &self.0 .0
83 }
84
85 fn as_error(&self) -> &dyn Error {
86 &self.0
87 }
88}
89
90pub trait Context<T, E>: private::Sealed {
94 fn show(self, display_error: impl FnOnce(&E) -> Element) -> Result<T>;
113
114 fn context<C: Display + 'static>(self, context: C) -> Result<T>;
127
128 fn with_context<C: Display + 'static>(self, context: impl FnOnce() -> C) -> Result<T>;
141}
142
143impl<T, E> Context<T, E> for std::result::Result<T, E>
144where
145 E: Error + 'static,
146{
147 fn show(self, display_error: impl FnOnce(&E) -> Element) -> Result<T> {
148 match self {
150 std::result::Result::Ok(value) => Ok(value),
151 Err(error) => {
152 let render = display_error(&error).unwrap_or_default();
153 let mut error: CapturedError = error.into();
154 error.render = render;
155 Err(error)
156 }
157 }
158 }
159
160 fn context<C: Display + 'static>(self, context: C) -> Result<T> {
161 self.with_context(|| context)
162 }
163
164 fn with_context<C: Display + 'static>(self, context: impl FnOnce() -> C) -> Result<T> {
165 match self {
167 std::result::Result::Ok(value) => Ok(value),
168 Err(error) => {
169 let mut error: CapturedError = error.into();
170 error.context.push(Rc::new(AdditionalErrorContext {
171 backtrace: Backtrace::capture(),
172 context: Box::new(context()),
173 scope: current_scope_id().ok(),
174 }));
175 Err(error)
176 }
177 }
178 }
179}
180
181impl<T> Context<T, CapturedError> for Option<T> {
182 fn show(self, display_error: impl FnOnce(&CapturedError) -> Element) -> Result<T> {
183 match self {
185 Some(value) => Ok(value),
186 None => {
187 let mut error = CapturedError::from_display("Value was none");
188 let render = display_error(&error).unwrap_or_default();
189 error.render = render;
190 Err(error)
191 }
192 }
193 }
194
195 fn context<C: Display + 'static>(self, context: C) -> Result<T> {
196 self.with_context(|| context)
197 }
198
199 fn with_context<C: Display + 'static>(self, context: impl FnOnce() -> C) -> Result<T> {
200 match self {
202 Some(value) => Ok(value),
203 None => {
204 let error = CapturedError::from_display(context());
205 Err(error)
206 }
207 }
208 }
209}
210
211pub(crate) mod private {
212 use super::*;
213
214 pub trait Sealed {}
215
216 impl<T, E> Sealed for std::result::Result<T, E> where E: Error {}
217 impl<T> Sealed for Option<T> {}
218}
219
220impl<T: Any + Error> AnyError for T {
221 fn as_any(&self) -> &dyn Any {
222 self
223 }
224
225 fn as_error(&self) -> &dyn Error {
226 self
227 }
228}
229
230#[derive(Debug, Clone)]
232pub struct ErrorContext {
233 errors: Rc<RefCell<Vec<CapturedError>>>,
234 id: ScopeId,
235}
236
237impl PartialEq for ErrorContext {
238 fn eq(&self, other: &Self) -> bool {
239 Rc::ptr_eq(&self.errors, &other.errors)
240 }
241}
242
243impl ErrorContext {
244 pub(crate) fn new(errors: Vec<CapturedError>, id: ScopeId) -> Self {
246 Self {
247 errors: Rc::new(RefCell::new(errors)),
248 id,
249 }
250 }
251
252 pub fn errors(&self) -> Ref<[CapturedError]> {
254 Ref::map(self.errors.borrow(), |errors| errors.as_slice())
255 }
256
257 pub fn show(&self) -> Option<Element> {
259 self.errors.borrow().iter().find_map(|task| task.show())
260 }
261
262 pub fn insert_error(&self, error: CapturedError) {
264 self.errors.borrow_mut().push(error);
265 self.id.needs_update();
266 }
267
268 pub fn clear_errors(&self) {
270 self.errors.borrow_mut().clear();
271 self.id.needs_update();
272 }
273}
274
275struct AdditionalErrorContext {
278 backtrace: Backtrace,
279 context: Box<dyn Display>,
280 scope: Option<ScopeId>,
281}
282
283impl Debug for AdditionalErrorContext {
284 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
285 f.debug_struct("ErrorContext")
286 .field("backtrace", &self.backtrace)
287 .field("context", &self.context.to_string())
288 .finish()
289 }
290}
291
292impl Display for AdditionalErrorContext {
293 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294 let AdditionalErrorContext {
295 backtrace,
296 context,
297 scope,
298 } = self;
299
300 write!(f, "{context} (from ")?;
301
302 if let Some(scope) = scope {
303 write!(f, "scope {scope:?} ")?;
304 }
305
306 write!(f, "at {backtrace:?})")
307 }
308}
309
310pub type Result<T = ()> = std::result::Result<T, CapturedError>;
313
314#[allow(non_snake_case)]
317pub fn Ok<T>(value: T) -> Result<T> {
318 Result::Ok(value)
319}
320
321#[derive(Clone)]
322pub struct CapturedError {
324 error: Rc<dyn AnyError + 'static>,
326
327 backtrace: Rc<Backtrace>,
329
330 scope: ScopeId,
332
333 pub(crate) render: VNode,
335
336 context: Vec<Rc<AdditionalErrorContext>>,
338}
339
340impl FromStr for CapturedError {
341 type Err = std::convert::Infallible;
342
343 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
344 std::result::Result::Ok(Self::from_display(s.to_string()))
345 }
346}
347
348#[cfg(feature = "serialize")]
349#[derive(serde::Serialize, serde::Deserialize)]
350struct SerializedCapturedError {
351 error: String,
352 context: Vec<String>,
353}
354
355#[cfg(feature = "serialize")]
356impl serde::Serialize for CapturedError {
357 fn serialize<S: serde::Serializer>(
358 &self,
359 serializer: S,
360 ) -> std::result::Result<S::Ok, S::Error> {
361 let serialized = SerializedCapturedError {
362 error: self.error.as_error().to_string(),
363 context: self
364 .context
365 .iter()
366 .map(|context| context.to_string())
367 .collect(),
368 };
369 serialized.serialize(serializer)
370 }
371}
372
373#[cfg(feature = "serialize")]
374impl<'de> serde::Deserialize<'de> for CapturedError {
375 fn deserialize<D: serde::Deserializer<'de>>(
376 deserializer: D,
377 ) -> std::result::Result<Self, D::Error> {
378 let serialized = SerializedCapturedError::deserialize(deserializer)?;
379
380 let error = DisplayError::from(serialized.error);
381 let context = serialized
382 .context
383 .into_iter()
384 .map(|context| {
385 Rc::new(AdditionalErrorContext {
386 scope: None,
387 backtrace: Backtrace::disabled(),
388 context: Box::new(context),
389 })
390 })
391 .collect();
392
393 std::result::Result::Ok(Self {
394 error: Rc::new(error),
395 context,
396 backtrace: Rc::new(Backtrace::disabled()),
397 scope: ScopeId::ROOT,
398 render: VNode::placeholder(),
399 })
400 }
401}
402
403impl Debug for CapturedError {
404 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
405 f.debug_struct("CapturedError")
406 .field("error", &self.error.as_error())
407 .field("backtrace", &self.backtrace)
408 .field("scope", &self.scope)
409 .finish()
410 }
411}
412
413impl<E: AnyError + 'static> From<E> for CapturedError {
414 fn from(error: E) -> Self {
415 Self {
416 error: Rc::new(error),
417 backtrace: Rc::new(Backtrace::capture()),
418 scope: current_scope_id()
419 .expect("Cannot create an error boundary outside of a component's scope."),
420 render: Default::default(),
421 context: Default::default(),
422 }
423 }
424}
425
426impl CapturedError {
427 pub fn new(error: impl AnyError + 'static) -> Self {
429 Self {
430 error: Rc::new(error),
431 backtrace: Rc::new(Backtrace::capture()),
432 scope: current_scope_id().unwrap_or(ScopeId::ROOT),
433 render: Default::default(),
434 context: Default::default(),
435 }
436 }
437
438 pub fn from_display(error: impl Display + 'static) -> Self {
440 Self {
441 error: Rc::new(DisplayError::from(error)),
442 backtrace: Rc::new(Backtrace::capture()),
443 scope: current_scope_id().unwrap_or(ScopeId::ROOT),
444 render: Default::default(),
445 context: Default::default(),
446 }
447 }
448
449 pub fn with_origin(mut self, scope: ScopeId) -> Self {
451 self.scope = scope;
452 self
453 }
454
455 pub fn show(&self) -> Option<Element> {
457 if self.render == VNode::placeholder() {
458 None
459 } else {
460 Some(std::result::Result::Ok(self.render.clone()))
461 }
462 }
463
464 pub(crate) fn deep_clone(&self) -> Self {
466 Self {
467 render: self.render.deep_clone(),
468 ..self.clone()
469 }
470 }
471}
472
473impl PartialEq for CapturedError {
474 fn eq(&self, other: &Self) -> bool {
475 format!("{:?}", self) == format!("{:?}", other)
476 }
477}
478
479impl Display for CapturedError {
480 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
481 f.write_fmt(format_args!(
482 "Encountered error: {:?}\nIn scope: {:?}\nBacktrace: {}\nContext: ",
483 self.error.as_error(),
484 self.scope,
485 self.backtrace
486 ))?;
487 for context in &*self.context {
488 f.write_fmt(format_args!("{}\n", context))?;
489 }
490 std::result::Result::Ok(())
491 }
492}
493
494impl CapturedError {
495 pub fn downcast<T: 'static>(&self) -> Option<&T> {
497 self.error.as_any().downcast_ref::<T>()
498 }
499}
500
501pub(crate) fn throw_into(error: impl Into<CapturedError>, scope: ScopeId) {
502 let error = error.into();
503 if let Some(cx) = scope.consume_context::<ErrorContext>() {
504 cx.insert_error(error)
505 } else {
506 tracing::error!(
507 "Tried to throw an error into an error boundary, but failed to locate a boundary: {:?}",
508 error
509 )
510 }
511}
512
513#[allow(clippy::type_complexity)]
514#[derive(Clone)]
515pub struct ErrorHandler(Rc<dyn Fn(ErrorContext) -> Element>);
516impl<F: Fn(ErrorContext) -> Element + 'static> From<F> for ErrorHandler {
517 fn from(value: F) -> Self {
518 Self(Rc::new(value))
519 }
520}
521
522fn default_handler(errors: ErrorContext) -> Element {
523 static TEMPLATE: Template = Template {
524 roots: &[TemplateNode::Element {
525 tag: "div",
526 namespace: None,
527 attrs: &[TemplateAttribute::Static {
528 name: "color",
529 namespace: Some("style"),
530 value: "red",
531 }],
532 children: &[TemplateNode::Dynamic { id: 0usize }],
533 }],
534 node_paths: &[&[0u8, 0u8]],
535 attr_paths: &[],
536 };
537 std::result::Result::Ok(VNode::new(
538 None,
539 TEMPLATE,
540 Box::new([errors
541 .errors()
542 .iter()
543 .map(|e| {
544 static TEMPLATE: Template = Template {
545 roots: &[TemplateNode::Element {
546 tag: "pre",
547 namespace: None,
548 attrs: &[],
549 children: &[TemplateNode::Dynamic { id: 0usize }],
550 }],
551 node_paths: &[&[0u8, 0u8]],
552 attr_paths: &[],
553 };
554 VNode::new(
555 None,
556 TEMPLATE,
557 Box::new([e.to_string().into_dyn_node()]),
558 Default::default(),
559 )
560 })
561 .into_dyn_node()]),
562 Default::default(),
563 ))
564}
565
566#[derive(Clone)]
567pub struct ErrorBoundaryProps {
568 children: Element,
569 handle_error: ErrorHandler,
570}
571impl ErrorBoundaryProps {
572 #[allow(dead_code)]
578 pub fn builder() -> ErrorBoundaryPropsBuilder<((), ())> {
579 ErrorBoundaryPropsBuilder { fields: ((), ()) }
580 }
581}
582#[must_use]
583#[doc(hidden)]
584#[allow(dead_code, non_camel_case_types, non_snake_case)]
585pub struct ErrorBoundaryPropsBuilder<TypedBuilderFields> {
586 fields: TypedBuilderFields,
587}
588impl<TypedBuilderFields> Clone for ErrorBoundaryPropsBuilder<TypedBuilderFields>
589where
590 TypedBuilderFields: Clone,
591{
592 fn clone(&self) -> Self {
593 Self {
594 fields: self.fields.clone(),
595 }
596 }
597}
598impl Properties for ErrorBoundaryProps {
599 type Builder = ErrorBoundaryPropsBuilder<((), ())>;
600 fn builder() -> Self::Builder {
601 ErrorBoundaryProps::builder()
602 }
603 fn memoize(&mut self, other: &Self) -> bool {
604 *self = other.clone();
605 false
606 }
607}
608#[doc(hidden)]
609#[allow(dead_code, non_camel_case_types, non_snake_case)]
610pub trait ErrorBoundaryPropsBuilder_Optional<T> {
611 fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
612}
613impl<T> ErrorBoundaryPropsBuilder_Optional<T> for () {
614 fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
615 default()
616 }
617}
618impl<T> ErrorBoundaryPropsBuilder_Optional<T> for (T,) {
619 fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
620 self.0
621 }
622}
623#[allow(dead_code, non_camel_case_types, missing_docs)]
624impl<__handle_error> ErrorBoundaryPropsBuilder<((), __handle_error)> {
625 pub fn children(
626 self,
627 children: Element,
628 ) -> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
629 let children = (children,);
630 let (_, handle_error) = self.fields;
631 ErrorBoundaryPropsBuilder {
632 fields: (children, handle_error),
633 }
634 }
635}
636#[doc(hidden)]
637#[allow(dead_code, non_camel_case_types, non_snake_case)]
638pub enum ErrorBoundaryPropsBuilder_Error_Repeated_field_children {}
639#[doc(hidden)]
640#[allow(dead_code, non_camel_case_types, missing_docs)]
641impl<__handle_error> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
642 #[deprecated(note = "Repeated field children")]
643 pub fn children(
644 self,
645 _: ErrorBoundaryPropsBuilder_Error_Repeated_field_children,
646 ) -> ErrorBoundaryPropsBuilder<((Element,), __handle_error)> {
647 self
648 }
649}
650#[allow(dead_code, non_camel_case_types, missing_docs)]
651impl<__children> ErrorBoundaryPropsBuilder<(__children, ())> {
652 pub fn handle_error(
653 self,
654 handle_error: impl ::core::convert::Into<ErrorHandler>,
655 ) -> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
656 let handle_error = (handle_error.into(),);
657 let (children, _) = self.fields;
658 ErrorBoundaryPropsBuilder {
659 fields: (children, handle_error),
660 }
661 }
662}
663#[doc(hidden)]
664#[allow(dead_code, non_camel_case_types, non_snake_case)]
665pub enum ErrorBoundaryPropsBuilder_Error_Repeated_field_handle_error {}
666#[doc(hidden)]
667#[allow(dead_code, non_camel_case_types, missing_docs)]
668impl<__children> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
669 #[deprecated(note = "Repeated field handle_error")]
670 pub fn handle_error(
671 self,
672 _: ErrorBoundaryPropsBuilder_Error_Repeated_field_handle_error,
673 ) -> ErrorBoundaryPropsBuilder<(__children, (ErrorHandler,))> {
674 self
675 }
676}
677#[allow(dead_code, non_camel_case_types, missing_docs)]
678impl<
679 __handle_error: ErrorBoundaryPropsBuilder_Optional<ErrorHandler>,
680 __children: ErrorBoundaryPropsBuilder_Optional<Element>,
681 > ErrorBoundaryPropsBuilder<(__children, __handle_error)>
682{
683 pub fn build(self) -> ErrorBoundaryProps {
684 let (children, handle_error) = self.fields;
685 let children = ErrorBoundaryPropsBuilder_Optional::into_value(children, VNode::empty);
686 let handle_error = ErrorBoundaryPropsBuilder_Optional::into_value(handle_error, || {
687 ErrorHandler(Rc::new(default_handler))
688 });
689 ErrorBoundaryProps {
690 children,
691 handle_error,
692 }
693 }
694}
695
696#[allow(non_upper_case_globals, non_snake_case)]
802pub fn ErrorBoundary(props: ErrorBoundaryProps) -> Element {
803 let error_boundary = use_hook(provide_error_boundary);
804 let errors = error_boundary.errors();
805 let has_errors = !errors.is_empty();
806 drop(errors);
808
809 if has_errors {
810 (props.handle_error.0)(error_boundary.clone())
811 } else {
812 std::result::Result::Ok({
813 static TEMPLATE: Template = Template {
814 roots: &[TemplateNode::Dynamic { id: 0usize }],
815 node_paths: &[&[0u8]],
816 attr_paths: &[],
817 };
818 VNode::new(
819 None,
820 TEMPLATE,
821 Box::new([(props.children).into_dyn_node()]),
822 Default::default(),
823 )
824 })
825 }
826}