use proc_macro::TokenStream;
use quote::quote;
use syn::{FnArg, ImplItem, ItemImpl, ReturnType};
use crate::function_wrapper::{is_mutable_reference, is_reference_type};
fn extract_reference_inner_type(param_type: &syn::Type) -> &syn::Type {
match param_type {
syn::Type::Reference(type_ref) => &type_ref.elem,
_ => param_type, }
}
fn to_snake_case(s: &str) -> String {
let mut result = String::new();
for (i, c) in s.chars().enumerate() {
if c.is_uppercase() && i > 0 {
result.push('_');
}
result.push(c.to_ascii_lowercase());
}
result
}
pub fn rustleaf_impl(input: ItemImpl) -> TokenStream {
let original_impl = &input;
let struct_name = if let syn::Type::Path(type_path) = &*input.self_ty {
&type_path.path.segments.last().unwrap().ident
} else {
panic!("rustleaf impl blocks must be for named types");
};
let mut static_wrappers = Vec::new();
let mut instance_wrappers = Vec::new();
for item in &input.items {
if let ImplItem::Fn(method) = item {
let has_self = method
.sig
.inputs
.iter()
.any(|arg| matches!(arg, FnArg::Receiver(_)));
if has_self {
let wrapper = generate_instance_method_wrapper(method, struct_name);
instance_wrappers.push(wrapper);
} else {
let wrapper = generate_static_method_wrapper(method, struct_name);
static_wrappers.push(wrapper);
}
}
}
let ref_name = quote::format_ident!("{}Ref", struct_name);
let instance_method_names: Vec<String> = input
.items
.iter()
.filter_map(|item| {
if let ImplItem::Fn(method) = item {
let has_self = method
.sig
.inputs
.iter()
.any(|arg| matches!(arg, FnArg::Receiver(_)));
if has_self {
Some(method.sig.ident.to_string())
} else {
None
}
} else {
None
}
})
.collect();
let get_method_impl = generate_get_method_impl(&ref_name, &instance_method_names);
let expanded = quote! {
#original_impl
#(#static_wrappers)*
impl #ref_name {
#(#instance_wrappers)*
}
#get_method_impl
};
TokenStream::from(expanded)
}
fn generate_static_method_wrapper(
method: &syn::ImplItemFn,
struct_name: &syn::Ident,
) -> proc_macro2::TokenStream {
let ref_name = quote::format_ident!("{}Ref", struct_name);
let method_name = &method.sig.ident;
let struct_name_snake = to_snake_case(&struct_name.to_string());
let method_name_snake = to_snake_case(&method_name.to_string());
let wrapper_name = quote::format_ident!("rustleaf_{}_{}", struct_name_snake, method_name_snake);
let params: Vec<_> = method
.sig
.inputs
.iter()
.filter_map(|param| {
if let FnArg::Typed(typed_param) = param {
if let syn::Pat::Ident(ident_pat) = &*typed_param.pat {
Some((ident_pat.ident.clone(), typed_param.ty.clone()))
} else {
None
}
} else {
None
}
})
.collect();
let param_count = params.len();
let param_conversions = generate_param_conversions(¶ms, &wrapper_name, method_name);
let param_names: Vec<_> = params.iter().map(|(name, _)| name).collect();
let returns_self = matches!(
&method.sig.output,
ReturnType::Type(_, return_type) if matches!(**return_type, syn::Type::Path(ref path) if path.path.is_ident("Self"))
);
if returns_self {
quote! {
pub fn #wrapper_name(values: Vec<rustleaf::core::Value>) -> anyhow::Result<rustleaf::core::Value> {
use rustleaf::core::{ToValue, BorrowValueAs, BorrowMutValueAs, FromValue};
if values.len() != #param_count {
return Err(anyhow::anyhow!(
"Function {}::{} expects {} parameters, got {}",
stringify!(#struct_name),
stringify!(#method_name),
#param_count,
values.len()
));
}
#(#param_conversions)*
let instance = #struct_name::#method_name(#(#param_names),*);
let wrapper = #ref_name::new(instance);
Ok(rustleaf::core::Value::from_rust(wrapper))
}
}
} else {
quote! {
pub fn #wrapper_name(values: Vec<rustleaf::core::Value>) -> anyhow::Result<rustleaf::core::Value> {
use rustleaf::core::{ToValue, BorrowValueAs, BorrowMutValueAs, FromValue};
if values.len() != #param_count {
return Err(anyhow::anyhow!(
"Function {}::{} expects {} parameters, got {}",
stringify!(#struct_name),
stringify!(#method_name),
#param_count,
values.len()
));
}
#(#param_conversions)*
let result = #struct_name::#method_name(#(#param_names),*);
Ok(result.to_value())
}
}
}
}
fn generate_instance_method_wrapper(
method: &syn::ImplItemFn,
struct_name: &syn::Ident,
) -> proc_macro2::TokenStream {
let method_name = &method.sig.ident;
let method_name_snake = to_snake_case(&method_name.to_string());
let wrapper_name = quote::format_ident!("rustleaf_{}", method_name_snake);
let mut self_mutability = None;
let mut params = Vec::new();
for param in &method.sig.inputs {
match param {
FnArg::Receiver(receiver) => {
self_mutability = Some(receiver.mutability.is_some());
}
FnArg::Typed(typed_param) => {
if let syn::Pat::Ident(ident_pat) = &*typed_param.pat {
params.push((ident_pat.ident.clone(), typed_param.ty.clone()));
}
}
}
}
let is_self_mut = self_mutability.unwrap_or(false);
let total_param_count = params.len() + 1;
let param_conversions: Vec<_> = params.iter().enumerate().map(|(i, (param_name, param_type))| {
let param_index = i + 1;
if is_reference_type(param_type) {
if is_mutable_reference(param_type) {
let inner_type = extract_reference_inner_type(param_type);
quote! {
let #param_name = if let Some(value) = values.get_mut(#param_index) {
&mut *rustleaf::core::BorrowMutValueAs::<#inner_type>::borrow_mut_value_as(value)?
} else {
return Err(anyhow::anyhow!("Missing parameter {} for method {}::{}", #param_index, stringify!(#struct_name), stringify!(#method_name)));
};
}
} else {
let inner_type = extract_reference_inner_type(param_type);
quote! {
let #param_name = if let Some(value) = values.get(#param_index) {
&*rustleaf::core::BorrowValueAs::<#inner_type>::borrow_value_as(value)?
} else {
return Err(anyhow::anyhow!("Missing parameter {} for method {}::{}", #param_index, stringify!(#struct_name), stringify!(#method_name)));
};
}
}
} else {
quote! {
let #param_name = if let Some(value) = values.get(#param_index) {
#param_type::from_value(value.clone())?
} else {
return Err(anyhow::anyhow!("Missing parameter {} for method {}::{}", #param_index, stringify!(#struct_name), stringify!(#method_name)));
};
}
}
}).collect();
let param_names: Vec<_> = params.iter().map(|(name, _)| name).collect();
if is_self_mut {
quote! {
pub fn #wrapper_name(values: Vec<rustleaf::core::Value>) -> anyhow::Result<rustleaf::core::Value> {
use rustleaf::core::{ToValue, BorrowValueAs, BorrowMutValueAs, FromValue};
if values.len() != #total_param_count {
return Err(anyhow::anyhow!(
"Method {}::{} expects {} parameters (including self), got {}",
stringify!(#struct_name),
stringify!(#method_name),
#total_param_count,
values.len()
));
}
let self_value = values.get(0).ok_or_else(|| anyhow::anyhow!("Missing self parameter"))?;
let self_ref = self_value.downcast_rust_value::<Self>()
.ok_or_else(|| anyhow::anyhow!("Expected {}Ref for self parameter, got {}", stringify!(#struct_name), self_value.type_name()))?;
#(#param_conversions)*
let mut borrowed = self_ref.borrow_mut();
let result = borrowed.#method_name(#(#param_names),*);
Ok(result.to_value())
}
}
} else {
quote! {
pub fn #wrapper_name(values: Vec<rustleaf::core::Value>) -> anyhow::Result<rustleaf::core::Value> {
use rustleaf::core::{ToValue, BorrowValueAs, BorrowMutValueAs, FromValue};
if values.len() != #total_param_count {
return Err(anyhow::anyhow!(
"Method {}::{} expects {} parameters (including self), got {}",
stringify!(#struct_name),
stringify!(#method_name),
#total_param_count,
values.len()
));
}
let self_value = values.get(0).ok_or_else(|| anyhow::anyhow!("Missing self parameter"))?;
let self_ref = self_value.downcast_rust_value::<Self>()
.ok_or_else(|| anyhow::anyhow!("Expected {}Ref for self parameter, got {}", stringify!(#struct_name), self_value.type_name()))?;
#(#param_conversions)*
let borrowed = self_ref.borrow();
let result = borrowed.#method_name(#(#param_names),*);
Ok(result.to_value())
}
}
}
}
fn generate_param_conversions(
params: &[(syn::Ident, Box<syn::Type>)],
wrapper_name: &syn::Ident,
_method_name: &syn::Ident,
) -> Vec<proc_macro2::TokenStream> {
params.iter().enumerate().map(|(i, (param_name, param_type))| {
if is_reference_type(param_type) {
if is_mutable_reference(param_type) {
let inner_type = extract_reference_inner_type(param_type);
quote! {
let #param_name = if let Some(value) = values.get_mut(#i) {
&mut *rustleaf::core::BorrowMutValueAs::<#inner_type>::borrow_mut_value_as(value)?
} else {
return Err(anyhow::anyhow!("Missing parameter {} for function {}", #i, stringify!(#wrapper_name)));
};
}
} else {
let inner_type = extract_reference_inner_type(param_type);
quote! {
let #param_name = if let Some(value) = values.get(#i) {
&*rustleaf::core::BorrowValueAs::<#inner_type>::borrow_value_as(value)?
} else {
return Err(anyhow::anyhow!("Missing parameter {} for function {}", #i, stringify!(#wrapper_name)));
};
}
}
} else {
quote! {
let #param_name = if let Some(value) = values.get(#i) {
#param_type::from_value(value.clone())?
} else {
return Err(anyhow::anyhow!("Missing parameter {} for function {}", #i, stringify!(#wrapper_name)));
};
}
}
}).collect()
}
fn generate_get_method_impl(
ref_name: &syn::Ident,
method_names: &[String],
) -> proc_macro2::TokenStream {
let mut method_arms = Vec::new();
for method_name in method_names {
let method_name_snake = to_snake_case(method_name);
let wrapper_name = quote::format_ident!("rustleaf_{}", method_name_snake);
method_arms.push(quote! {
#method_name => Some(rustleaf::core::Value::from_rust(rustleaf::core::BoundMethodVec::new(
&rustleaf::core::Value::rust_value(self.dyn_clone()),
Self::#wrapper_name,
))),
});
}
quote! {
impl #ref_name {
pub fn get_method(&self, name: &str) -> Option<rustleaf::core::Value> {
match name {
#(#method_arms)*
_ => None,
}
}
}
}
}