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}