tessera_ui_macros/lib.rs
1//! # Tessera Macros
2//!
3//! This crate provides procedural macros for the Tessera UI framework.
4//! The main export is the `#[tessera]` attribute macro, which transforms
5//! regular Rust functions into Tessera UI components.
6//!
7//! ## Usage
8//!
9//! ```
10//! use tessera_ui_macros::tessera;
11//!
12//! #[tessera]
13//! fn my_component() {
14//! // Component logic here
15//! // The macro provides access to `measure`, `state_handler` and `on_minimize` functions
16//! }
17//! ```
18//!
19//! The `#[tessera]` macro automatically:
20//! - Registers the function as a component in the Tessera component tree
21//! - Injects `measure`, `state_handler` and `on_minimize` functions into the component scope
22//! - Handles component tree management (adding/removing nodes)
23//! - Provides error safety by wrapping the function body
24
25use proc_macro::TokenStream;
26use quote::quote;
27use syn::{ItemFn, parse_macro_input};
28
29/// The `#[tessera]` attribute macro transforms a regular Rust function into a Tessera UI component.
30///
31/// This macro performs several key transformations:
32/// 1. Registers the function as a node in the Tessera component tree
33/// 2. Injects `measure`, `state_handler` and `on_minimize` functions into the component scope
34/// 3. Manages component tree lifecycle (push/pop operations)
35/// 4. Provides error safety by wrapping the original function body
36///
37/// ## Parameters
38///
39/// - `_attr`: Attribute arguments (currently unused)
40/// - `item`: The function to be transformed into a component
41///
42/// ## Generated Code
43///
44/// The macro generates code that:
45///
46/// - Accesses the Tessera runtime to manage the component tree
47/// - Creates a new component node with the function name
48/// - Provides closures for `measure` and `state_handler` functionality
49/// - Executes the original function body within a safe closure
50/// - Cleans up the component tree after execution
51///
52/// ## Example
53///
54/// ```
55/// use tessera_ui_macros::tessera;
56///
57/// #[tessera]
58/// fn button_component(label: String) {
59/// // The macro provides access to these functions:
60/// measure(Box::new(|_| {
61/// // Custom layout logic
62/// use tessera_ui::{ComputedData, Px};
63/// Ok(ComputedData {
64/// width: Px(100),
65/// height: Px(50),
66/// })
67/// }));
68///
69/// state_handler(Box::new(|_| {
70/// // Event handling logic
71/// }));
72///
73/// on_minimize(Box::new(|minimized| {
74/// if minimized {
75/// println!("Window minimized!");
76/// } else {
77/// println!("Window restored!");
78/// }
79/// }));
80/// }
81/// ```
82///
83/// ## Error Handling
84///
85/// The macro wraps the original function body in a closure to prevent
86/// early returns from breaking the component tree structure. This ensures
87/// that the component tree is always properly cleaned up, even if the
88/// component function returns early.
89#[proc_macro_attribute]
90pub fn tessera(_attr: TokenStream, item: TokenStream) -> TokenStream {
91 // Parse the input function that will be transformed into a component
92 let input_fn = parse_macro_input!(item as ItemFn);
93 let fn_name = &input_fn.sig.ident; // Function name for component identification
94 let fn_vis = &input_fn.vis; // Visibility (pub, pub(crate), etc.)
95 let fn_attrs = &input_fn.attrs; // Attributes like #[doc], #[allow], etc.
96 let fn_sig = &input_fn.sig; // Function signature (parameters, return type)
97 let fn_block = &input_fn.block; // Original function body
98
99 // Generate the transformed function with Tessera runtime integration
100 let expanded = quote! {
101 #(#fn_attrs)*
102 #fn_vis #fn_sig {
103 // Step 1: Register this function as a component node in the tree
104 {
105 use tessera_ui::{TesseraRuntime, ComponentNode};
106
107 TesseraRuntime::write()
108 .component_tree
109 .add_node(
110 ComponentNode {
111 fn_name: stringify!(#fn_name).to_string(),
112 measure_fn: None,
113 state_handler_fn: None,
114 }
115 );
116 }
117
118 // Step 2: Inject the `measure` function into the component scope
119 // This allows components to define custom layout behavior
120 let measure = {
121 use tessera_ui::{MeasureFn, TesseraRuntime};
122 |fun: Box<MeasureFn>| {
123 TesseraRuntime::write()
124 .component_tree
125 .current_node_mut()
126 .unwrap()
127 .measure_fn = Some(fun);
128 }
129 };
130
131 // Step 3: Inject the `state_handler` function into the component scope
132 // This allows components to handle user interactions and events
133 let state_handler = {
134 use tessera_ui::{StateHandlerFn, TesseraRuntime};
135 |fun: Box<StateHandlerFn>| {
136 TesseraRuntime::write()
137 .component_tree
138 .current_node_mut()
139 .unwrap()
140 .state_handler_fn = Some(fun);
141 }
142 };
143
144 // Step 4: Inject the `on_minimize` function into the component scope
145 // This allows components to respond to window minimize events
146 let on_minimize = {
147 use tessera_ui::TesseraRuntime;
148 |fun: Box<dyn Fn(bool) + Send + Sync + 'static>| {
149 TesseraRuntime::write().on_minimize(fun);
150 }
151 };
152
153 // Step 4b: Inject the `on_close` function into the component scope
154 // This allows components to respond to window close events
155 let on_close = {
156 use tessera_ui::TesseraRuntime;
157 |fun: Box<dyn Fn() + Send + Sync + 'static>| {
158 TesseraRuntime::write().on_close(fun);
159 }
160 };
161
162 // Step 5: Execute the original function body within a closure
163 // This prevents early returns from breaking the component tree structure
164 let result = {
165 let closure = || #fn_block;
166 closure()
167 };
168
169 // Step 6: Clean up the component tree by removing this node
170 // This ensures proper tree management and prevents memory leaks
171 {
172 use tessera_ui::TesseraRuntime;
173
174 TesseraRuntime::write()
175 .component_tree
176 .pop_node();
177 }
178
179 result
180 }
181 };
182
183 TokenStream::from(expanded)
184}