use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{ItemFn, ItemImpl};
use crate::extendr_options::ExtendrOptions;
use crate::wrappers;
pub(crate) fn extendr_impl(
mut item_impl: ItemImpl,
opts: &ExtendrOptions,
) -> syn::Result<TokenStream> {
if item_impl.defaultness.is_some() {
return Err(syn::Error::new_spanned(
item_impl,
"default not allowed in #[extendr] impl",
));
}
if item_impl.unsafety.is_some() {
return Err(syn::Error::new_spanned(
item_impl,
"unsafe not allowed in #[extendr] impl",
));
}
if item_impl.generics.const_params().count() != 0 {
return Err(syn::Error::new_spanned(
item_impl,
"const params not allowed in #[extendr] impl",
));
}
if item_impl.generics.type_params().count() != 0 {
return Err(syn::Error::new_spanned(
item_impl,
"type params not allowed in #[extendr] impl",
));
}
if item_impl.generics.where_clause.is_some() {
return Err(syn::Error::new_spanned(
item_impl,
"where clause not allowed in #[extendr] impl",
));
}
let self_ty = item_impl.self_ty.as_ref();
let self_ty_name = wrappers::type_name(self_ty);
let prefix = format!("{}__", self_ty_name);
let mut method_meta_names = Vec::new();
let doc_string = wrappers::get_doc_string(&item_impl.attrs);
let mut wrappers: Vec<ItemFn> = Vec::new();
for impl_item in &mut item_impl.items {
if let syn::ImplItem::Fn(ref mut method) = impl_item {
method_meta_names.push(format_ident!(
"{}{}__{}",
wrappers::META_PREFIX,
self_ty_name,
method.sig.ident
));
wrappers::make_function_wrappers(
opts,
&mut wrappers,
prefix.as_str(),
&method.attrs,
&mut method.sig,
Some(self_ty),
)?;
}
}
let meta_name = format_ident!("{}{}", wrappers::META_PREFIX, self_ty_name);
let expanded = TokenStream::from(quote! {
#item_impl
#( #wrappers )*
#[allow(non_snake_case)]
fn #meta_name(impls: &mut Vec<extendr_api::metadata::Impl>) {
let mut methods = Vec::new();
#( #method_meta_names(&mut methods); )*
impls.push(extendr_api::metadata::Impl {
doc: #doc_string,
name: #self_ty_name,
methods,
});
}
});
Ok(expanded)
}