diff --git a/extendr-macros/src/extendr_enum.rs b/extendr-macros/src/extendr_enum.rs index caab64422b..623cff2d11 100644 --- a/extendr-macros/src/extendr_enum.rs +++ b/extendr-macros/src/extendr_enum.rs @@ -1,19 +1,24 @@ use quote::{format_ident, quote}; +use crate::extendr_options::ExtendrOptions; + //TODO: Variants with Named structs, that happens to be ExternalPtr // could be supported. The API needs investigation though.. /// Adds the ability to take an `enum` of plain variants and turn them into -/// an R factor. -/// +/// an R factor. +/// /// The order of the enums listed in Rust dictates the order in `levels`. /// We do not use the discriminant value (if specified) for anything. -/// -/// -pub(crate) fn extendr_enum(item_enum: syn::ItemEnum) -> proc_macro::TokenStream { +/// +/// +pub(crate) fn extendr_enum( + item_enum: syn::ItemEnum, + opts: &ExtendrOptions, +) -> proc_macro::TokenStream { //TODO: error on opts that isn't used here: // first, inherent &opts, and see if any value is provided.. - + //FIXME: sanitize field names, as sometimes they have r# etc. let enum_name = &item_enum.ident; diff --git a/extendr-macros/src/extendr_function.rs b/extendr-macros/src/extendr_function.rs index 2c841ac7c5..56a1059649 100644 --- a/extendr-macros/src/extendr_function.rs +++ b/extendr-macros/src/extendr_function.rs @@ -1,10 +1,10 @@ -use crate::wrappers; +use crate::{extendr_options::ExtendrOptions, wrappers}; use proc_macro::TokenStream; use quote::quote; -use syn::{meta::ParseNestedMeta, ItemFn, Lit, LitBool}; +use syn::ItemFn; /// Generate bindings for a single function. -pub fn extendr_function(mut func: ItemFn, opts: &wrappers::ExtendrOptions) -> TokenStream { +pub fn extendr_function(mut func: ItemFn, opts: &ExtendrOptions) -> TokenStream { let mut wrappers: Vec = Vec::new(); wrappers::make_function_wrappers(opts, &mut wrappers, "", &func.attrs, &mut func.sig, None); @@ -14,56 +14,3 @@ pub fn extendr_function(mut func: ItemFn, opts: &wrappers::ExtendrOptions) -> To # ( #wrappers )* }) } - -impl wrappers::ExtendrOptions { - /// Parse a set of attribute arguments for `#[extendr(opts...)]` - /// - /// Supported options: - /// - /// - `use_try_from = bool` which uses `TryFrom` for argument conversions. - /// - `r_name = "name"` which specifies the name of the wrapper on the R-side. - /// - `use_rng = bool` ensures the RNG-state is pulled and pushed - /// - pub fn parse(&mut self, meta: ParseNestedMeta) -> syn::parse::Result<()> { - fn help_message() -> ! { - panic!("expected #[extendr(use_try_from = bool, r_name = \"name\", mod_name = \"r_mod_name\", use_rng = bool)]"); - } - - let value = match meta.value() { - Ok(value) => value, - Err(_) => help_message(), - }; - - if meta.path.is_ident("use_try_from") { - if let Ok(LitBool { value, .. }) = value.parse() { - self.use_try_from = value; - Ok(()) - } else { - help_message(); - } - } else if meta.path.is_ident("r_name") { - if let Ok(Lit::Str(litstr)) = value.parse() { - self.r_name = Some(litstr.value()); - Ok(()) - } else { - help_message(); - } - } else if meta.path.is_ident("mod_name") { - if let Ok(Lit::Str(litstr)) = value.parse() { - self.mod_name = Some(litstr.value()); - Ok(()) - } else { - help_message(); - } - } else if meta.path.is_ident("use_rng") { - if let Ok(LitBool { value, .. }) = value.parse() { - self.use_rng = value; - Ok(()) - } else { - help_message(); - } - } else { - help_message(); - } - } -} diff --git a/extendr-macros/src/extendr_impl.rs b/extendr-macros/src/extendr_impl.rs index 007be966fc..68814c98ab 100644 --- a/extendr-macros/src/extendr_impl.rs +++ b/extendr-macros/src/extendr_impl.rs @@ -3,7 +3,7 @@ use proc_macro::TokenStream; use quote::{format_ident, quote}; use syn::{ItemFn, ItemImpl}; -use crate::wrappers; +use crate::{extendr_options::ExtendrOptions, wrappers}; /// Handle trait implementations. /// @@ -36,7 +36,7 @@ use crate::wrappers; /// fn aux_func; /// } /// ``` -pub fn extendr_impl(mut item_impl: ItemImpl) -> TokenStream { +pub fn extendr_impl(mut item_impl: ItemImpl, opts: &ExtendrOptions) -> TokenStream { // Only `impl name { }` allowed if item_impl.defaultness.is_some() { return quote! { compile_error!("default not allowed in #[extendr] impl"); }.into(); @@ -62,7 +62,7 @@ pub fn extendr_impl(mut item_impl: ItemImpl) -> TokenStream { return quote! { compile_error!("where clause not allowed in #[extendr] impl"); }.into(); } - let opts = wrappers::ExtendrOptions::default(); + let opts = ExtendrOptions::default(); let self_ty = item_impl.self_ty.as_ref(); let self_ty_name = wrappers::type_name(self_ty); let prefix = format!("{}__", self_ty_name); @@ -103,6 +103,8 @@ pub fn extendr_impl(mut item_impl: ItemImpl) -> TokenStream { let meta_name = format_ident!("{}{self_ty_name}", wrappers::META_PREFIX); + //FIXME: use `opts` and `use_try_from` here! + let expanded = TokenStream::from(quote! { // The impl itself copied from the source. #item_impl @@ -136,15 +138,6 @@ pub fn extendr_impl(mut item_impl: ItemImpl) -> TokenStream { } } - //FIXME: where is this used? - // impl extendr_api::IntoRobj for #self_ty { - // fn into_robj(self) -> Robj { - // let res = ExternalPtr::new(self).into(); - // res.set_attrib(class_symbol(), #self_ty_name).unwrap(); - // res - // } - // } - // Output conversion function for this type. impl From<#self_ty> for Robj { fn from(value: #self_ty) -> Self { diff --git a/extendr-macros/src/extendr_options.rs b/extendr-macros/src/extendr_options.rs new file mode 100644 index 0000000000..5683a5a664 --- /dev/null +++ b/extendr-macros/src/extendr_options.rs @@ -0,0 +1,65 @@ +use syn::{meta::ParseNestedMeta, Lit, LitBool}; + +#[derive(Debug, Default)] +pub struct ExtendrOptions { + pub use_try_from: bool, + pub r_name: Option, + pub mod_name: Option, + pub use_rng: bool, + /// For `#[extendr]` on `enum`s, there is a wrapper around `Vec` + /// generated; Default to `VecEnumtype`. + pub vec_name: Option, +} + +impl ExtendrOptions { + /// Parse a set of attribute arguments for `#[extendr(opts...)]` + /// + /// Supported options: + /// + /// - `use_try_from = bool` which uses `TryFrom` for argument conversions. + /// - `r_name = "name"` which specifies the name of the wrapper on the R-side. + /// - `use_rng = bool` ensures the RNG-state is pulled and pushed + /// + pub fn parse(&mut self, meta: ParseNestedMeta) -> syn::parse::Result<()> { + fn help_message() -> ! { + panic!("expected #[extendr(use_try_from = bool, r_name = \"name\", mod_name = \"r_mod_name\", use_rng = bool)]"); + } + + let value = match meta.value() { + Ok(value) => value, + Err(_) => help_message(), + }; + + if meta.path.is_ident("use_try_from") { + if let Ok(LitBool { value, .. }) = value.parse() { + self.use_try_from = value; + Ok(()) + } else { + help_message(); + } + } else if meta.path.is_ident("r_name") { + if let Ok(Lit::Str(litstr)) = value.parse() { + self.r_name = Some(litstr.value()); + Ok(()) + } else { + help_message(); + } + } else if meta.path.is_ident("mod_name") { + if let Ok(Lit::Str(litstr)) = value.parse() { + self.mod_name = Some(litstr.value()); + Ok(()) + } else { + help_message(); + } + } else if meta.path.is_ident("use_rng") { + if let Ok(LitBool { value, .. }) = value.parse() { + self.use_rng = value; + Ok(()) + } else { + help_message(); + } + } else { + help_message(); + } + } +} diff --git a/extendr-macros/src/lib.rs b/extendr-macros/src/lib.rs index 83f4f3775a..364e15c372 100644 --- a/extendr-macros/src/lib.rs +++ b/extendr-macros/src/lib.rs @@ -58,10 +58,13 @@ mod R; mod call; mod dataframe; +// region: #[extendr] macro +mod extendr_enum; mod extendr_function; mod extendr_impl; mod extendr_module; -mod extendr_enum; +mod extendr_options; +// endregion mod list; mod list_struct; mod pairlist; @@ -74,7 +77,8 @@ use syn::{parse_macro_input, Item}; #[proc_macro_attribute] pub fn extendr(attr: TokenStream, item: TokenStream) -> TokenStream { - let mut opts = wrappers::ExtendrOptions::default(); + let item = parse_macro_input!(item as Item); + let mut opts = extendr_options::ExtendrOptions::default(); let extendr_opts_parser = syn::meta::parser(|meta| opts.parse(meta)); parse_macro_input!(attr with extendr_opts_parser); diff --git a/extendr-macros/src/wrappers.rs b/extendr-macros/src/wrappers.rs index a80d8b4d25..a49b46b341 100644 --- a/extendr-macros/src/wrappers.rs +++ b/extendr-macros/src/wrappers.rs @@ -7,17 +7,11 @@ use proc_macro2::Ident; use quote::{format_ident, quote}; use syn::{parse_quote, punctuated::Punctuated, Expr, ExprLit, FnArg, ItemFn, Token, Type}; +use crate::extendr_options::ExtendrOptions; + pub const META_PREFIX: &str = "meta__"; pub const WRAP_PREFIX: &str = "wrap__"; -#[derive(Debug, Default)] -pub struct ExtendrOptions { - pub use_try_from: bool, - pub r_name: Option, - pub mod_name: Option, - pub use_rng: bool, -} - // Generate wrappers for a specific function. pub fn make_function_wrappers( opts: &ExtendrOptions, @@ -150,7 +144,7 @@ pub fn make_function_wrappers( }, } }; - + let return_type_conversion = if !return_is_ref_self { if opts.use_try_from { quote!(Ok(#call_name(#actual_args).try_into()?))