aorist_extendr_macros/lib.rs
1//!
2//! Macros for generating wrappers for rust functions.
3
4//
5// We can invoke the #[extendr] macro on functions or struct impls.
6//
7// eg.
8//
9// ```ignore
10// #[extendr]
11// fn hello() -> &'static str {
12// "hello"
13// }
14// ```
15//
16// These macros add additional functions which you can see using the
17// `cargo expand` extension.
18//
19// Invoking the #[extendr_module] macro generates an entrypoint for the
20// library that will be called by R. Note that we add a postfix
21// `_extendr` to the init function because we need to forward routine
22// registration from C to Rust, and the C function will be called
23// `R_init_hello()`.
24//
25// ```ignore
26// #[no_mangle]
27// #[allow(non_snake_case)]
28// pub extern "C" fn R_init_hello_extendr(info: *mut extendr_api::DllInfo) {
29// let mut call_methods = Vec::new();
30// init__hello(info, &mut call_methods);
31// unsafe { extendr_api::register_call_methods(info, call_methods.as_ref()) };
32// }
33// ```
34//
35// The module also generates the `init__` functions that provide metadata
36// to R to register the wrappers.
37//
38// ```ignore
39// #[allow(non_snake_case)]
40// fn init__hello(info: *mut extendr_api::DllInfo, call_methods: &mut Vec<extendr_api::CallMethod>) {
41// call_methods.push(extendr_api::CallMethod {
42// call_symbol: std::ffi::CString::new("wrap__hello").unwrap(),
43// func_ptr: wrap__hello as *const u8,
44// num_args: 0i32,
45// })
46// }
47// ```
48//
49// In the case of struct impls we also generate the following:
50//
51// * Wrappers and init functions for all methods.
52// * A single init function that calls the other init functions for the methods.
53// * An input conversion from an external pointer to a reference and a move of that type.
54// * An output converstion from that type to an owned external pointer object.
55// * A finalizer for that type to free memory allocated.
56
57#[allow(non_snake_case)]
58mod R;
59mod extendr_function;
60mod extendr_impl;
61mod extendr_module;
62mod pairlist;
63mod pairs;
64mod wrappers;
65
66use proc_macro::TokenStream;
67use quote::quote;
68use syn::{parse_macro_input, Item};
69
70#[proc_macro_attribute]
71pub fn extendr(attr: TokenStream, item: TokenStream) -> TokenStream {
72 let args = parse_macro_input!(attr as syn::AttributeArgs);
73 match parse_macro_input!(item as Item) {
74 Item::Fn(func) => extendr_function::extendr_function(args, func),
75 Item::Impl(item_impl) => extendr_impl::extendr_impl(item_impl),
76 other_item => TokenStream::from(quote! {#other_item}),
77 }
78}
79
80/// Define a module and export symbols to R
81/// Example:
82///```ignore
83/// extendr_module! {
84/// mod name;
85/// fn my_func1;
86/// fn my_func2;
87/// impl MyTrait;
88/// }
89/// ```
90/// Outputs:
91///
92/// ```ignore
93/// #[no_mangle]
94/// #[allow(non_snake_case)]
95/// pub extern "C" fn R_init_hello_extendr(info: *mut extendr_api::DllInfo) {
96/// let mut call_methods = Vec::new();
97/// init__hello(info, &mut call_methods);
98/// unsafe { extendr_api::register_call_methods(info, call_methods.as_ref()) };
99/// }
100/// ```
101#[proc_macro]
102pub fn extendr_module(item: TokenStream) -> TokenStream {
103 extendr_module::extendr_module(item)
104}
105
106/// Create a Pairlist R object from a list of name-value pairs.
107/// ```ignore
108/// assert_eq!(pairlist!(a=1, 2, 3), Pairlist::from_pairs(&[("a", 1), ("", 2), ("", 3)]));
109/// ```
110#[proc_macro]
111pub fn pairlist(item: TokenStream) -> TokenStream {
112 pairlist::pairlist(item)
113}
114
115/// Execute R code by parsing and evaluating tokens.
116///
117/// ```ignore
118/// R!("c(1, 2, 3)");
119/// R!("{{(0..3).collect_robj()}} + 1");
120/// R!(r#"
121/// print("hello")
122/// "#);
123/// ```
124#[proc_macro]
125#[allow(non_snake_case)]
126pub fn R(item: TokenStream) -> TokenStream {
127 R::R(item.into(), true).into()
128}
129
130/// Execute R code by parsing and evaluating tokens
131/// but without expanding parameters.
132///
133/// ```ignore
134/// // c.f. https://dplyr.tidyverse.org/articles/programming.html
135/// Rraw!(r#"
136/// var_summary <- function(data, var) {
137/// data %>%
138/// summarise(n = n(), min = min({{ var }}), max = max({{ var }}))
139/// }
140/// "#)
141/// ```
142#[proc_macro]
143#[allow(non_snake_case)]
144pub fn Rraw(item: TokenStream) -> TokenStream {
145 R::R(item.into(), false).into()
146}