Skip to main content

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}