Skip to main content

euv_ui/hook/error_boundary/
impl.rs

1use super::*;
2
3/// Inherent implementation of [`ErrorBoundary`].
4impl ErrorBoundary {
5    /// Creates a new `ErrorBoundary` in the `Healthy`
6    /// phase.
7    pub fn new() -> Self {
8        Self {
9            phase: Signal::create(ErrorBoundaryPhase::Healthy),
10        }
11    }
12
13    /// Runs a closure and, if it panics, transitions
14    /// the boundary to `Caught` and returns `Err`.
15    ///
16    /// On success, the closure's return value is
17    /// returned wrapped in `Ok`. The closure is
18    /// wrapped in `AssertUnwindSafe` so it does not
19    /// have to satisfy `UnwindSafe`.
20    ///
21    /// # Arguments
22    ///
23    /// - `F: FnOnce() -> R + UnwindSafe` - A generic type parameter.
24    ///
25    /// # Returns
26    ///
27    /// - `Result<R, String>` - Result of the operation; an `Err` variant on failure.
28    pub fn try_with<F, R>(&self, closure: F) -> Result<R, String>
29    where
30        F: FnOnce() -> R + UnwindSafe,
31    {
32        match catch_unwind(closure) {
33            Ok(value) => Ok(value),
34            Err(payload) => {
35                let message: String = extract_message(&payload);
36                let _ = catch_unwind(AssertUnwindSafe(|| {
37                    self.get_phase()
38                        .set(ErrorBoundaryPhase::Caught(message.clone()));
39                }));
40                Err(message)
41            }
42        }
43    }
44
45    /// Feeds an `Err` straight into the boundary
46    /// without forcing the caller to `panic!`.
47    ///
48    /// `try_with` requires a real panic to transition
49    /// the phase to `Caught`, which makes demonstrating
50    /// the hook from outside `tests/` awkward. This
51    /// helper lets demo / driver code report a failure
52    /// message via the regular `Result` channel and
53    /// still flip the boundary into `Caught`.
54    ///
55    /// # Arguments
56    ///
57    /// - `&str` - The error message to surface.
58    ///
59    /// # Returns
60    ///
61    /// - `String` - The same message that was passed in.
62    pub fn report_error(&self, message: &str) -> String {
63        let owned: String = String::from(message);
64        let _ = catch_unwind(AssertUnwindSafe(|| {
65            self.get_phase()
66                .set(ErrorBoundaryPhase::Caught(owned.clone()));
67        }));
68        owned
69    }
70
71    /// Transitions the boundary back to `Healthy`.
72    /// Useful when invalidating the cache (e.g.,
73    /// after a retry).
74    pub fn reset(&self) {
75        let _ = catch_unwind(AssertUnwindSafe(|| {
76            self.get_phase().set(ErrorBoundaryPhase::Healthy);
77        }));
78    }
79}
80
81/// Default-construction for [`ErrorBoundary`].
82impl Default for ErrorBoundary {
83    /// Constructs a default [`ErrorBoundary`] value.
84    fn default() -> Self {
85        Self::new()
86    }
87}
88
89/// Formatting / debug-printing for [`ErrorBoundary`].
90impl Display for ErrorBoundary {
91    /// Formats the [`ErrorBoundary`] via the supplied formatter.
92    ///
93    /// # Arguments
94    ///
95    /// - `&mut Formatter<'_>` - The formatter receiving the formatted output.
96    ///
97    /// # Returns
98    ///
99    /// - `FmtResult` - Result of the formatting operation.
100    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
101        write!(formatter, "ErrorBoundary({:?})", self.get_phase().get())
102    }
103}
104
105/// Equality comparison for [`ErrorBoundaryPhase`].
106impl PartialEq for ErrorBoundaryPhase {
107    /// Returns `true` when `self` and `other` are equivalent by the [`PartialEq`] contract.
108    ///
109    /// # Arguments
110    ///
111    /// - `&Self` - The other value to compare against `self`.
112    ///
113    /// # Returns
114    ///
115    /// - `bool` - `true` when `self` and `other` are equivalent by the trait contract.
116    fn eq(&self, other: &Self) -> bool {
117        match (self, other) {
118            (ErrorBoundaryPhase::Healthy, ErrorBoundaryPhase::Healthy) => true,
119            (ErrorBoundaryPhase::Caught(a), ErrorBoundaryPhase::Caught(b)) => a == b,
120            _ => false,
121        }
122    }
123}