use proc_macro::TokenStream;
use quote::quote;
use syn::ItemFn;
fn extract_reference_inner_type(param_type: &syn::Type) -> &syn::Type {
match param_type {
syn::Type::Reference(type_ref) => &type_ref.elem,
_ => param_type, }
}
pub fn rustleaf_fn(input: ItemFn) -> TokenStream {
let original_fn = &input;
let original_name = &input.sig.ident;
let params: Vec<_> = input
.sig
.inputs
.iter()
.map(|param| {
if let syn::FnArg::Typed(typed_param) = param {
if let syn::Pat::Ident(ident_pat) = &*typed_param.pat {
(ident_pat.ident.clone(), typed_param.ty.clone())
} else {
panic!("Function parameters must be simple identifiers");
}
} else {
panic!("Function parameters must be typed (no self allowed)");
}
})
.collect();
let wrapper_body = generate_wrapper(original_name, ¶ms);
let expanded = quote! {
#original_fn
#wrapper_body
};
TokenStream::from(expanded)
}
fn generate_wrapper(
original_name: &syn::Ident,
params: &[(syn::Ident, Box<syn::Type>)],
) -> proc_macro2::TokenStream {
let wrapper_name = quote::format_ident!("rustleaf_{}", original_name);
let param_count = params.len();
let param_conversions: Vec<_> = 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 mut #param_name = values.pop().ok_or_else(|| anyhow::anyhow!("Missing parameter {} for function {}", #i, stringify!(#original_name)))?;
let #param_name = &mut *rustleaf::core::BorrowMutValueAs::<#inner_type>::borrow_mut_value_as(&mut #param_name)?;
}
} else {
let inner_type = extract_reference_inner_type(param_type);
quote! {
let #param_name = values.pop().ok_or_else(|| anyhow::anyhow!("Missing parameter {} for function {}", #i, stringify!(#original_name)))?;
let #param_name = &*rustleaf::core::BorrowValueAs::<#inner_type>::borrow_value_as(&#param_name)?;
}
}
} else {
quote! {
let #param_name = values.pop().ok_or_else(|| anyhow::anyhow!("Missing parameter {} for function {}", #i, stringify!(#original_name)))?;
let #param_name = #param_type::from_value(#param_name)?;
}
}
}).collect();
let param_names: Vec<_> = params.iter().map(|(param_name, _)| param_name).collect();
quote! {
pub fn #wrapper_name(mut 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!(#original_name),
#param_count,
values.len()
));
}
values.reverse();
#(#param_conversions)*
let result = #original_name(#(#param_names),*);
Ok(result.to_value())
}
}
}
pub fn is_reference_type(ty: &syn::Type) -> bool {
matches!(ty, syn::Type::Reference(_))
}
pub fn is_mutable_reference(ty: &syn::Type) -> bool {
if let syn::Type::Reference(type_ref) = ty {
type_ref.mutability.is_some()
} else {
false
}
}