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