use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Item, ItemImpl, ItemStruct};
mod function_wrapper;
mod method_wrapper;
#[proc_macro_derive(RustValue)]
pub fn derive_rust_value(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let expanded = quote! {
impl crate::core::value::RustValue for #name {
fn type_name(&self) -> &'static str {
stringify!(#name)
}
}
};
TokenStream::from(expanded)
}
#[proc_macro]
pub fn rustleaf_tests(_input: TokenStream) -> TokenStream {
TokenStream::new()
}
#[proc_macro_attribute]
pub fn rust_value_any(_args: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemImpl);
let as_any_methods = quote! {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
};
let ItemImpl {
attrs,
defaultness,
unsafety,
impl_token,
generics,
trait_,
self_ty,
brace_token: _,
items,
} = input;
let expanded = if let Some((bang, path, for_token)) = trait_ {
quote! {
#(#attrs)*
#defaultness #unsafety #impl_token #generics #bang #path #for_token #self_ty {
#as_any_methods
#(#items)*
}
}
} else {
quote! {
#(#attrs)*
#defaultness #unsafety #impl_token #generics #self_ty {
#as_any_methods
#(#items)*
}
}
};
TokenStream::from(expanded)
}
#[proc_macro_derive(RustLeafWrapper, attributes(core_type))]
pub fn derive_rustleaf_wrapper(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let wrapper_name = &input.ident;
let core_type = input
.attrs
.iter()
.find_map(|attr| {
if attr.path().is_ident("core_type") {
attr.parse_args::<syn::Type>().ok()
} else {
None
}
})
.expect("RustLeafWrapper requires #[core_type(Type)] attribute");
let expanded = quote! {
impl #wrapper_name {
pub fn new(inner: #core_type) -> Self {
Self(std::rc::Rc::new(std::cell::RefCell::new(inner)))
}
pub fn borrow(&self) -> std::cell::Ref<#core_type> {
self.0.borrow()
}
pub fn borrow_mut(&self) -> std::cell::RefMut<#core_type> {
self.0.borrow_mut()
}
}
};
TokenStream::from(expanded)
}
#[proc_macro_attribute]
pub fn rustleaf(_args: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as Item);
match input {
Item::Struct(item_struct) => rustleaf_struct(item_struct),
Item::Impl(item_impl) => method_wrapper::rustleaf_impl(item_impl),
Item::Fn(item_fn) => function_wrapper::rustleaf_fn(item_fn),
_ => {
panic!("rustleaf attribute can only be applied to structs, impl blocks, and functions")
}
}
}
fn rustleaf_struct(input: ItemStruct) -> TokenStream {
let struct_name = &input.ident;
let ref_name = quote::format_ident!("{}Ref", struct_name);
let original_struct = quote! {
#input
};
let wrapper_struct = quote! {
#[derive(Debug, Clone, RustLeafWrapper)]
#[core_type(#struct_name)]
pub struct #ref_name(std::rc::Rc<std::cell::RefCell<#struct_name>>);
};
let rust_value_impl = generate_trivial_rust_value_impl(&ref_name, struct_name);
let get_property_impl = generate_get_property_impl(&ref_name, &input);
let borrow_value_impl = generate_borrow_value_impl(struct_name, &ref_name);
let expanded = quote! {
#original_struct
#wrapper_struct
#rust_value_impl
#get_property_impl
#borrow_value_impl
};
TokenStream::from(expanded)
}
fn generate_trivial_rust_value_impl(
ref_name: &syn::Ident,
struct_name: &syn::Ident,
) -> proc_macro2::TokenStream {
let struct_name_str = struct_name.to_string();
quote! {
impl rustleaf::core::RustValue for #ref_name {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn dyn_clone(&self) -> Box<dyn rustleaf::core::RustValue> {
Box::new(self.clone())
}
fn type_name(&self) -> Option<&str> {
Some(#struct_name_str)
}
fn str(&self) -> String {
format!("{:?}", self.borrow())
}
fn get_attr(&self, name: &str) -> Option<rustleaf::core::Value> {
if let Some(value) = self.get_property(name) {
return Some(value);
}
self.get_method(name)
}
}
}
}
fn generate_get_property_impl(
ref_name: &syn::Ident,
input: &ItemStruct,
) -> proc_macro2::TokenStream {
let mut field_arms = Vec::new();
if let syn::Fields::Named(named_fields) = &input.fields {
for field in &named_fields.named {
if let Some(field_name) = &field.ident {
if matches!(field.vis, syn::Visibility::Public(_)) {
let field_name_str = field_name.to_string();
let field_access = field_type_to_value_conversion(
&field.ty,
quote! { self.borrow().#field_name },
);
field_arms.push(quote! {
#field_name_str => Some(#field_access),
});
}
}
}
}
quote! {
impl #ref_name {
pub fn get_property(&self, name: &str) -> Option<rustleaf::core::Value> {
match name {
#(#field_arms)*
_ => None,
}
}
}
}
}
fn field_type_to_value_conversion(
field_type: &syn::Type,
field_access: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
if let syn::Type::Path(type_path) = field_type {
if let Some(segment) = type_path.path.segments.last() {
match segment.ident.to_string().as_str() {
"f64" => return quote! { rustleaf::core::Value::Float(#field_access) },
"f32" => return quote! { rustleaf::core::Value::Float(#field_access as f64) },
"i64" => return quote! { rustleaf::core::Value::Int(#field_access) },
"i32" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
"i16" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
"i8" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
"u64" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
"u32" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
"u16" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
"u8" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
"usize" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
"isize" => return quote! { rustleaf::core::Value::Int(#field_access as i64) },
"bool" => return quote! { rustleaf::core::Value::Bool(#field_access) },
"String" => return quote! { rustleaf::core::Value::String(#field_access.clone()) },
_ => {}
}
}
}
if matches!(field_type, syn::Type::Reference(_)) {
return quote! { rustleaf::core::Value::String(#field_access.to_string()) };
}
quote! { (#field_access).into() }
}
fn generate_borrow_value_impl(
struct_name: &syn::Ident,
ref_name: &syn::Ident,
) -> proc_macro2::TokenStream {
quote! {
impl rustleaf::core::BorrowValueAs<#struct_name> for rustleaf::core::Value {
type Guard<'a> = std::cell::Ref<'a, #struct_name>;
fn borrow_value_as(&self) -> Result<Self::Guard<'_>, anyhow::Error> {
if let Some(rust_value) = self.downcast_rust_value::<#ref_name>() {
Ok(rust_value.borrow())
} else {
Err(anyhow::anyhow!("Cannot borrow {} as {}", self.type_name(), stringify!(#struct_name)))
}
}
}
impl rustleaf::core::BorrowMutValueAs<#struct_name> for rustleaf::core::Value {
type Guard<'a> = std::cell::RefMut<'a, #struct_name>;
fn borrow_mut_value_as(&mut self) -> Result<Self::Guard<'_>, anyhow::Error> {
if let Some(rust_value) = self.downcast_rust_value::<#ref_name>() {
Ok(rust_value.borrow_mut())
} else {
Err(anyhow::anyhow!("Cannot borrow {} as mut {}", self.type_name(), stringify!(#struct_name)))
}
}
}
}
}