1use proc_macro::TokenStream;
36use quote::quote;
37use syn::{parse_macro_input, spanned::Spanned, DeriveInput};
38
39mod attrs;
40mod custom_type;
41mod function;
42mod module;
43mod register;
44mod rhai_module;
45
46#[cfg(test)]
47mod test;
48
49#[proc_macro_attribute]
75pub fn export_module(args: TokenStream, input: TokenStream) -> TokenStream {
76 let parsed_params = match crate::attrs::outer_item_attributes(args.into(), "export_module") {
77 Ok(args) => args,
78 Err(err) => return err.to_compile_error().into(),
79 };
80 let mut module_def = parse_macro_input!(input as module::Module);
81 if let Err(e) = module_def.set_params(parsed_params) {
82 return e.to_compile_error().into();
83 }
84
85 let tokens = module_def.generate();
86 TokenStream::from(tokens)
87}
88
89#[proc_macro]
115pub fn exported_module(module_path: TokenStream) -> TokenStream {
116 let module_path = parse_macro_input!(module_path as syn::Path);
117 TokenStream::from(quote::quote! {
118 #module_path::rhai_module_generate()
119 })
120}
121
122#[proc_macro]
161pub fn combine_with_exported_module(args: TokenStream) -> TokenStream {
162 match crate::register::parse_register_macro(args) {
163 Ok((module_expr, _export_name, module_path)) => TokenStream::from(quote! {
164 #module_path::rhai_generate_into_module(#module_expr, true)
165 }),
166 Err(e) => e.to_compile_error().into(),
167 }
168}
169
170#[deprecated(since = "1.18.0")]
178#[proc_macro_attribute]
179pub fn export_fn(args: TokenStream, input: TokenStream) -> TokenStream {
180 let mut output = quote! {
181 #[allow(clippy::needless_pass_by_value)]
182 };
183 output.extend(proc_macro2::TokenStream::from(input.clone()));
184
185 let parsed_params = match crate::attrs::outer_item_attributes(args.into(), "export_fn") {
186 Ok(args) => args,
187 Err(err) => return err.to_compile_error().into(),
188 };
189 let mut function_def = parse_macro_input!(input as function::ExportedFn);
190
191 if !function_def.cfg_attrs().is_empty() {
192 return syn::Error::new(
193 function_def.cfg_attrs()[0].span(),
194 "`cfg` attributes are not allowed for `export_fn`",
195 )
196 .to_compile_error()
197 .into();
198 }
199
200 if let Err(e) = function_def.set_params(parsed_params) {
201 return e.to_compile_error().into();
202 }
203
204 output.extend(function_def.generate());
205 TokenStream::from(output)
206}
207
208#[deprecated(since = "1.18.0")]
216#[proc_macro]
217pub fn register_exported_fn(args: TokenStream) -> TokenStream {
218 match crate::register::parse_register_macro(args) {
219 Ok((engine_expr, export_name, rust_mod_path)) => {
220 let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
221 TokenStream::from(quote! {
222 #engine_expr.register_fn(#export_name, #gen_mod_path::dynamic_result_fn)
223 })
224 }
225 Err(e) => e.to_compile_error().into(),
226 }
227}
228
229#[deprecated(since = "1.18.0")]
237#[proc_macro]
238pub fn set_exported_fn(args: TokenStream) -> TokenStream {
239 match crate::register::parse_register_macro(args) {
240 Ok((module_expr, export_name, rust_mod_path)) => {
241 let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
242
243 let mut tokens = quote! {
244 let fx = FuncRegistration::new(#export_name).with_namespace(FnNamespace::Internal)
245 };
246 #[cfg(feature = "metadata")]
247 tokens.extend(quote! {
248 .with_params_info(#gen_mod_path::Token::PARAM_NAMES)
249 });
250 tokens.extend(quote! {
251 ;
252 #module_expr.set_fn_raw_with_options(fx, &#gen_mod_path::Token::param_types(), #gen_mod_path::Token().into());
253 });
254 tokens.into()
255 }
256 Err(e) => e.to_compile_error().into(),
257 }
258}
259
260#[deprecated(since = "1.18.0")]
268#[proc_macro]
269pub fn set_exported_global_fn(args: TokenStream) -> TokenStream {
270 match crate::register::parse_register_macro(args) {
271 Ok((module_expr, export_name, rust_mod_path)) => {
272 let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
273
274 let mut tokens = quote! {
275 let fx = FuncRegistration::new(#export_name).with_namespace(FnNamespace::Global)
276 };
277 #[cfg(feature = "metadata")]
278 tokens.extend(quote! {
279 .with_params_info(#gen_mod_path::Token::PARAM_NAMES)
280 });
281 tokens.extend(quote! {
282 ;
283 #module_expr.set_fn_raw_with_options(fx, &#gen_mod_path::Token::param_types(), #gen_mod_path::Token().into());
284 });
285 tokens.into()
286 }
287 Err(e) => e.to_compile_error().into(),
288 }
289}
290
291#[proc_macro_derive(CustomType, attributes(rhai_type,))]
306pub fn derive_custom_type(input: TokenStream) -> TokenStream {
307 let input = parse_macro_input!(input as DeriveInput);
308 let expanded = custom_type::derive_custom_type_impl(input);
309 expanded.into()
310}
311
312#[proc_macro_attribute]
319pub fn expose_under_internals(args: TokenStream, input: TokenStream) -> TokenStream {
320 let args: proc_macro2::TokenStream = args.into();
321 let input: proc_macro2::TokenStream = input.into();
322
323 if !args.is_empty() {
324 return syn::Error::new(
325 args.span(),
326 "`expose_under_internals` cannot have arguments.",
327 )
328 .to_compile_error()
329 .into();
330 }
331
332 if let Ok(mut item) = syn::parse2::<syn::ItemFn>(input.clone()) {
334 match item.vis {
335 syn::Visibility::Inherited => (),
336 _ => {
337 return syn::Error::new(
338 item.vis.span(),
339 "Function with `expose_under_internals` must not have any visibility.",
340 )
341 .to_compile_error()
342 .into();
343 }
344 }
345
346 item.vis = syn::parse2(quote! { pub }).unwrap();
347
348 let mut result = quote! {
349 #[cfg(feature = "internals")]
350 #item
351 };
352
353 item.vis = syn::parse2(quote! { pub(crate) }).unwrap();
354
355 result.extend(quote! {
356 #[cfg(not(feature = "internals"))]
357 #item
358 });
359
360 return result.into();
361 }
362
363 if let Ok(mut item) = syn::parse2::<syn::ItemType>(input.clone()) {
365 match item.vis {
366 syn::Visibility::Inherited => (),
367 _ => {
368 return syn::Error::new(
369 item.vis.span(),
370 "`type` definitions with `expose_under_internals` must not have any visibility.",
371 )
372 .to_compile_error()
373 .into();
374 }
375 }
376
377 item.vis = syn::parse2(quote! { pub }).unwrap();
378
379 let mut result = quote! {
380 #[cfg(feature = "internals")]
381 #item
382 };
383
384 item.vis = syn::parse2(quote! { pub(crate) }).unwrap();
385
386 result.extend(quote! {
387 #[cfg(not(feature = "internals"))]
388 #item
389 });
390
391 return result.into();
392 }
393
394 if let Ok(mut item) = syn::parse2::<syn::ItemUse>(input.clone()) {
396 match item.vis {
397 syn::Visibility::Inherited => (),
398 _ => {
399 return syn::Error::new(
400 item.vis.span(),
401 "`use` statements with `expose_under_internals` must not have any visibility.",
402 )
403 .to_compile_error()
404 .into();
405 }
406 }
407
408 item.vis = syn::parse2(quote! { pub }).unwrap();
409
410 let mut result = quote! {
411 #[cfg(feature = "internals")]
412 #item
413 };
414
415 item.vis = syn::parse2(quote! { pub(crate) }).unwrap();
416
417 result.extend(quote! {
418 #[cfg(not(feature = "internals"))]
419 #item
420 });
421
422 return result.into();
423 }
424
425 syn::Error::new(input.span(), "Cannot use `expose_under_internals` here.")
426 .to_compile_error()
427 .into()
428}