Skip to main content

euv_core/reactive/error_boundary/
impl.rs

1use super::*;
2
3impl ErrorBoundary {
4    /// Creates a new `ErrorBoundary` in the `Healthy`
5    /// phase.
6    pub fn new() -> Self {
7        Self {
8            phase: Signal::create(ErrorBoundaryPhase::Healthy),
9        }
10    }
11
12    /// Returns the underlying phase signal.
13    pub fn phase(&self) -> Signal<ErrorBoundaryPhase> {
14        self.phase.clone()
15    }
16
17    /// Returns a snapshot of the current phase.
18    pub fn current(&self) -> ErrorBoundaryPhase {
19        self.phase.get()
20    }
21
22    /// Returns `true` if no child has thrown yet.
23    pub fn is_healthy(&self) -> bool {
24        matches!(self.phase.get(), ErrorBoundaryPhase::Healthy)
25    }
26
27    /// Returns `true` if a child has thrown.
28    pub fn is_caught(&self) -> bool {
29        matches!(self.phase.get(), ErrorBoundaryPhase::Caught(_))
30    }
31
32    /// Runs a closure and, if it panics, transitions
33    /// the boundary to `Caught` and returns `Err`.
34    ///
35    /// On success, the closure's return value is
36    /// returned wrapped in `Ok`. The closure is
37    /// wrapped in `AssertUnwindSafe` so it does not
38    /// have to satisfy `UnwindSafe`.
39    pub fn try_with<F, R>(&self, closure: F) -> Result<R, String>
40    where
41        F: FnOnce() -> R + std::panic::UnwindSafe,
42    {
43        match std::panic::catch_unwind(closure) {
44            Ok(value) => Ok(value),
45            Err(payload) => {
46                let message = extract_message(&payload);
47                let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
48                    self.phase.set(ErrorBoundaryPhase::Caught(message.clone()));
49                }));
50                Err(message)
51            }
52        }
53    }
54
55    /// Transitions the boundary back to `Healthy`.
56    /// Useful when invalidating the cache (e.g.,
57    /// after a retry).
58    pub fn reset(&self) {
59        let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
60            self.phase.set(ErrorBoundaryPhase::Healthy);
61        }));
62    }
63}
64
65impl Default for ErrorBoundary {
66    fn default() -> Self {
67        Self::new()
68    }
69}
70
71impl std::fmt::Display for ErrorBoundary {
72    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        write!(formatter, "ErrorBoundary({:?})", self.phase.get())
74    }
75}
76
77impl Default for ErrorBoundaryPhase {
78    fn default() -> Self {
79        ErrorBoundaryPhase::Healthy
80    }
81}
82
83impl PartialEq for ErrorBoundaryPhase {
84    fn eq(&self, other: &Self) -> bool {
85        match (self, other) {
86            (ErrorBoundaryPhase::Healthy, ErrorBoundaryPhase::Healthy) => true,
87            (ErrorBoundaryPhase::Caught(a), ErrorBoundaryPhase::Caught(b)) => a == b,
88            _ => false,
89        }
90    }
91}