use {
crate::attr::MethodOrFunction,
proc_macro2::TokenStream as TokenStream2,
quote::quote,
std::borrow::Borrow,
syn::{Field, Fields, FieldsNamed, FieldsUnnamed, parse_str},
};
pub(crate) fn builder_for_functions(
name: &TokenStream2,
fns: Vec<&MethodOrFunction>,
) -> TokenStream2 {
if fns.is_empty() {
quote!(Ok(None))
} else {
let public_functions = fns
.into_iter()
.map(|fun| {
let (args, tys): (Vec<_>, Vec<_>) = fun
.args
.iter()
.enumerate()
.map(|(idx, ty)| {
(
parse_str::<syn::Ident>(&format!("a{idx}"))
.expect("This should always be a valid ident"),
parse_str::<syn::Type>(ty).expect("This should always be a valid type"),
)
})
.unzip();
let fn_name = parse_str::<syn::Ident>(&fun.name)
.expect("This should always be a valid ident");
quote! {
table.set(stringify!(#fn_name),
lua.create_function(|this, (#(#args),*): (#(#tys),*)| {
Ok(#name::#fn_name(#(#args),*))
})?
)?;
}
})
.collect::<Vec<_>>();
quote!(
Ok({
let table = lua.create_table()?;
#(#public_functions)*
Some(table)
})
)
}
}
pub(crate) fn builder_for_fields(name: &TokenStream2, fields: &Fields) -> TokenStream2 {
match fields {
Fields::Unit => quote! { Ok::<_, ::mlua::Error>(#name) },
Fields::Unnamed(unnamed) => builder_for_unnamed(name, unnamed),
Fields::Named(named) => builder_for_named(name, named),
}
}
fn builder_for_unnamed(name: &TokenStream2, fields: &FieldsUnnamed) -> TokenStream2 {
let (access, tys) = generate_tuple_access(fields.unnamed.iter());
quote! {
lua.create_function(|_, args: #tys| {
Ok(#name (#access))
})
}
}
fn builder_for_named(name: &TokenStream2, fields: &FieldsNamed) -> TokenStream2 {
let names = fields.named.iter().map(|x| &x.ident);
quote! {
lua.create_function(|_, data: ::mlua::Table| {
Ok(#name {
#( #names: data.get(stringify!(#names))?, )*
})
})
}
}
pub fn generate_tuple_access<'l, I: Iterator<Item = &'l Field>>(
mut fields_unnamed: I,
) -> (TokenStream2, TokenStream2) {
match (fields_unnamed.next(), fields_unnamed.next()) {
(None, _) => (quote!(), quote! {()}),
(Some(field_unnamed), None) => {
let ty = &field_unnamed.ty;
(quote!(args), quote!((#ty)))
},
(Some(first), Some(second)) => {
fn process_values<B: Borrow<Field>>(
(idx, arg): (usize, &B),
) -> (TokenStream2, syn::Type) {
let index = syn::Index::from(idx);
(quote!(args.#index), arg.borrow().ty.clone())
}
let (mut args_ident, mut tys): (Vec<_>, Vec<_>) = [first, second]
.iter()
.enumerate()
.map(process_values)
.unzip();
let (remaining_args_ident, remaining_tys): (Vec<_>, Vec<_>) = fields_unnamed
.enumerate()
.map(|(idx, value)| (idx + 2, value))
.map(process_values)
.unzip();
args_ident.extend(remaining_args_ident);
tys.extend(remaining_tys);
(quote!(#(#args_ident),*), quote!((#(#tys),*)))
},
}
}