miniextendr_macros/
list_derive.rs1use proc_macro2::TokenStream;
28use quote::quote;
29use syn::{DeriveInput, Fields, parse_quote, spanned::Spanned};
30
31fn field_is_ignored(field: &syn::Field) -> syn::Result<bool> {
36 let mut ignored = false;
37
38 for attr in &field.attrs {
39 if !attr.path().is_ident("into_list") {
40 continue;
41 }
42
43 attr.parse_nested_meta(|meta| {
44 if meta.path.is_ident("ignore") {
45 ignored = true;
46 return Ok(());
47 }
48
49 Err(meta.error("unknown #[into_list(...)] option; supported: ignore"))
50 })?;
51 }
52
53 Ok(ignored)
54}
55
56pub fn derive_into_list(input: DeriveInput) -> syn::Result<TokenStream> {
68 let struct_data = match input.data {
69 syn::Data::Struct(data) => data,
70 _ => {
71 return Err(syn::Error::new(
72 input.ident.span(),
73 "IntoList can only be derived for structs",
74 ));
75 }
76 };
77
78 let name = &input.ident;
79 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
80
81 let mut bounds: Vec<syn::WherePredicate> = Vec::new();
82
83 let (destructure_pat, list_construction) = match &struct_data.fields {
84 Fields::Named(fields) => {
86 let mut names: Vec<String> = Vec::new();
87 let mut idents: Vec<syn::Ident> = Vec::new();
88
89 for f in fields.named.iter() {
90 let ident = f.ident.as_ref().unwrap().clone();
91 if field_is_ignored(f)? {
92 continue;
93 }
94 let ty = &f.ty;
95 bounds.push(parse_quote!(#ty: ::miniextendr_api::into_r::IntoR));
96 names.push(ident.to_string());
97 idents.push(ident);
98 }
99
100 let pat = if idents.is_empty() {
101 quote! { { .. } }
102 } else {
103 quote! { { #(#idents),*, .. } }
104 };
105 let construction = quote! {
110 unsafe {
112 let __scope = ::miniextendr_api::gc_protect::ProtectScope::new();
113 ::miniextendr_api::list::List::from_raw_pairs(vec![ #( (#names, __scope.protect_raw(#idents.into_sexp())) ),* ])
114 }
115 };
116 (pat, construction)
117 }
118
119 Fields::Unnamed(fields) => {
121 let mut pat_elems: Vec<proc_macro2::TokenStream> = Vec::new();
122 let mut value_idents: Vec<syn::Ident> = Vec::new();
123
124 for (idx, f) in fields.unnamed.iter().enumerate() {
125 if field_is_ignored(f)? {
126 pat_elems.push(quote! { _ });
127 continue;
128 }
129 let ident = syn::Ident::new(&format!("_field{idx}"), f.span());
130 let ty = &f.ty;
131 bounds.push(parse_quote!(#ty: ::miniextendr_api::into_r::IntoR));
132 pat_elems.push(quote! { #ident });
133 value_idents.push(ident);
134 }
135
136 let pat = quote! { ( #(#pat_elems),* ) };
137 let construction = quote! {
138 unsafe {
140 let __scope = ::miniextendr_api::gc_protect::ProtectScope::new();
141 ::miniextendr_api::list::List::from_raw_values(vec![ #( __scope.protect_raw(#value_idents.into_sexp()) ),* ])
142 }
143 };
144 (pat, construction)
145 }
146
147 Fields::Unit => {
149 let pat = quote! {};
150 let construction = quote! {
151 ::miniextendr_api::list::List::from_raw_values(vec![])
152 };
153 (pat, construction)
154 }
155 };
156
157 let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
159 where_token: <syn::Token![where]>::default(),
160 predicates: syn::punctuated::Punctuated::new(),
161 });
162 for b in bounds {
163 where_clause.predicates.push(b);
164 }
165
166 let expand = quote! {
167 impl #impl_generics ::miniextendr_api::list::IntoList for #name #ty_generics #where_clause {
168 fn into_list(self) -> ::miniextendr_api::list::List {
169 use ::miniextendr_api::into_r::IntoR;
170 let Self #destructure_pat = self;
171 #list_construction
172 }
173 }
174 };
175
176 Ok(expand)
177}
178
179pub fn derive_try_from_list(input: DeriveInput) -> syn::Result<TokenStream> {
191 let struct_data = match input.data {
192 syn::Data::Struct(data) => data,
193 _ => {
194 return Err(syn::Error::new(
195 input.ident.span(),
196 "TryFromList can only be derived for structs",
197 ));
198 }
199 };
200
201 let name = &input.ident;
202 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
203
204 let mut bounds: Vec<syn::WherePredicate> = Vec::new();
205
206 let from_list_body = match &struct_data.fields {
207 Fields::Named(fields) => {
209 let mut field_extractions: Vec<proc_macro2::TokenStream> = Vec::new();
210 let mut field_inits: Vec<proc_macro2::TokenStream> = Vec::new();
211
212 for f in fields.named.iter() {
213 let ident = f.ident.as_ref().unwrap().clone();
214 let ty = &f.ty;
215
216 if field_is_ignored(f)? {
217 bounds.push(parse_quote!(#ty: ::core::default::Default));
218 field_inits.push(quote! { #ident: ::core::default::Default::default() });
219 continue;
220 }
221
222 bounds.push(parse_quote!(#ty: ::miniextendr_api::from_r::TryFromSexp<Error = ::miniextendr_api::from_r::SexpError>));
223
224 let name_str = ident.to_string();
225 field_extractions.push(quote! {
226 let #ident: #ty = list.get_named(#name_str)
227 .ok_or_else(|| ::miniextendr_api::from_r::SexpError::MissingField(#name_str.into()))?;
228 });
229 field_inits.push(quote! { #ident });
230 }
231
232 quote! {
233 #(#field_extractions)*
234 Ok(Self { #(#field_inits),* })
235 }
236 }
237
238 Fields::Unnamed(fields) => {
240 let mut field_extractions: Vec<proc_macro2::TokenStream> = Vec::new();
241 let mut ctor_args: Vec<proc_macro2::TokenStream> = Vec::new();
242 let mut ignored_fields: Vec<bool> = Vec::with_capacity(fields.unnamed.len());
243 for f in fields.unnamed.iter() {
244 ignored_fields.push(field_is_ignored(f)?);
245 }
246 let input_fields: usize = ignored_fields.iter().filter(|&&b| !b).count();
247 let mut input_idx: usize = 0;
248
249 for (idx, f) in fields.unnamed.iter().enumerate() {
250 let ty = &f.ty;
251
252 if ignored_fields[idx] {
253 bounds.push(parse_quote!(#ty: ::core::default::Default));
254 ctor_args.push(quote! { ::core::default::Default::default() });
255 continue;
256 }
257
258 let ident = syn::Ident::new(&format!("_field{idx}"), f.span());
259 bounds.push(parse_quote!(#ty: ::miniextendr_api::from_r::TryFromSexp<Error = ::miniextendr_api::from_r::SexpError>));
260
261 let idx_isize = input_idx as isize;
262 field_extractions.push(quote! {
263 let #ident: #ty = list.get_index(#idx_isize)
264 .ok_or_else(|| ::miniextendr_api::from_r::SexpError::Length(
265 ::miniextendr_api::from_r::SexpLengthError {
266 expected: #input_fields,
267 actual: list.len() as usize,
268 }
269 ))?;
270 });
271 ctor_args.push(quote! { #ident });
272 input_idx += 1;
273 }
274
275 quote! {
276 #(#field_extractions)*
277 Ok(Self( #(#ctor_args),* ))
278 }
279 }
280
281 Fields::Unit => {
283 quote! { Ok(Self) }
284 }
285 };
286
287 let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
289 where_token: <syn::Token![where]>::default(),
290 predicates: syn::punctuated::Punctuated::new(),
291 });
292 for b in bounds {
293 where_clause.predicates.push(b);
294 }
295
296 let expand = quote! {
297 impl #impl_generics ::miniextendr_api::list::TryFromList for #name #ty_generics #where_clause {
298 type Error = ::miniextendr_api::from_r::SexpError;
299
300 fn try_from_list(list: ::miniextendr_api::list::List) -> Result<Self, Self::Error> {
301 #from_list_body
302 }
303 }
304 };
305
306 Ok(expand)
307}
308
309pub fn derive_prefer_list(input: DeriveInput) -> syn::Result<TokenStream> {
315 let name = &input.ident;
316 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
317
318 let expand = quote! {
319 impl #impl_generics ::miniextendr_api::markers::PrefersList for #name #ty_generics #where_clause {}
320
321 impl #impl_generics ::miniextendr_api::into_r::IntoR for #name #ty_generics #where_clause {
322 type Error = std::convert::Infallible;
323
324 #[inline]
325 fn try_into_sexp(self) -> Result<::miniextendr_api::ffi::SEXP, Self::Error> {
326 Ok(self.into_sexp())
327 }
328
329 #[inline]
330 unsafe fn try_into_sexp_unchecked(self) -> Result<::miniextendr_api::ffi::SEXP, Self::Error> {
331 self.try_into_sexp()
332 }
333
334 #[inline]
335 fn into_sexp(self) -> ::miniextendr_api::ffi::SEXP {
336 ::miniextendr_api::list::IntoList::into_list(self).into_sexp()
337 }
338
339 #[inline]
340 unsafe fn into_sexp_unchecked(self) -> ::miniextendr_api::ffi::SEXP {
341 ::miniextendr_api::list::IntoList::into_list(self).into_sexp()
342 }
343 }
344 };
345
346 Ok(expand)
347}
348
349pub fn derive_prefer_externalptr(input: DeriveInput) -> syn::Result<TokenStream> {
355 let name = &input.ident;
356 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
357
358 let expand = quote! {
359 impl #impl_generics ::miniextendr_api::markers::PrefersExternalPtr for #name #ty_generics #where_clause {}
360
361 impl #impl_generics ::miniextendr_api::into_r::IntoR for #name #ty_generics #where_clause {
362 type Error = std::convert::Infallible;
363
364 #[inline]
365 fn try_into_sexp(self) -> Result<::miniextendr_api::ffi::SEXP, Self::Error> {
366 Ok(self.into_sexp())
367 }
368
369 #[inline]
370 unsafe fn try_into_sexp_unchecked(self) -> Result<::miniextendr_api::ffi::SEXP, Self::Error> {
371 self.try_into_sexp()
372 }
373
374 #[inline]
375 fn into_sexp(self) -> ::miniextendr_api::ffi::SEXP {
376 ::miniextendr_api::externalptr::ExternalPtr::new(self).into_sexp()
377 }
378
379 #[inline]
380 unsafe fn into_sexp_unchecked(self) -> ::miniextendr_api::ffi::SEXP {
381 ::miniextendr_api::externalptr::ExternalPtr::new(self).into_sexp()
382 }
383 }
384 };
385
386 Ok(expand)
387}
388
389pub fn derive_prefer_data_frame(input: DeriveInput) -> syn::Result<TokenStream> {
395 let name = &input.ident;
396 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
397
398 let expand = quote! {
399 impl #impl_generics ::miniextendr_api::markers::PrefersDataFrame for #name #ty_generics #where_clause {}
400
401 impl #impl_generics ::miniextendr_api::into_r::IntoR for #name #ty_generics #where_clause {
402 type Error = std::convert::Infallible;
403
404 #[inline]
405 fn try_into_sexp(self) -> Result<::miniextendr_api::ffi::SEXP, Self::Error> {
406 Ok(self.into_sexp())
407 }
408
409 #[inline]
410 unsafe fn try_into_sexp_unchecked(self) -> Result<::miniextendr_api::ffi::SEXP, Self::Error> {
411 self.try_into_sexp()
412 }
413
414 #[inline]
415 fn into_sexp(self) -> ::miniextendr_api::ffi::SEXP {
416 ::miniextendr_api::convert::IntoDataFrame::into_data_frame(self).into_sexp()
417 }
418
419 #[inline]
420 unsafe fn into_sexp_unchecked(self) -> ::miniextendr_api::ffi::SEXP {
421 ::miniextendr_api::convert::IntoDataFrame::into_data_frame(self).into_sexp()
422 }
423 }
424 };
425
426 Ok(expand)
427}
428
429pub fn derive_prefer_rnative(input: DeriveInput) -> syn::Result<TokenStream> {
436 let name = &input.ident;
437 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
438
439 let expand = quote! {
440 impl #impl_generics ::miniextendr_api::markers::PrefersRNativeType for #name #ty_generics #where_clause {}
441
442 impl #impl_generics ::miniextendr_api::into_r::IntoR for #name #ty_generics #where_clause {
443 type Error = std::convert::Infallible;
444
445 #[inline]
446 fn try_into_sexp(self) -> Result<::miniextendr_api::ffi::SEXP, Self::Error> {
447 Ok(self.into_sexp())
448 }
449
450 #[inline]
451 unsafe fn try_into_sexp_unchecked(self) -> Result<::miniextendr_api::ffi::SEXP, Self::Error> {
452 self.try_into_sexp()
453 }
454
455 #[inline]
456 fn into_sexp(self) -> ::miniextendr_api::ffi::SEXP {
457 ::miniextendr_api::into_r::IntoR::into_sexp(
458 ::miniextendr_api::convert::AsRNative(self)
459 )
460 }
461
462 #[inline]
463 unsafe fn into_sexp_unchecked(self) -> ::miniextendr_api::ffi::SEXP {
464 ::miniextendr_api::into_r::IntoR::into_sexp_unchecked(
465 ::miniextendr_api::convert::AsRNative(self)
466 )
467 }
468 }
469 };
470
471 Ok(expand)
472}