extendr_macros/
call.rs
1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse::ParseStream, punctuated::Punctuated, Token};
4use syn::{parse_macro_input, parse_quote, Expr, ExprAssign, ExprPath, LitStr};
5
6#[derive(Debug)]
7struct Call {
8 caller: LitStr,
9 pairs: Punctuated<Expr, Token![,]>,
10}
11
12impl syn::parse::Parse for Call {
14 fn parse(input: ParseStream) -> syn::Result<Self> {
15 let mut res = Self {
16 caller: input.parse::<LitStr>()?,
17 pairs: Punctuated::new(),
18 };
19
20 while !input.is_empty() {
21 input.parse::<Token![,]>()?;
22 res.pairs.push(input.parse::<Expr>()?);
23 }
24 Ok(res)
25 }
26}
27
28pub fn call(item: TokenStream) -> TokenStream {
29 let call = parse_macro_input!(item as Call);
33
34 let pairs = call
36 .pairs
37 .iter()
38 .map(|e| {
39 if let Expr::Assign(ExprAssign { left, right, .. }) = e {
40 if let Expr::Path(ExprPath { path, .. }) = &**left {
41 if let Some(ident) = path.get_ident() {
42 let s = ident.to_string();
43 return parse_quote!( (#s, extendr_api::Robj::from(#right)) );
44 }
45 }
46 }
47 parse_quote!( ("", extendr_api::Robj::from(#e)) )
48 })
49 .collect::<Vec<Expr>>();
50
51 let caller = &call.caller;
53 let caller = quote!(extendr_api::functions::eval_string(#caller));
54
55 let res = if pairs.is_empty() {
58 quote!(
59 (#caller).and_then(|caller| caller.call(extendr_api::wrapper::Pairlist::new()))
60 )
61 } else {
62 quote!(
63 (#caller).and_then(|caller| caller.call(extendr_api::wrapper::Pairlist::from_pairs(&[# ( #pairs ),*])))
64 )
65 };
66
67 TokenStream::from(res)
68}