extendr_macros/
list_struct.rs

1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::quote;
4use syn::{Data, DeriveInput};
5
6/// Implementation of the TryFromRobj macro. Refer to the documentation there
7pub fn derive_try_from_robj(item: TokenStream) -> syn::parse::Result<TokenStream> {
8    // Parse the tokens into a Struct
9    let ast = syn::parse::<DeriveInput>(item)?;
10    let inside = if let Data::Struct(inner) = ast.data {
11        inner
12    } else {
13        return Err(syn::Error::new_spanned(ast, "Only struct is supported"));
14    };
15    let struct_name = ast.ident;
16
17    // Iterate each struct field and capture a conversion from Robj for each field
18    let mut tokens = Vec::<TokenStream2>::with_capacity(inside.fields.len());
19    for field in inside.fields {
20        let field_name = field.ident.as_ref().unwrap();
21        let field_str = field_name.to_string();
22        // This is like `value$foo` in R
23        tokens.push(quote!(
24            #field_name: value.dollar(#field_str)?.try_into()?
25        ));
26    }
27
28    // Emit the conversion trait impl
29    Ok(TokenStream::from(quote!(
30        impl std::convert::TryFrom<&extendr_api::Robj> for #struct_name {
31            type Error = extendr_api::Error;
32
33            fn try_from(value: &extendr_api::Robj) -> extendr_api::Result<Self> {
34                Ok(#struct_name {
35                    #(#tokens),*
36                })
37            }
38        }
39
40        impl std::convert::TryFrom<extendr_api::Robj> for #struct_name {
41            type Error = extendr_api::Error;
42
43            fn try_from(value: extendr_api::Robj) -> extendr_api::Result<Self> {
44                Ok(#struct_name {
45                    #(#tokens),*
46                })
47            }
48        }
49    )))
50}
51
52/// Implementation of the IntoRobj macro. Refer to the documentation there
53pub fn derive_into_robj(item: TokenStream) -> syn::parse::Result<TokenStream> {
54    // Parse the tokens into a Struct
55    let ast = syn::parse::<DeriveInput>(item)?;
56    let inside = if let Data::Struct(inner) = ast.data {
57        inner
58    } else {
59        return Err(syn::Error::new_spanned(ast, "Only struct is supported"));
60    };
61    let struct_name = ast.ident;
62
63    // Iterate each struct field and capture a token that creates a KeyValue pair (tuple) for
64    // each field
65    let mut tokens = Vec::<TokenStream2>::with_capacity(inside.fields.len());
66
67    for field in inside.fields {
68        let field_name = field.ident.as_ref().unwrap();
69        let field_str = field_name.to_string();
70        tokens.push(quote!(
71            (#field_str, (&value.#field_name).into())
72        ));
73    }
74
75    // The only thing we emit from this macro is the conversion trait impl
76    Ok(TokenStream::from(quote!(
77        impl std::convert::From<&#struct_name> for extendr_api::Robj {
78            fn from(value: &#struct_name) -> Self {
79                extendr_api::List::from_pairs([#(#tokens),*]).into()
80            }
81        }
82        impl std::convert::From<#struct_name> for extendr_api::Robj {
83            fn from(value: #struct_name) -> Self {
84                extendr_api::List::from_pairs([#(#tokens),*]).into()
85            }
86        }
87    )))
88}