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.