Skip to main content

rosace_core/
lib.rs

1pub mod app;
2pub mod app_lifecycle;
3pub mod asset;
4pub mod child_container;
5pub mod component;
6pub mod context;
7pub mod element;
8pub mod error;
9pub mod error_boundary;
10pub mod ime_hint;
11pub mod lifecycle;
12pub mod media_query;
13pub mod persist;
14pub mod platform;
15pub mod render_object;
16pub mod safe_area;
17pub mod semantic_node;
18pub mod shader;
19pub mod types;
20
21pub use app::App;
22pub use app_lifecycle::{app_lifecycle, set_app_lifecycle, use_app_lifecycle, LifecycleState};
23pub use child_container::ChildContainer;
24pub use component::Component;
25pub use context::Context;
26pub use element::{Element, NativeElement, ComponentElement, TextElement, WidgetPayload};
27pub use error::{RosaceError, RosaceResult};
28pub use error_boundary::ErrorBoundary;
29pub use ime_hint::{ime_cursor_area, keyboard_type, set_ime_cursor_area, set_keyboard_type, KeyboardType};
30pub use media_query::{use_media_query, set_media_query, MediaQuery};
31pub use persist::{persist_backend, set_persist_backend, PersistBackend, PersistValue};
32pub use platform::{use_platform, set_platform, Platform};
33pub use render_object::{AxisBound, Canvas, Constraints, RenderObject};
34pub use safe_area::{use_safe_area, set_safe_area, SafeArea};
35pub use semantic_node::{Role, SemanticNode};
36pub use types::{AtomId, ComponentId, Key, Location, Point, Rect, Size};
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41    use crate::lifecycle::on_mount;
42
43    struct Greeting;
44    impl Component for Greeting {
45        fn build(&self, _ctx: &mut Context) -> Element {
46            Element::text("Hello, ROSACE!")
47        }
48    }
49
50    #[test]
51    fn component_builds_element() {
52        let greeting = Greeting;
53        let mut ctx = Context::new(ComponentId(1));
54        let element = greeting.build(&mut ctx);
55        assert!(!matches!(element, Element::Empty));
56    }
57
58    #[test]
59    fn lifecycle_on_cleanup_registered() {
60        let id = ComponentId(2);
61        let mut ctx = Context::new(id);
62        on_mount(&mut ctx, || || {});
63        // Cleanup is stored in cleanup_store, not on Context directly.
64        assert!(rosace_state::cleanup_store::has_callbacks(id));
65    }
66
67    #[test]
68    fn error_boundary_has_fallback() {
69        let boundary = ErrorBoundary::new()
70            .fallback(|_e| Element::text("something went wrong"))
71            .child(Element::text("normal content"));
72        let result = boundary.render();
73        assert!(!matches!(result, Element::Empty));
74    }
75
76    struct SimpleContainer { elements: Vec<Element> }
77    impl SimpleContainer {
78        fn new() -> Self { SimpleContainer { elements: Vec::new() } }
79    }
80    impl ChildContainer for SimpleContainer {
81        fn child(mut self, element: impl Into<Element>) -> Self {
82            self.elements.push(element.into());
83            self
84        }
85        fn children<E: Into<Element>>(mut self, elements: Vec<E>) -> Self {
86            self.elements.extend(elements.into_iter().map(|e| e.into()));
87            self
88        }
89        fn prepend(mut self, element: impl Into<Element>) -> Self {
90            self.elements.insert(0, element.into());
91            self
92        }
93    }
94
95    #[test]
96    fn child_container_order_preserved() {
97        let container = SimpleContainer::new()
98            .child(Element::text("first"))
99            .child(Element::text("second"))
100            .child(Element::text("third"));
101        assert_eq!(container.elements.len(), 3);
102        let texts: Vec<&str> = container.elements.iter().filter_map(|e| {
103            if let Element::Text(t) = e { Some(t.content.as_str()) } else { None }
104        }).collect();
105        assert_eq!(texts, ["first", "second", "third"]);
106    }
107
108    #[test]
109    fn constraints_loose_has_zero_min() {
110        let c = Constraints::loose(800.0, 600.0);
111        assert_eq!(c.min_width, 0.0);
112        assert_eq!(c.min_height, 0.0);
113    }
114
115    #[test]
116    fn rosace_error_display() {
117        let e = RosaceError::not_found("User");
118        assert!(e.to_string().contains("User"));
119    }
120
121    #[test]
122    fn context_state_creates_atom() {
123        let mut ctx = Context::new(ComponentId(100));
124        let atom = ctx.state(42i32);
125        assert_eq!(atom.get(), 42);
126        atom.set(100);
127        assert_eq!(atom.get(), 100);
128    }
129
130    #[test]
131    fn context_state_persists_across_frames() {
132        let mut ctx = Context::new(ComponentId(200));
133        let atom = ctx.state(0i32);
134        atom.set(7);
135
136        // Simulate next frame: new Context with same component_id
137        let mut ctx2 = Context::new(ComponentId(200));
138        let atom2 = ctx2.state(0i32);
139        assert_eq!(atom2.get(), 7, "state must survive frame rebuild");
140    }
141}