openkit_macros/
lib.rs

1//! Procedural macros for the OpenKit UI framework.
2//!
3//! This crate provides derive macros and attribute macros for building
4//! OpenKit applications with less boilerplate.
5//!
6//! # Macros
7//!
8//! - [`Widget`] - Derive macro for implementing the Widget trait
9//! - [`Component`] - Derive macro for creating Angular-like components
10//! - [`Styleable`] - Derive macro for CSS styling support
11//! - [`#[component]`] - Attribute macro for component definitions
12//! - [`#[prop]`] - Attribute macro for component properties
13//! - [`#[state]`] - Attribute macro for component state
14//! - [`#[event]`] - Attribute macro for event emitters
15//!
16//! # Examples
17//!
18//! ```rust,ignore
19//! use openkit::prelude::*;
20//! use openkit_macros::{Widget, Component, Styleable};
21//!
22//! #[derive(Widget, Styleable)]
23//! struct MyWidget {
24//!     #[base]
25//!     base: WidgetBase,
26//!     label: String,
27//! }
28//!
29//! #[derive(Component)]
30//! #[component(selector = "my-counter")]
31//! struct CounterComponent {
32//!     #[state]
33//!     count: i32,
34//!
35//!     #[prop(default = 1)]
36//!     step: i32,
37//!
38//!     #[event]
39//!     on_change: EventEmitter<i32>,
40//! }
41//! ```
42
43use proc_macro::TokenStream;
44use syn::{parse_macro_input, DeriveInput};
45use proc_macro_error::proc_macro_error;
46
47mod widget;
48mod component;
49mod styleable;
50
51/// Derive macro for implementing the Widget trait.
52///
53/// This macro generates the boilerplate implementation of the `Widget` trait
54/// for a struct that contains a `WidgetBase` field.
55///
56/// # Requirements
57///
58/// - The struct must have a field marked with `#[base]` of type `WidgetBase`
59/// - The struct must have a `type_name` attribute specifying the widget type
60///
61/// # Example
62///
63/// ```rust,ignore
64/// use openkit_macros::Widget;
65///
66/// #[derive(Widget)]
67/// #[widget(type_name = "my-widget")]
68/// struct MyWidget {
69///     #[base]
70///     base: WidgetBase,
71///
72///     label: String,
73///     value: i32,
74/// }
75///
76/// // The macro generates:
77/// // - id() -> WidgetId
78/// // - type_name() -> &'static str
79/// // - element_id() -> Option<&str>
80/// // - classes() -> &ClassList
81/// // - state() -> WidgetState
82/// // - bounds() -> Rect
83/// // - set_bounds(&mut self, Rect)
84/// ```
85#[proc_macro_derive(Widget, attributes(widget, base))]
86#[proc_macro_error]
87pub fn derive_widget(input: TokenStream) -> TokenStream {
88    let input = parse_macro_input!(input as DeriveInput);
89    widget::derive_widget_impl(input)
90        .unwrap_or_else(|e| e.to_compile_error())
91        .into()
92}
93
94/// Derive macro for creating Angular-like components.
95///
96/// This macro generates the component infrastructure including state management,
97/// property binding, event emission, and lifecycle hooks.
98///
99/// # Attributes
100///
101/// - `#[component(selector = "...")]` - Set the component selector
102/// - `#[state]` - Mark a field as reactive state
103/// - `#[prop]` - Mark a field as a component property
104/// - `#[prop(default = value)]` - Property with default value
105/// - `#[event]` - Mark a field as an event emitter
106///
107/// # Example
108///
109/// ```rust,ignore
110/// use openkit_macros::Component;
111///
112/// #[derive(Component)]
113/// #[component(selector = "counter")]
114/// struct Counter {
115///     #[state]
116///     count: i32,
117///
118///     #[prop(default = 1)]
119///     step: i32,
120///
121///     #[event]
122///     on_change: EventEmitter<i32>,
123/// }
124///
125/// impl Counter {
126///     fn increment(&mut self) {
127///         self.count += self.step;
128///         self.on_change.emit(self.count);
129///     }
130/// }
131/// ```
132#[proc_macro_derive(Component, attributes(component, state, prop, event))]
133#[proc_macro_error]
134pub fn derive_component(input: TokenStream) -> TokenStream {
135    let input = parse_macro_input!(input as DeriveInput);
136    component::derive_component_impl(input)
137        .unwrap_or_else(|e| e.to_compile_error())
138        .into()
139}
140
141/// Derive macro for CSS styling support.
142///
143/// This macro generates the `class()` and `id()` builder methods
144/// for adding CSS classes and element IDs to widgets.
145///
146/// # Requirements
147///
148/// - The struct must have a field marked with `#[base]` of type `WidgetBase`
149///
150/// # Example
151///
152/// ```rust,ignore
153/// use openkit_macros::Styleable;
154///
155/// #[derive(Styleable)]
156/// struct MyWidget {
157///     #[base]
158///     base: WidgetBase,
159///     // other fields...
160/// }
161///
162/// // The macro generates:
163/// impl MyWidget {
164///     pub fn class(mut self, class: &str) -> Self { ... }
165///     pub fn id(mut self, id: &str) -> Self { ... }
166///     pub fn classes(&self) -> &ClassList { ... }
167/// }
168/// ```
169#[proc_macro_derive(Styleable, attributes(base))]
170#[proc_macro_error]
171pub fn derive_styleable(input: TokenStream) -> TokenStream {
172    let input = parse_macro_input!(input as DeriveInput);
173    styleable::derive_styleable_impl(input)
174        .unwrap_or_else(|e| e.to_compile_error())
175        .into()
176}
177
178/// Attribute macro for defining components with inline render function.
179///
180/// This is an alternative to the derive macro that allows defining
181/// the component and its render function in one place.
182///
183/// # Example
184///
185/// ```rust,ignore
186/// use openkit_macros::component;
187///
188/// #[component(selector = "greeting")]
189/// fn Greeting(
190///     #[prop] name: String,
191///     #[prop(default = "Hello")] greeting: String,
192/// ) -> impl Widget {
193///     label!(format!("{}, {}!", greeting, name))
194/// }
195/// ```
196#[proc_macro_attribute]
197#[proc_macro_error]
198pub fn component(attr: TokenStream, item: TokenStream) -> TokenStream {
199    let attr = parse_macro_input!(attr as component::ComponentAttrArgs);
200    let item = parse_macro_input!(item as syn::ItemFn);
201    component::component_attribute_impl(attr, item)
202        .unwrap_or_else(|e| e.to_compile_error())
203        .into()
204}
205
206/// Attribute macro for marking component properties.
207///
208/// Properties are values passed from parent to child components.
209///
210/// # Attributes
211///
212/// - `#[prop]` - Required property
213/// - `#[prop(default = value)]` - Property with default value
214/// - `#[prop(optional)]` - Optional property (wrapped in Option)
215///
216/// # Example
217///
218/// ```rust,ignore
219/// #[derive(Component)]
220/// struct MyComponent {
221///     #[prop]
222///     required_value: String,
223///
224///     #[prop(default = 42)]
225///     with_default: i32,
226///
227///     #[prop(optional)]
228///     maybe_value: Option<String>,
229/// }
230/// ```
231#[proc_macro_attribute]
232pub fn prop(_attr: TokenStream, item: TokenStream) -> TokenStream {
233    // This is handled by the Component derive macro
234    // The attribute just marks the field
235    item
236}
237
238/// Attribute macro for marking component state.
239///
240/// State fields are reactive - changes trigger re-renders.
241///
242/// # Example
243///
244/// ```rust,ignore
245/// #[derive(Component)]
246/// struct Counter {
247///     #[state]
248///     count: i32,  // Changes to count trigger re-render
249/// }
250/// ```
251#[proc_macro_attribute]
252pub fn state(_attr: TokenStream, item: TokenStream) -> TokenStream {
253    // This is handled by the Component derive macro
254    item
255}
256
257/// Attribute macro for marking event emitters.
258///
259/// Events allow components to communicate with their parents.
260///
261/// # Example
262///
263/// ```rust,ignore
264/// #[derive(Component)]
265/// struct Button {
266///     #[event]
267///     on_click: EventEmitter<()>,
268///
269///     #[event]
270///     on_value_change: EventEmitter<String>,
271/// }
272/// ```
273#[proc_macro_attribute]
274pub fn event(_attr: TokenStream, item: TokenStream) -> TokenStream {
275    // This is handled by the Component derive macro
276    item
277}
278
279/// Helper function to find a field with a specific attribute.
280#[allow(dead_code)]
281fn find_field_with_attr<'a>(fields: &'a syn::Fields, attr_name: &str) -> Option<&'a syn::Field> {
282    match fields {
283        syn::Fields::Named(named) => {
284            named.named.iter().find(|f| {
285                f.attrs.iter().any(|a| a.path().is_ident(attr_name))
286            })
287        }
288        _ => None,
289    }
290}
291
292/// Helper function to get all fields with a specific attribute.
293#[allow(dead_code)]
294fn get_fields_with_attr<'a>(fields: &'a syn::Fields, attr_name: &str) -> Vec<&'a syn::Field> {
295    match fields {
296        syn::Fields::Named(named) => {
297            named.named.iter().filter(|f| {
298                f.attrs.iter().any(|a| a.path().is_ident(attr_name))
299            }).collect()
300        }
301        _ => Vec::new(),
302    }
303}