rustleaf_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput, Item, ItemImpl, ItemStruct};
4
5mod function_wrapper;
6mod method_wrapper;
7
8#[proc_macro_derive(RustValue)]
9pub fn derive_rust_value(input: TokenStream) -> TokenStream {
10    let input = parse_macro_input!(input as DeriveInput);
11    let name = &input.ident;
12
13    let expanded = quote! {
14        impl crate::core::value::RustValue for #name {
15            fn type_name(&self) -> &'static str {
16                stringify!(#name)
17            }
18        }
19    };
20
21    TokenStream::from(expanded)
22}
23
24#[proc_macro]
25pub fn rustleaf_tests(_input: TokenStream) -> TokenStream {
26    TokenStream::new()
27}
28
29#[proc_macro_attribute]
30pub fn rust_value_any(_args: TokenStream, input: TokenStream) -> TokenStream {
31    let input = parse_macro_input!(input as ItemImpl);
32
33    // Generate the as_any methods
34    let as_any_methods = quote! {
35        fn as_any(&self) -> &dyn std::any::Any {
36            self
37        }
38
39        fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
40            self
41        }
42    };
43
44    // Build the new impl block with the added methods
45    let ItemImpl {
46        attrs,
47        defaultness,
48        unsafety,
49        impl_token,
50        generics,
51        trait_,
52        self_ty,
53        brace_token: _,
54        items,
55    } = input;
56
57    // Insert the as_any methods at the beginning of the impl items
58    let expanded = if let Some((bang, path, for_token)) = trait_ {
59        quote! {
60            #(#attrs)*
61            #defaultness #unsafety #impl_token #generics #bang #path #for_token #self_ty {
62                #as_any_methods
63
64                #(#items)*
65            }
66        }
67    } else {
68        // This shouldn't happen for RustValue impls, but handle it gracefully
69        quote! {
70            #(#attrs)*
71            #defaultness #unsafety #impl_token #generics #self_ty {
72                #as_any_methods
73
74                #(#items)*
75            }
76        }
77    };
78
79    TokenStream::from(expanded)
80}
81
82#[proc_macro_derive(RustLeafWrapper, attributes(core_type))]
83pub fn derive_rustleaf_wrapper(input: TokenStream) -> TokenStream {
84    let input = parse_macro_input!(input as DeriveInput);
85    let wrapper_name = &input.ident;
86
87    // Extract the core type from the #[core_type(Type)] attribute
88    let core_type = input
89        .attrs
90        .iter()
91        .find_map(|attr| {
92            if attr.path().is_ident("core_type") {
93                attr.parse_args::<syn::Type>().ok()
94            } else {
95                None
96            }
97        })
98        .expect("RustLeafWrapper requires #[core_type(Type)] attribute");
99
100    let expanded = quote! {
101        impl #wrapper_name {
102            pub fn new(inner: #core_type) -> Self {
103                Self(std::rc::Rc::new(std::cell::RefCell::new(inner)))
104            }
105
106            pub fn borrow(&self) -> std::cell::Ref<#core_type> {
107                self.0.borrow()
108            }
109
110            pub fn borrow_mut(&self) -> std::cell::RefMut<#core_type> {
111                self.0.borrow_mut()
112            }
113        }
114    };
115
116    TokenStream::from(expanded)
117}
118
119#[proc_macro_attribute]
120pub fn rustleaf(_args: TokenStream, input: TokenStream) -> TokenStream {
121    let input = parse_macro_input!(input as Item);
122
123    match input {
124        Item::Struct(item_struct) => rustleaf_struct(item_struct),
125        Item::Impl(item_impl) => method_wrapper::rustleaf_impl(item_impl),
126        Item::Fn(item_fn) => function_wrapper::rustleaf_fn(item_fn),
127        _ => {
128            panic!("rustleaf attribute can only be applied to structs, impl blocks, and functions")
129        }
130    }
131}
132
133fn rustleaf_struct(input: ItemStruct) -> TokenStream {
134    let struct_name = &input.ident;
135    let ref_name = quote::format_ident!("{}Ref", struct_name);
136
137    // Generate the original struct unchanged
138    let original_struct = quote! {
139        #input
140    };
141
142    // Generate the wrapper struct with RustLeafWrapper derive
143    let wrapper_struct = quote! {
144        /// Generated wrapper for RustLeaf integration
145        #[derive(Debug, Clone, RustLeafWrapper)]
146        #[core_type(#struct_name)]
147        pub struct #ref_name(std::rc::Rc<std::cell::RefCell<#struct_name>>);
148    };
149
150    // Generate trivial RustValue methods
151    let rust_value_impl = generate_trivial_rust_value_impl(&ref_name, struct_name);
152
153    // Generate get_property helper for struct fields
154    let get_property_impl = generate_get_property_impl(&ref_name, &input);
155
156    // Generate BorrowValueAs implementations for this user type
157    let borrow_value_impl = generate_borrow_value_impl(struct_name, &ref_name);
158
159    let expanded = quote! {
160        #original_struct
161
162        #wrapper_struct
163
164        #rust_value_impl
165
166        #get_property_impl
167
168        #borrow_value_impl
169    };
170
171    TokenStream::from(expanded)
172}
173
174fn generate_trivial_rust_value_impl(
175    ref_name: &syn::Ident,
176    struct_name: &syn::Ident,
177) -> proc_macro2::TokenStream {
178    let struct_name_str = struct_name.to_string();
179
180    quote! {
181        impl rustleaf::core::RustValue for #ref_name {
182            fn as_any(&self) -> &dyn std::any::Any {
183                self
184            }
185
186            fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
187                self
188            }
189
190            fn dyn_clone(&self) -> Box<dyn rustleaf::core::RustValue> {
191                Box::new(self.clone())
192            }
193
194            fn type_name(&self) -> Option<&str> {
195                Some(#struct_name_str)
196            }
197
198            fn str(&self) -> String {
199                // Simple default implementation - can be improved later
200                format!("{:?}", self.borrow())
201            }
202
203            fn get_attr(&self, name: &str) -> Option<rustleaf::core::Value> {
204                // Try properties first (struct fields)
205                if let Some(value) = self.get_property(name) {
206                    return Some(value);
207                }
208
209                // Then try methods (from impl blocks)
210                self.get_method(name)
211            }
212        }
213    }
214}
215
216fn generate_get_property_impl(
217    ref_name: &syn::Ident,
218    input: &ItemStruct,
219) -> proc_macro2::TokenStream {
220    let mut field_arms = Vec::new();
221
222    // Analyze struct fields
223    if let syn::Fields::Named(named_fields) = &input.fields {
224        for field in &named_fields.named {
225            if let Some(field_name) = &field.ident {
226                // Only generate for public fields
227                if matches!(field.vis, syn::Visibility::Public(_)) {
228                    let field_name_str = field_name.to_string();
229                    let field_access = field_type_to_value_conversion(
230                        &field.ty,
231                        quote! { self.borrow().#field_name },
232                    );
233
234                    field_arms.push(quote! {
235                        #field_name_str => Some(#field_access),
236                    });
237                }
238            }
239        }
240    }
241
242    quote! {
243        impl #ref_name {
244            /// Generated helper for accessing struct fields
245            pub fn get_property(&self, name: &str) -> Option<rustleaf::core::Value> {
246                match name {
247                    #(#field_arms)*
248                    _ => None,
249                }
250            }
251        }
252    }
253}
254
255fn field_type_to_value_conversion(
256    field_type: &syn::Type,
257    field_access: proc_macro2::TokenStream,
258) -> proc_macro2::TokenStream {
259    // Handle common field types and convert to appropriate Value variants
260    if let syn::Type::Path(type_path) = field_type {
261        if let Some(segment) = type_path.path.segments.last() {
262            match segment.ident.to_string().as_str() {
263                "f64" => return quote! { rustleaf::core::Value::Float(#field_access) },
264                "f32" => return quote! { rustleaf::core::Value::Float(#field_access as f64) },
265                "i64" => return quote! { rustleaf::core::Value::Int(#field_access) },
266                "i32" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
267                "i16" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
268                "i8" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
269                "u64" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
270                "u32" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
271                "u16" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
272                "u8" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
273                "usize" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
274                "isize" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
275                "bool" => return quote! { rustleaf::core::Value::Bool(#field_access) },
276                "String" => return quote! { rustleaf::core::Value::String(#field_access.clone()) },
277                _ => {}
278            }
279        }
280    }
281
282    // Handle string slice
283    if matches!(field_type, syn::Type::Reference(_)) {
284        // This is a simplified check - in practice you'd want more sophisticated analysis
285        return quote! { rustleaf::core::Value::String(#field_access.to_string()) };
286    }
287
288    // Default fallback - try to convert with Into<Value>
289    quote! { (#field_access).into() }
290}
291
292fn generate_borrow_value_impl(
293    struct_name: &syn::Ident,
294    ref_name: &syn::Ident,
295) -> proc_macro2::TokenStream {
296    quote! {
297        // Generate BorrowValueAs implementation for this user-defined type
298        impl rustleaf::core::BorrowValueAs<#struct_name> for rustleaf::core::Value {
299            type Guard<'a> = std::cell::Ref<'a, #struct_name>;
300
301            fn borrow_value_as(&self) -> Result<Self::Guard<'_>, anyhow::Error> {
302                if let Some(rust_value) = self.downcast_rust_value::<#ref_name>() {
303                    Ok(rust_value.borrow())
304                } else {
305                    Err(anyhow::anyhow!("Cannot borrow {} as {}", self.type_name(), stringify!(#struct_name)))
306                }
307            }
308        }
309
310        // Generate BorrowMutValueAs implementation for this user-defined type
311        impl rustleaf::core::BorrowMutValueAs<#struct_name> for rustleaf::core::Value {
312            type Guard<'a> = std::cell::RefMut<'a, #struct_name>;
313
314            fn borrow_mut_value_as(&mut self) -> Result<Self::Guard<'_>, anyhow::Error> {
315                if let Some(rust_value) = self.downcast_rust_value::<#ref_name>() {
316                    Ok(rust_value.borrow_mut())
317                } else {
318                    Err(anyhow::anyhow!("Cannot borrow {} as mut {}", self.type_name(), stringify!(#struct_name)))
319                }
320            }
321        }
322    }
323}