1use proc_macro::TokenStream;
36use quote::quote;
37use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Path};
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 let root: Path = syn::parse_quote!(::rhai);
206
207 output.extend(function_def.generate(&root));
208 TokenStream::from(output)
209}
210
211#[deprecated(since = "1.18.0")]
219#[proc_macro]
220pub fn register_exported_fn(args: TokenStream) -> TokenStream {
221 match crate::register::parse_register_macro(args) {
222 Ok((engine_expr, export_name, rust_mod_path)) => {
223 let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
224 TokenStream::from(quote! {
225 #engine_expr.register_fn(#export_name, #gen_mod_path::dynamic_result_fn)
226 })
227 }
228 Err(e) => e.to_compile_error().into(),
229 }
230}
231
232#[deprecated(since = "1.18.0")]
240#[proc_macro]
241pub fn set_exported_fn(args: TokenStream) -> TokenStream {
242 let root: Path = syn::parse_quote!(::rhai);
244
245 match crate::register::parse_register_macro(args) {
246 Ok((module_expr, export_name, rust_mod_path)) => {
247 let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
248
249 let mut tokens = quote! {
250 let fx = #root::FuncRegistration::new(#export_name).with_namespace(#root::FnNamespace::Internal)
251 };
252 #[cfg(feature = "metadata")]
253 tokens.extend(quote! {
254 .with_params_info(#gen_mod_path::Token::PARAM_NAMES)
255 });
256 tokens.extend(quote! {
257 ;
258 #module_expr.set_fn_raw_with_options(fx, &#gen_mod_path::Token::param_types(), #gen_mod_path::Token().into());
259 });
260 tokens.into()
261 }
262 Err(e) => e.to_compile_error().into(),
263 }
264}
265
266#[deprecated(since = "1.18.0")]
274#[proc_macro]
275pub fn set_exported_global_fn(args: TokenStream) -> TokenStream {
276 let root: Path = syn::parse_quote!(::rhai);
278
279 match crate::register::parse_register_macro(args) {
280 Ok((module_expr, export_name, rust_mod_path)) => {
281 let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
282
283 let mut tokens = quote! {
284 let fx = #root::FuncRegistration::new(#export_name).with_namespace(#root::FnNamespace::Global)
285 };
286 #[cfg(feature = "metadata")]
287 tokens.extend(quote! {
288 .with_params_info(#gen_mod_path::Token::PARAM_NAMES)
289 });
290 tokens.extend(quote! {
291 ;
292 #module_expr.set_fn_raw_with_options(fx, &#gen_mod_path::Token::param_types(), #gen_mod_path::Token().into());
293 });
294 tokens.into()
295 }
296 Err(e) => e.to_compile_error().into(),
297 }
298}
299
300#[proc_macro_derive(CustomType, attributes(rhai_type,))]
315pub fn derive_custom_type(input: TokenStream) -> TokenStream {
316 let input = parse_macro_input!(input as DeriveInput);
317 let expanded = custom_type::derive_custom_type_impl(input);
318 expanded.into()
319}
320
321#[proc_macro_attribute]
328pub fn expose_under_internals(args: TokenStream, input: TokenStream) -> TokenStream {
329 let args: proc_macro2::TokenStream = args.into();
330 let input: proc_macro2::TokenStream = input.into();
331
332 if !args.is_empty() {
333 return syn::Error::new(
334 args.span(),
335 "`expose_under_internals` cannot have arguments.",
336 )
337 .to_compile_error()
338 .into();
339 }
340
341 if let Ok(mut item) = syn::parse2::<syn::ItemFn>(input.clone()) {
343 match item.vis {
344 syn::Visibility::Inherited => (),
345 _ => {
346 return syn::Error::new(
347 item.vis.span(),
348 "Function with `expose_under_internals` must not have any visibility.",
349 )
350 .to_compile_error()
351 .into();
352 }
353 }
354
355 item.vis = syn::parse2(quote! { pub }).unwrap();
356
357 let mut result = quote! {
358 #[cfg(feature = "internals")]
359 #item
360 };
361
362 item.vis = syn::parse2(quote! { pub(crate) }).unwrap();
363
364 result.extend(quote! {
365 #[cfg(not(feature = "internals"))]
366 #item
367 });
368
369 return result.into();
370 }
371
372 if let Ok(mut item) = syn::parse2::<syn::ItemType>(input.clone()) {
374 match item.vis {
375 syn::Visibility::Inherited => (),
376 _ => {
377 return syn::Error::new(
378 item.vis.span(),
379 "`type` definitions with `expose_under_internals` must not have any visibility.",
380 )
381 .to_compile_error()
382 .into();
383 }
384 }
385
386 item.vis = syn::parse2(quote! { pub }).unwrap();
387
388 let mut result = quote! {
389 #[cfg(feature = "internals")]
390 #item
391 };
392
393 item.vis = syn::parse2(quote! { pub(crate) }).unwrap();
394
395 result.extend(quote! {
396 #[cfg(not(feature = "internals"))]
397 #item
398 });
399
400 return result.into();
401 }
402
403 if let Ok(mut item) = syn::parse2::<syn::ItemUse>(input.clone()) {
405 match item.vis {
406 syn::Visibility::Inherited => (),
407 _ => {
408 return syn::Error::new(
409 item.vis.span(),
410 "`use` statements with `expose_under_internals` must not have any visibility.",
411 )
412 .to_compile_error()
413 .into();
414 }
415 }
416
417 item.vis = syn::parse2(quote! { pub }).unwrap();
418
419 let mut result = quote! {
420 #[cfg(feature = "internals")]
421 #item
422 };
423
424 item.vis = syn::parse2(quote! { pub(crate) }).unwrap();
425
426 result.extend(quote! {
427 #[cfg(not(feature = "internals"))]
428 #item
429 });
430
431 return result.into();
432 }
433
434 syn::Error::new(input.span(), "Cannot use `expose_under_internals` here.")
435 .to_compile_error()
436 .into()
437}