extendr_macros/
extendr_impl.rs

1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{ItemFn, ItemImpl};
4
5use crate::extendr_options::ExtendrOptions;
6use crate::wrappers;
7
8/// Make inherent implementations available to R
9///
10/// The `extendr_impl` function is used to make inherent implementations
11/// available to R as an environment. By adding the [`macro@extendr`] attribute
12/// macro to an `impl` block (supported with `enum`s and `struct`s), the
13/// methods in the impl block are made available as functions in an
14/// environment.
15///
16///
17/// On the R side, an environment with the same name of the inherent
18/// implementation is created. The environment has functions within it
19/// that correspond to each method in the impl block. Note that in order
20/// for an impl block to be compatible with extendr (and thus R), its return
21/// type must be able to be returned to R. For example, any struct that might
22/// be returned must _also_ have an `#[extendr]` annotated impl block.
23///
24/// Example:
25/// ```dont_run
26/// use extendr_api::prelude::*;
27///
28/// // a struct that will be used internal the People struct
29/// #[derive(Clone, Debug, IntoDataFrameRow)]
30/// struct Person {
31///     name: String,
32///     age: i32,
33/// }
34///
35/// // This will collect people in the struct
36/// #[extendr]
37/// #[derive(Clone, Debug)]
38/// struct People(Vec<Person>);
39///
40/// #[extendr]
41/// /// @export
42/// impl People {
43///     // instantiate a new struct with an empty vector
44///     fn new() -> Self {
45///         let vec: Vec<Person> = Vec::new();
46///         Self(vec)
47///     }
48///
49///     // add a person to the internal vector
50///     fn add_person(&mut self, name: &str, age: i32) -> &mut Self {
51///         let person = Person {
52///             name: String::from(name),
53///             age: age,
54///         };
55///
56///         self.0.push(person);
57///
58///         // return self
59///         self
60///     }
61///
62///     // Convert the struct into a data.frame
63///     fn into_df(&self) -> Robj {
64///         let df = self.0.clone().into_dataframe();
65///
66///         match df {
67///             Ok(df) => df.as_robj().clone(),
68///             Err(_) => data_frame!(),
69///         }
70///     }
71///
72///     // add another `People` struct to self
73///     fn add_people(&mut self, others: &People) -> &mut Self {
74///         self.0.extend(others.0.clone().into_iter());
75///         self
76///     }
77///
78///     // create a function to print the self which can be called
79///     // from an R print method
80///     fn print_self(&self) -> String {
81///         format!("{:?}", self.0)
82///     }
83/// }
84///
85/// // Macro to generate exports.
86/// // This ensures exported functions are registered with R.
87/// // See corresponding C code in `entrypoint.c`.
88/// extendr_module! {
89///     mod testself;
90///     impl People;
91/// }
92/// ```
93pub(crate) fn extendr_impl(
94    mut item_impl: ItemImpl,
95    opts: &ExtendrOptions,
96) -> syn::Result<TokenStream> {
97    // Only `impl name { }` allowed
98    if item_impl.defaultness.is_some() {
99        return Err(syn::Error::new_spanned(
100            item_impl,
101            "default not allowed in #[extendr] impl",
102        ));
103    }
104
105    if item_impl.unsafety.is_some() {
106        return Err(syn::Error::new_spanned(
107            item_impl,
108            "unsafe not allowed in #[extendr] impl",
109        ));
110    }
111
112    if item_impl.generics.const_params().count() != 0 {
113        return Err(syn::Error::new_spanned(
114            item_impl,
115            "const params not allowed in #[extendr] impl",
116        ));
117    }
118
119    if item_impl.generics.type_params().count() != 0 {
120        return Err(syn::Error::new_spanned(
121            item_impl,
122            "type params not allowed in #[extendr] impl",
123        ));
124    }
125
126    // if item_impl.generics.lifetimes().count() != 0 {
127    //     return quote! { compile_error!("lifetime params not allowed in #[extendr] impl"); }.into();
128    // }
129
130    if item_impl.generics.where_clause.is_some() {
131        return Err(syn::Error::new_spanned(
132            item_impl,
133            "where clause not allowed in #[extendr] impl",
134        ));
135    }
136
137    let self_ty = item_impl.self_ty.as_ref();
138    let self_ty_name = wrappers::type_name(self_ty);
139    let prefix = format!("{}__", self_ty_name);
140    let mut method_meta_names = Vec::new();
141    let doc_string = wrappers::get_doc_string(&item_impl.attrs);
142
143    // Generate wrappers for methods.
144    // eg.
145    // ```
146    // #[no_mangle]
147    // #[allow(non_snake_case)]
148    // pub extern "C" fn wrap__Person__new() -> extendr_api::SEXP {
149    //     unsafe {
150    //         use extendr_api::FromRobj;
151    //         extendr_api::Robj::from(<Person>::new()).get()
152    //     }
153    // }
154    // ```
155    let mut wrappers: Vec<ItemFn> = Vec::new();
156    for impl_item in &mut item_impl.items {
157        if let syn::ImplItem::Fn(ref mut method) = impl_item {
158            method_meta_names.push(format_ident!(
159                "{}{}__{}",
160                wrappers::META_PREFIX,
161                self_ty_name,
162                method.sig.ident
163            ));
164            wrappers::make_function_wrappers(
165                opts,
166                &mut wrappers,
167                prefix.as_str(),
168                &method.attrs,
169                &mut method.sig,
170                Some(self_ty),
171            )?;
172        }
173    }
174
175    let meta_name = format_ident!("{}{}", wrappers::META_PREFIX, self_ty_name);
176
177    let expanded = TokenStream::from(quote! {
178        // The impl itself copied from the source.
179        #item_impl
180
181        // Function wrappers
182        #( #wrappers )*
183
184        #[allow(non_snake_case)]
185        fn #meta_name(impls: &mut Vec<extendr_api::metadata::Impl>) {
186            let mut methods = Vec::new();
187            #( #method_meta_names(&mut methods); )*
188            impls.push(extendr_api::metadata::Impl {
189                doc: #doc_string,
190                name: #self_ty_name,
191                methods,
192            });
193        }
194    });
195
196    //eprintln!("{}", expanded);
197    Ok(expanded)
198}
199
200// This structure contains parameters parsed from the #[extendr_module] definition.