extendr_macros/
dataframe.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, Data, DataStruct, DeriveInput};
4
5fn parse_struct(input: &DeriveInput, datastruct: &DataStruct) -> TokenStream {
6    let structname = &input.ident;
7    let mut a = Vec::new();
8    for f in &datastruct.fields {
9        a.push(f.ident.clone());
10    }
11    quote! {
12        impl extendr_api::wrapper::IntoDataFrameRow<#structname> for Vec<#structname>
13        {
14            fn into_dataframe(self) -> extendr_api::Result<extendr_api::wrapper::Dataframe<#structname>> {
15                #(let mut #a = Vec::with_capacity(self.len());)*
16                for val in self {
17                    #(#a.push(val.#a);)*
18                }
19                let caller = extendr_api::functions::eval_string("data.frame")?;
20                let res = caller.call(extendr_api::wrapper::Pairlist::from_pairs(&[
21                    #((stringify!(#a), extendr_api::robj::Robj::from(#a))),*
22                ]))?;
23                res.try_into()
24            }
25        }
26
27        impl<I> extendr_api::wrapper::IntoDataFrameRow<#structname> for (I,)
28        where
29            I : ExactSizeIterator<Item=#structname>,
30        {
31            /// Thanks to RFC 2451, we need to wrap a generic iterator in a tuple!
32            fn into_dataframe(self) -> extendr_api::Result<extendr_api::wrapper::Dataframe<#structname>> {
33                #(let mut #a = Vec::with_capacity(self.0.len());)*
34                for val in self.0 {
35                    #(#a.push(val.#a);)*
36                }
37                let caller = extendr_api::functions::eval_string("data.frame")?;
38                let res = caller.call(extendr_api::wrapper::Pairlist::from_pairs(&[
39                    #((stringify!(#a), extendr_api::robj::Robj::from(#a))),*
40                ]))?;
41                res.try_into()
42            }
43        }
44    }
45    .into()
46}
47
48pub fn derive_into_dataframe(item: TokenStream) -> TokenStream {
49    let input: DeriveInput = parse_macro_input!(item as DeriveInput);
50
51    match &input.data {
52        Data::Struct(datastruct) => parse_struct(&input, datastruct),
53        _ => quote!(compile_error("IntoDataFrameRow expected a struct.")).into(),
54    }
55}