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}