1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DataStruct, DeriveInput};

fn parse_struct(input: &DeriveInput, datastruct: &DataStruct) -> TokenStream {
    let structname = &input.ident;
    let mut a = Vec::new();
    for f in &datastruct.fields {
        a.push(f.ident.clone());
    }
    quote! {
        impl IntoDataFrameRow<#structname> for Vec<#structname>
        {
            fn into_dataframe(self) -> Result<Dataframe<#structname>> {
                #(let mut #a = Vec::with_capacity(self.len());)*
                for val in self {
                    #(#a.push(val.#a);)*
                }
                let caller = eval_string("data.frame")?;
                let res = caller.call(Pairlist::from_pairs(&[
                    #((stringify!(#a), extendr_api::robj::Robj::from(#a))),*
                ]))?;
                res.try_into()
            }
        }

        impl<I> IntoDataFrameRow<#structname> for (I,)
        where
            I : ExactSizeIterator<Item=#structname>,
        {
            /// Thanks to RFC 2451, we need to wrap a generic iterator in a tuple!
            fn into_dataframe(self) -> Result<Dataframe<#structname>> {
                #(let mut #a = Vec::with_capacity(self.0.len());)*
                for val in self.0 {
                    #(#a.push(val.#a);)*
                }
                let caller = eval_string("data.frame")?;
                let res = caller.call(Pairlist::from_pairs(&[
                    #((stringify!(#a), extendr_api::robj::Robj::from(#a))),*
                ]))?;
                res.try_into()
            }
        }
    }
    .into()
}

pub fn derive_into_dataframe(item: TokenStream) -> TokenStream {
    let input: DeriveInput = parse_macro_input!(item as DeriveInput);

    match &input.data {
        Data::Struct(datastruct) => parse_struct(&input, datastruct),
        _ => quote!(compile_error("IntoDataFrameRow expected a struct.")).into(),
    }
}