extendr_macros/
extendr_module.rs

1use crate::wrappers;
2use proc_macro::TokenStream;
3use quote::{format_ident, quote};
4use syn::{parse::ParseStream, parse_macro_input, Ident, Token, Type};
5
6pub fn extendr_module(item: TokenStream) -> TokenStream {
7    let module = parse_macro_input!(item as Module);
8    let Module {
9        modname,
10        fnnames,
11        implnames,
12        usenames,
13    } = module;
14    let modname = modname.expect("cannot include unnamed modules");
15    let modname_string = modname.to_string();
16    let module_init_name = format_ident!("R_init_{}_extendr", modname);
17
18    let module_metadata_name = format_ident!("get_{}_metadata", modname);
19    let module_metadata_name_string = module_metadata_name.to_string();
20    let wrap_module_metadata_name =
21        format_ident!("{}get_{}_metadata", wrappers::WRAP_PREFIX, modname);
22    let wrap_module_metadata_name_str = wrap_module_metadata_name.to_string();
23
24    let make_module_wrappers_name = format_ident!("make_{}_wrappers", modname);
25    let make_module_wrappers_name_string = make_module_wrappers_name.to_string();
26    let wrap_make_module_wrappers =
27        format_ident!("{}make_{}_wrappers", wrappers::WRAP_PREFIX, modname);
28    let wrap_make_module_wrappers_string = wrap_make_module_wrappers.to_string();
29
30    let fnmetanames = fnnames
31        .iter()
32        .map(|id| format_ident!("{}{}", wrappers::META_PREFIX, id));
33    let implmetanames = implnames
34        .iter()
35        .map(|id| format_ident!("{}{}", wrappers::META_PREFIX, wrappers::type_name(id)));
36    let usemetanames = usenames
37        .iter()
38        .map(|id| format_ident!("get_{}_metadata", id))
39        .collect::<Vec<Ident>>();
40
41    TokenStream::from(quote! {
42        #[no_mangle]
43        #[allow(non_snake_case)]
44        pub fn #module_metadata_name() -> extendr_api::metadata::Metadata {
45            let mut functions = Vec::new();
46            let mut impls = Vec::new();
47
48            // Pushes metadata (eg. extendr_api::metadata::Func) to functions and impl vectors.
49            #( #fnmetanames(&mut functions); )*
50            #( #implmetanames(&mut impls); )*
51
52            // Extends functions and impls with the submodules metadata
53            #( functions.extend(#usenames::#usemetanames().functions); )*
54            #( impls.extend(#usenames::#usemetanames().impls); )*
55
56            // Add this function to the list, but set hidden: true.
57            functions.push(extendr_api::metadata::Func {
58                doc: "Metadata access function.",
59                rust_name: #module_metadata_name_string,
60                mod_name: #module_metadata_name_string,
61                r_name: #module_metadata_name_string,
62                c_name: #wrap_module_metadata_name_str,
63                args: Vec::new(),
64                return_type: "Metadata",
65                func_ptr: #wrap_module_metadata_name as * const u8,
66                hidden: true,
67                invisible: None,
68            });
69            let mut args = vec![
70                extendr_api::metadata::Arg { name: "use_symbols", arg_type: "bool", default: None },
71                extendr_api::metadata::Arg { name: "package_name", arg_type: "&str", default: None }
72            ];
73            let args = args;
74
75            // Add this function to the list, but set hidden: true.
76            functions.push(extendr_api::metadata::Func {
77                doc: "Wrapper generator.",
78                rust_name: #make_module_wrappers_name_string,
79                mod_name: #make_module_wrappers_name_string,
80                r_name: #make_module_wrappers_name_string,
81                c_name: #wrap_make_module_wrappers_string,
82                args,
83                return_type: "String",
84                func_ptr: #wrap_make_module_wrappers as * const u8,
85                hidden: true,
86                invisible: None,
87            });
88
89            extendr_api::metadata::Metadata {
90                name: #modname_string,
91                functions,
92                impls,
93            }
94        }
95
96        #[no_mangle]
97        #[allow(non_snake_case)]
98        pub extern "C" fn #wrap_module_metadata_name() -> extendr_api::SEXP {
99            use extendr_api::GetSexp;
100            unsafe { extendr_api::Robj::from(#module_metadata_name()).get() }
101        }
102
103        #[no_mangle]
104        #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)]
105        pub extern "C" fn #wrap_make_module_wrappers(
106            use_symbols_sexp: extendr_api::SEXP,
107            package_name_sexp: extendr_api::SEXP,
108        ) -> extendr_api::SEXP {
109            unsafe {
110                use extendr_api::robj::*;
111                use extendr_api::GetSexp;
112                let robj = Robj::from_sexp(use_symbols_sexp);
113                let use_symbols: bool = <bool>::try_from(&robj).unwrap();
114
115                let robj = Robj::from_sexp(package_name_sexp);
116                let package_name: &str = <&str>::try_from(&robj).unwrap();
117
118                extendr_api::Robj::from(
119                    #module_metadata_name()
120                        .make_r_wrappers(
121                            use_symbols,
122                            package_name,
123                        ).unwrap()
124                ).get()
125            }
126        }
127
128        #[no_mangle]
129        #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)]
130        pub extern "C" fn #module_init_name(info: * mut extendr_api::DllInfo) {
131            unsafe { extendr_api::register_call_methods(info, #module_metadata_name()) };
132        }
133    })
134}
135
136#[derive(Debug)]
137struct Module {
138    modname: Option<Ident>,
139    fnnames: Vec<Ident>,
140    implnames: Vec<Type>,
141    usenames: Vec<Ident>,
142}
143
144// Custom parser for the module.
145impl syn::parse::Parse for Module {
146    fn parse(input: ParseStream) -> syn::Result<Self> {
147        use syn::spanned::Spanned;
148        let mut res = Self {
149            modname: None,
150            fnnames: Vec::new(),
151            implnames: Vec::new(),
152            usenames: Vec::new(),
153        };
154        while !input.is_empty() {
155            if let Ok(kmod) = input.parse::<Token![mod]>() {
156                let name: Ident = input.parse()?;
157                if res.modname.is_some() {
158                    return Err(syn::Error::new(kmod.span(), "only one mod allowed"));
159                }
160                res.modname = Some(name);
161            } else if input.parse::<Token![fn]>().is_ok() {
162                res.fnnames.push(input.parse()?);
163            } else if input.parse::<Token![impl]>().is_ok() {
164                res.implnames.push(input.parse()?);
165            } else if input.parse::<Token![use]>().is_ok() {
166                res.usenames.push(input.parse()?);
167            } else {
168                return Err(syn::Error::new(input.span(), "expected mod, fn or impl"));
169            }
170
171            input.parse::<Token![;]>()?;
172        }
173        if res.modname.is_none() {
174            return Err(syn::Error::new(input.span(), "expected one 'mod name'"));
175        }
176        Ok(res)
177    }
178}