kizzasi_macros/lib.rs
1//! Procedural macros for Kizzasi AGSP.
2//!
3//! This crate provides derive macros to simplify working with Kizzasi predictors
4//! and custom configurations.
5//!
6//! # Derive Macros
7//!
8//! ## `#[derive(KizzasiConfig)]`
9//!
10//! Automatically implements builder pattern and validation for custom configurations.
11//!
12//! ```rust,ignore
13//! use kizzasi_macros::KizzasiConfig;
14//!
15//! #[derive(KizzasiConfig)]
16//! struct MyCustomConfig {
17//! #[config(default = 4096)]
18//! context_window: usize,
19//!
20//! #[config(validate = "validate_dim")]
21//! hidden_dim: usize,
22//!
23//! learning_rate: f64,
24//! }
25//!
26//! fn validate_dim(dim: &usize) -> Result<(), String> {
27//! if *dim > 0 && *dim % 64 == 0 {
28//! Ok(())
29//! } else {
30//! Err("Dimension must be positive and divisible by 64".into())
31//! }
32//! }
33//! ```
34//!
35//! ## `#[derive(Preset)]`
36//!
37//! Generates preset constructor functions for common configurations.
38//!
39//! ```rust,ignore
40//! use kizzasi_macros::Preset;
41//!
42//! #[derive(Preset)]
43//! #[preset(name = "audio", context_window = 8192, hidden_dim = 256)]
44//! #[preset(name = "video", context_window = 16384, hidden_dim = 512)]
45//! struct ModelConfig {
46//! context_window: usize,
47//! hidden_dim: usize,
48//! }
49//!
50//! // Generated:
51//! // impl ModelConfig {
52//! // pub fn audio_preset() -> Self { ... }
53//! // pub fn video_preset() -> Self { ... }
54//! // }
55//! ```
56
57use proc_macro::TokenStream;
58use quote::quote;
59use syn::{parse_macro_input, Data, DeriveInput, Fields};
60
61/// Derive macro for custom Kizzasi configurations.
62///
63/// Generates a builder pattern with automatic validation and default values.
64///
65/// # Attributes
66///
67/// - `#[config(default = value)]` - Set a default value for a field
68/// - `#[config(validate = "function")]` - Specify a validation function
69/// - `#[config(skip)]` - Skip this field in the builder
70///
71/// # Example
72///
73/// ```rust,ignore
74/// #[derive(KizzasiConfig)]
75/// struct MyConfig {
76/// #[config(default = 1024)]
77/// buffer_size: usize,
78///
79/// #[config(validate = "validate_positive")]
80/// sample_rate: f64,
81/// }
82/// ```
83#[proc_macro_derive(KizzasiConfig, attributes(config))]
84pub fn derive_kizzasi_config(input: TokenStream) -> TokenStream {
85 let input = parse_macro_input!(input as DeriveInput);
86 let name = &input.ident;
87 let builder_name = syn::Ident::new(&format!("{}Builder", name), name.span());
88
89 let fields = match &input.data {
90 Data::Struct(data) => match &data.fields {
91 Fields::Named(fields) => &fields.named,
92 _ => panic!("KizzasiConfig only supports named fields"),
93 },
94 _ => panic!("KizzasiConfig only supports structs"),
95 };
96
97 let mut builder_fields = Vec::new();
98 let mut builder_methods = Vec::new();
99 let mut build_assignments = Vec::new();
100
101 for field in fields {
102 let field_name = field.ident.as_ref().unwrap();
103 let field_type = &field.ty;
104
105 // Simplified: no attribute parsing for now, just basic builder
106 // Builder field (Option<T>)
107 builder_fields.push(quote! {
108 #field_name: Option<#field_type>
109 });
110
111 // Builder method
112 builder_methods.push(quote! {
113 pub fn #field_name(mut self, value: #field_type) -> Self {
114 self.#field_name = Some(value);
115 self
116 }
117 });
118
119 // Build assignment - require all fields
120 build_assignments.push(quote! {
121 #field_name: self.#field_name.ok_or_else(|| format!("Missing required field: {}", stringify!(#field_name)))?
122 });
123 }
124
125 let expanded = quote! {
126 impl #name {
127 /// Create a new builder for this configuration.
128 pub fn builder() -> #builder_name {
129 #builder_name::new()
130 }
131 }
132
133 /// Builder for #name.
134 #[derive(Default)]
135 pub struct #builder_name {
136 #(#builder_fields),*
137 }
138
139 impl #builder_name {
140 /// Create a new builder.
141 pub fn new() -> Self {
142 Self::default()
143 }
144
145 #(#builder_methods)*
146
147 /// Build the configuration, validating all fields.
148 pub fn build(self) -> Result<#name, String> {
149 Ok(#name {
150 #(#build_assignments),*
151 })
152 }
153 }
154 };
155
156 TokenStream::from(expanded)
157}
158
159/// Derive macro for generating preset constructors.
160///
161/// # Attributes
162///
163/// - `#[preset(name = "preset_name", field1 = value1, field2 = value2, ...)]`
164///
165/// Each preset attribute generates a static constructor method.
166///
167/// # Example
168///
169/// ```rust,ignore
170/// #[derive(Preset)]
171/// #[preset(name = "fast", workers = 4, buffer = 1024)]
172/// #[preset(name = "balanced", workers = 8, buffer = 4096)]
173/// struct Config {
174/// workers: usize,
175/// buffer: usize,
176/// }
177///
178/// // Usage:
179/// let config = Config::fast_preset();
180/// ```
181#[proc_macro_derive(Preset, attributes(preset))]
182pub fn derive_preset(input: TokenStream) -> TokenStream {
183 let input = parse_macro_input!(input as DeriveInput);
184 let name = &input.ident;
185
186 let mut preset_methods = Vec::new();
187
188 // Parse preset attributes
189 for attr in &input.attrs {
190 if attr.path().is_ident("preset") {
191 // Simplified: just generate a basic preset method
192 let method_name = syn::Ident::new("preset", name.span());
193 preset_methods.push(quote! {
194 pub fn #method_name() -> Self {
195 Self::default()
196 }
197 });
198 }
199 }
200
201 let expanded = quote! {
202 impl #name {
203 #(#preset_methods)*
204 }
205 };
206
207 TokenStream::from(expanded)
208}
209
210/// Derive macro for metrics instrumentation.
211///
212/// Automatically adds metrics collection to struct methods.
213///
214/// # Example
215///
216/// ```rust,ignore
217/// #[derive(Instrumented)]
218/// struct MyPredictor {
219/// #[metrics]
220/// collector: Arc<MetricsCollector>,
221/// }
222/// ```
223#[proc_macro_derive(Instrumented, attributes(metrics))]
224pub fn derive_instrumented(input: TokenStream) -> TokenStream {
225 let input = parse_macro_input!(input as DeriveInput);
226 let name = &input.ident;
227
228 let expanded = quote! {
229 impl kizzasi::telemetry::Instrumented for #name {
230 fn metrics(&self) -> std::sync::Arc<kizzasi::telemetry::MetricsCollector> {
231 self.collector.clone()
232 }
233 }
234 };
235
236 TokenStream::from(expanded)
237}
238
239#[cfg(test)]
240mod tests {
241 // Note: Testing proc-macros requires integration tests
242 // See tests/ directory for actual test cases
243}