extendr_macros/
extendr_module.rs1use 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 #( #fnmetanames(&mut functions); )*
50 #( #implmetanames(&mut impls); )*
51
52 #( functions.extend(#usenames::#usemetanames().functions); )*
54 #( impls.extend(#usenames::#usemetanames().impls); )*
55
56 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 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
144impl 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}