diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 6ee13ed8..88ff685d 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -2,12 +2,45 @@ use quote::{quote, quote_spanned}; use syn::{parse::Parse, parse::ParseStream, spanned::Spanned}; +#[derive(Debug, Default)] +pub(crate) struct ExportedFnParams { + name: Option, +} + +impl Parse for ExportedFnParams { + fn parse(args: ParseStream) -> syn::Result { + if args.is_empty() { + return Ok(ExportedFnParams::default()); + } + let assignment: syn::ExprAssign = args.parse()?; + + let attr_name: syn::Ident = match assignment.left.as_ref() { + syn::Expr::Path(syn::ExprPath { path: attr_path, .. }) => attr_path.get_ident().cloned() + .ok_or_else(|| syn::Error::new(attr_path.span(), "expecting attribute name"))?, + x => return Err(syn::Error::new(x.span(), "expecting attribute name")), + }; + if &attr_name != "name" { + return Err(syn::Error::new(attr_name.span(), format!("unknown attribute '{}'", &attr_name))); + } + + let attr_value: String = match assignment.right.as_ref() { + syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(string), .. }) => string.value(), + x => return Err(syn::Error::new(x.span(), "expecting string literal value")), + }; + + Ok(ExportedFnParams { + name: Some(attr_value), + }) + } +} + #[derive(Debug)] pub(crate) struct ExportedFn { entire_span: proc_macro2::Span, signature: syn::Signature, is_public: bool, mut_receiver: bool, + params: ExportedFnParams, } impl Parse for ExportedFn { @@ -111,6 +144,7 @@ impl Parse for ExportedFn { signature: fn_all.sig, is_public, mut_receiver, + params: ExportedFnParams::default(), }) } } @@ -148,11 +182,20 @@ impl ExportedFn { } } + pub fn generate_with_params(mut self, + mut params: ExportedFnParams) -> proc_macro2::TokenStream { + self.params = params; + self.generate() + } + pub fn generate(self) -> proc_macro2::TokenStream { - let name: syn::Ident = syn::Ident::new( - &format!("rhai_fn_{}", self.name().to_string()), - self.name().span(), - ); + let name_str = if let Some(ref name) = self.params.name { + name.clone() + } else { + self.name().to_string() + }; + let name: syn::Ident = syn::Ident::new(&format!("rhai_fn_{}", name_str), + self.name().span()); let impl_block = self.generate_impl("Token"); let callable_block = self.generate_callable("Token"); let input_types_block = self.generate_input_types("Token"); diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index af641be8..167bd0fb 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -76,12 +76,13 @@ mod rhai_module; #[proc_macro_attribute] pub fn export_fn( - _args: proc_macro::TokenStream, + args: proc_macro::TokenStream, input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let mut output = proc_macro2::TokenStream::from(input.clone()); + let parsed_params = parse_macro_input!(args as function::ExportedFnParams); let function_def = parse_macro_input!(input as function::ExportedFn); - output.extend(function_def.generate()); + output.extend(function_def.generate_with_params(parsed_params)); proc_macro::TokenStream::from(output) } diff --git a/codegen/tests/test_functions.rs b/codegen/tests/test_functions.rs index ee88be4a..24dba324 100644 --- a/codegen/tests/test_functions.rs +++ b/codegen/tests/test_functions.rs @@ -158,3 +158,35 @@ fn mut_opaque_ref_test() -> Result<(), Box> { ); Ok(()) } + +mod rename_fn { + use rhai::plugin::*; + use rhai::FLOAT; + + #[export_fn(name = "add_float")] + pub fn add(f1: FLOAT, f2: FLOAT) -> FLOAT { + f1 + f2 + } +} + +#[test] +fn rename_fn_test() -> Result<(), Box> { + let mut engine = Engine::new(); + engine.register_fn("get_mystic_number", || 42 as FLOAT); + let mut m = Module::new(); + rhai::register_exported_fn!(m, "add_two_floats", rename_fn::add_float); + let mut r = StaticModuleResolver::new(); + r.insert("Math::Advanced".to_string(), m); + engine.set_module_resolver(Some(r)); + + assert_eq!( + engine.eval::( + r#"import "Math::Advanced" as math; + let x = get_mystic_number(); + let y = math::add_two_floats(x, 1.0); + y"# + )?, + 43.0 + ); + Ok(()) +} diff --git a/codegen/ui_tests/export_fn_bad_attr.rs b/codegen/ui_tests/export_fn_bad_attr.rs new file mode 100644 index 00000000..7a268f01 --- /dev/null +++ b/codegen/ui_tests/export_fn_bad_attr.rs @@ -0,0 +1,24 @@ +use rhai::plugin::*; + +#[derive(Clone)] +struct Point { + x: f32, + y: f32, +} + +#[export_fn(unknown = true)] +pub fn test_fn(input: Point) -> bool { + input.x > input.y +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/export_fn_bad_attr.stderr b/codegen/ui_tests/export_fn_bad_attr.stderr new file mode 100644 index 00000000..f08dd188 --- /dev/null +++ b/codegen/ui_tests/export_fn_bad_attr.stderr @@ -0,0 +1,11 @@ +error: unknown attribute 'unknown' + --> $DIR/export_fn_bad_attr.rs:9:13 + | +9 | #[export_fn(unknown = true)] + | ^^^^^^^ + +error[E0425]: cannot find function `test_fn` in this scope + --> $DIR/export_fn_bad_attr.rs:19:8 + | +19 | if test_fn(n) { + | ^^^^^^^ not found in this scope diff --git a/codegen/ui_tests/export_fn_bad_value.rs b/codegen/ui_tests/export_fn_bad_value.rs new file mode 100644 index 00000000..f8044f9c --- /dev/null +++ b/codegen/ui_tests/export_fn_bad_value.rs @@ -0,0 +1,24 @@ +use rhai::plugin::*; + +#[derive(Clone)] +struct Point { + x: f32, + y: f32, +} + +#[export_fn(name = true)] +pub fn test_fn(input: Point) -> bool { + input.x > input.y +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/export_fn_bad_value.stderr b/codegen/ui_tests/export_fn_bad_value.stderr new file mode 100644 index 00000000..0db1969f --- /dev/null +++ b/codegen/ui_tests/export_fn_bad_value.stderr @@ -0,0 +1,11 @@ +error: expecting string literal value + --> $DIR/export_fn_bad_value.rs:9:20 + | +9 | #[export_fn(name = true)] + | ^^^^ + +error[E0425]: cannot find function `test_fn` in this scope + --> $DIR/export_fn_bad_value.rs:19:8 + | +19 | if test_fn(n) { + | ^^^^^^^ not found in this scope diff --git a/codegen/ui_tests/export_fn_junk_arg.rs b/codegen/ui_tests/export_fn_junk_arg.rs new file mode 100644 index 00000000..3abb9399 --- /dev/null +++ b/codegen/ui_tests/export_fn_junk_arg.rs @@ -0,0 +1,24 @@ +use rhai::plugin::*; + +#[derive(Clone)] +struct Point { + x: f32, + y: f32, +} + +#[export_fn("wheeeee")] +pub fn test_fn(input: Point) -> bool { + input.x > input.y +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/export_fn_junk_arg.stderr b/codegen/ui_tests/export_fn_junk_arg.stderr new file mode 100644 index 00000000..d6003354 --- /dev/null +++ b/codegen/ui_tests/export_fn_junk_arg.stderr @@ -0,0 +1,11 @@ +error: expected assignment expression + --> $DIR/export_fn_junk_arg.rs:9:13 + | +9 | #[export_fn("wheeeee")] + | ^^^^^^^^^ + +error[E0425]: cannot find function `test_fn` in this scope + --> $DIR/export_fn_junk_arg.rs:19:8 + | +19 | if test_fn(n) { + | ^^^^^^^ not found in this scope