rosace_core/error_boundary.rs
1use crate::element::Element;
2use crate::error::RosaceError;
3
4/// Catches panics (and future framework-level errors) from a child subtree and
5/// renders a fallback element in their place.
6///
7/// # Phase 1 note
8///
9/// In Phase 1 the `child` is already a resolved `Element`, so `render()` simply
10/// returns the child clone. Panic catching around `RosaceComponent::build()`
11/// calls happens at the framework dispatch level (in `rosace-render` /
12/// `rosace-cli`), not here. The `ErrorBoundary` struct and its API are the
13/// stable surface; the real panic-catching wiring is added in a later phase.
14pub struct ErrorBoundary {
15 fallback: Box<dyn Fn(&RosaceError) -> Element + Send + Sync>,
16 child: Element,
17}
18
19impl ErrorBoundary {
20 /// Creates an `ErrorBoundary` with a default fallback that renders the
21 /// error message as a text element.
22 pub fn new() -> Self {
23 ErrorBoundary {
24 fallback: Box::new(|e| Element::text(format!("Error: {e}"))),
25 child: Element::Empty,
26 }
27 }
28
29 /// Replaces the fallback renderer.
30 pub fn fallback(
31 mut self,
32 f: impl Fn(&RosaceError) -> Element + Send + Sync + 'static,
33 ) -> Self {
34 self.fallback = Box::new(f);
35 self
36 }
37
38 /// Sets the child element to render when no error has occurred.
39 pub fn child(mut self, element: impl Into<Element>) -> Self {
40 self.child = element.into();
41 self
42 }
43
44 /// Returns the child element.
45 ///
46 /// In Phase 1 this always returns the child as-is. The real panic-catching
47 /// path wraps `RosaceComponent::build()` at the framework dispatch level.
48 pub fn render(&self) -> Element {
49 self.child.clone()
50 }
51}
52
53impl Default for ErrorBoundary {
54 fn default() -> Self {
55 ErrorBoundary::new()
56 }
57}