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}