diff --git a/RELEASES.md b/RELEASES.md index 18bb9cdd..62f11c8c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -8,13 +8,19 @@ This version adds the `switch` statement. It also allows exposing selected module functions (usually methods) to the global namespace. +Breaking changes +---------------- + +* `Module::set_fn`, `Module::set_raw_fn` and `Module::set_fn_XXX_mut` all take an additional parameter of `FnNamespace`. + New features ------------ * `switch` statement. * `Engine::register_module` to register a module as a sub-module in the global namespace. -* `Module::get_fn_namespace` and `Module::set_fn_namespace` can expose a module function to the global namespace. This is convenient when registering an API for a custom type. * `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace. +* `Module::set_fn_XXX_mut` can expose a module function to the global namespace. This is convenient when registering an API for a custom type. +* `Module::set_getter_fn`, `Module::set_setter_fn`, `Module::set_indexer_get_fn`, `Module::set_indexer_set_fn` all expose the function to the global namespace by default. This is convenient when registering an API for a custom type. * `#[rhai_fn(global)]` and `#[rhai_fn(internal)]` attributes to determine whether a function defined in a plugin module should be exposed to the global namespace. This is convenient when defining an API for a custom type. Enhancements diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md index 7f58b103..937f85e9 100644 --- a/doc/src/rust/modules/create.md +++ b/doc/src/rust/modules/create.md @@ -60,7 +60,7 @@ engine.register_module("calc", module); engine.eval::("calc::inc(41)")? == 42; // refer to the 'Calc' module ``` -`Module::set_fn_namespace` can expose functions (usually _methods_) in the module +`Module::set_fn_XXX_mut` can expose functions (usually _methods_) in the module to the _global_ namespace, so [getters/setters] and [indexers] for [custom types] can work as expected. Type iterators, because of their special nature, are always exposed to the _global_ namespace. @@ -69,13 +69,12 @@ Type iterators, because of their special nature, are always exposed to the _glob use rhai::{Engine, Module, FnNamespace}; let mut module = Module::new(); // new module -let hash = module.set_fn_1_mut("inc", // add new method + +// Expose method 'inc' to the global namespace (default is 'Internal') +module.set_fn_1_mut("inc", FnNamespace::Global, |x: &mut i64| Ok(x+1) ); -// Expose 'inc' to the global namespace (default is 'Internal') -module.set_fn_namespace(hash, FnNamespace::Global); - // Load the module into the Engine as a sub-module named 'calc' let mut engine = Engine::new(); engine.register_module("calc", module); diff --git a/src/module/mod.rs b/src/module/mod.rs index f14f191d..70d9208e 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -42,6 +42,12 @@ pub enum FnNamespace { Internal, } +impl Default for FnNamespace { + fn default() -> Self { + Self::Internal + } +} + impl FnNamespace { /// Is this namespace global? #[inline(always)] @@ -545,8 +551,7 @@ impl Module { /// use rhai::{Module, FnNamespace, FnAccess}; /// /// let mut module = Module::new(); - /// let hash = module.set_raw_fn("double_or_not", - /// FnNamespace::Internal, FnAccess::Public, + /// let hash = module.set_raw_fn("double_or_not", FnNamespace::Internal, FnAccess::Public, /// // Pass parameter types via a slice with TypeId's /// &[std::any::TypeId::of::(), std::any::TypeId::of::()], /// // Fixed closure signature @@ -594,30 +599,6 @@ impl Module { ) } - /// Get the namespace of a registered function. - /// Returns `None` if a function with the hash does not exist. - /// - /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. - #[inline(always)] - pub fn get_fn_namespace(&self, hash: u64) -> Option { - self.functions.get(&hash).map(|f| f.namespace) - } - - /// Set the namespace of a registered function. - /// Returns the original namespace or `None` if a function with the hash does not exist. - /// - /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. - #[inline] - pub fn set_fn_namespace(&mut self, hash: u64, namespace: FnNamespace) -> Option { - if let Some(f) = self.functions.get_mut(&hash) { - let old_ns = f.namespace; - f.namespace = namespace; - Some(old_ns) - } else { - None - } - } - /// Set a Rust function taking no parameters into the module, returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. @@ -687,16 +668,19 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; + /// use rhai::{Module, FnNamespace}; /// /// let mut module = Module::new(); - /// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) }); + /// let hash = module.set_fn_1_mut("calc", FnNamespace::Internal, + /// |x: &mut i64| { *x += 1; Ok(*x) } + /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_fn_1_mut( &mut self, name: impl Into, + namespace: FnNamespace, func: impl Fn(&mut A) -> Result> + SendSync + 'static, ) -> u64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { @@ -705,7 +689,7 @@ impl Module { let arg_types = [TypeId::of::()]; self.set_fn( name, - FnNamespace::Internal, + namespace, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -713,6 +697,7 @@ impl Module { } /// Set a Rust getter function taking one mutable parameter, returning a hash key. + /// This function is automatically exposed to the global namespace. /// /// If there is a similar existing Rust getter function, it is replaced. /// @@ -732,7 +717,11 @@ impl Module { name: impl Into, func: impl Fn(&mut A) -> Result> + SendSync + 'static, ) -> u64 { - self.set_fn_1_mut(crate::engine::make_getter(&name.into()), func) + self.set_fn_1_mut( + crate::engine::make_getter(&name.into()), + FnNamespace::Global, + func, + ) } /// Set a Rust function taking two parameters into the module, returning a hash key. @@ -780,18 +769,22 @@ impl Module { /// # Example /// /// ``` - /// use rhai::{Module, ImmutableString}; + /// use rhai::{Module, FnNamespace, ImmutableString}; /// /// let mut module = Module::new(); - /// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: ImmutableString| { - /// *x += y.len() as i64; Ok(*x) - /// }); + /// let hash = module.set_fn_2_mut("calc", FnNamespace::Internal, + /// |x: &mut i64, y: ImmutableString| { + /// *x += y.len() as i64; + /// Ok(*x) + /// } + /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_fn_2_mut( &mut self, name: impl Into, + namespace: FnNamespace, func: impl Fn(&mut A, B) -> Result> + SendSync + 'static, ) -> u64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { @@ -803,7 +796,7 @@ impl Module { let arg_types = [TypeId::of::(), TypeId::of::()]; self.set_fn( name, - FnNamespace::Internal, + namespace, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -812,6 +805,7 @@ impl Module { /// Set a Rust setter function taking two parameters (the first one mutable) into the module, /// returning a hash key. + /// This function is automatically exposed to the global namespace. /// /// If there is a similar existing setter Rust function, it is replaced. /// @@ -834,11 +828,16 @@ impl Module { name: impl Into, func: impl Fn(&mut A, B) -> Result<(), Box> + SendSync + 'static, ) -> u64 { - self.set_fn_2_mut(crate::engine::make_setter(&name.into()), func) + self.set_fn_2_mut( + crate::engine::make_setter(&name.into()), + FnNamespace::Global, + func, + ) } /// Set a Rust index getter taking two parameters (the first one mutable) into the module, /// returning a hash key. + /// This function is automatically exposed to the global namespace. /// /// If there is a similar existing setter Rust function, it is replaced. /// @@ -878,7 +877,7 @@ impl Module { panic!("Cannot register indexer for strings."); } - self.set_fn_2_mut(crate::engine::FN_IDX_GET, func) + self.set_fn_2_mut(crate::engine::FN_IDX_GET, FnNamespace::Global, func) } /// Set a Rust function taking three parameters into the module, returning a hash key. @@ -932,12 +931,15 @@ impl Module { /// # Example /// /// ``` - /// use rhai::{Module, ImmutableString}; + /// use rhai::{Module, FnNamespace, ImmutableString}; /// /// let mut module = Module::new(); - /// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: ImmutableString, z: i64| { - /// *x += y.len() as i64 + z; Ok(*x) - /// }); + /// let hash = module.set_fn_3_mut("calc", FnNamespace::Internal, + /// |x: &mut i64, y: ImmutableString, z: i64| { + /// *x += y.len() as i64 + z; + /// Ok(*x) + /// } + /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] @@ -949,6 +951,7 @@ impl Module { >( &mut self, name: impl Into, + namespace: FnNamespace, func: impl Fn(&mut A, B, C) -> Result> + SendSync + 'static, ) -> u64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { @@ -961,7 +964,7 @@ impl Module { let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( name, - FnNamespace::Internal, + namespace, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -970,6 +973,7 @@ impl Module { /// Set a Rust index setter taking three parameters (the first one mutable) into the module, /// returning a hash key. + /// This function is automatically exposed to the global namespace. /// /// If there is a similar existing Rust function, it is replaced. /// @@ -1126,12 +1130,15 @@ impl Module { /// # Example /// /// ``` - /// use rhai::{Module, ImmutableString}; + /// use rhai::{Module, FnNamespace, ImmutableString}; /// /// let mut module = Module::new(); - /// let hash = module.set_fn_4_mut("calc", |x: &mut i64, y: ImmutableString, z: i64, _w: ()| { - /// *x += y.len() as i64 + z; Ok(*x) - /// }); + /// let hash = module.set_fn_4_mut("calc", FnNamespace::Internal, + /// |x: &mut i64, y: ImmutableString, z: i64, _w: ()| { + /// *x += y.len() as i64 + z; + /// Ok(*x) + /// } + /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] @@ -1144,6 +1151,7 @@ impl Module { >( &mut self, name: impl Into, + namespace: FnNamespace, func: impl Fn(&mut A, B, C, D) -> Result> + SendSync + 'static, ) -> u64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { @@ -1162,7 +1170,7 @@ impl Module { ]; self.set_fn( name, - FnNamespace::Internal, + namespace, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), diff --git a/tests/modules.rs b/tests/modules.rs index de8e4de3..ef148bc9 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -22,9 +22,11 @@ fn test_module_sub_module() -> Result<(), Box> { let mut sub_module2 = Module::new(); sub_module2.set_var("answer", 41 as INT); - let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1)); - let hash_super_inc = sub_module2.set_fn_1_mut("super_inc", |x: &mut INT| Ok(*x + 1)); - sub_module2.set_fn_namespace(hash_super_inc, FnNamespace::Global); + let hash_inc = sub_module2.set_fn_1_mut("inc", FnNamespace::Internal, |x: &mut INT| Ok(*x + 1)); + sub_module2.set_fn_1_mut("super_inc", FnNamespace::Global, |x: &mut INT| Ok(*x + 1)); + + #[cfg(not(feature = "no_object"))] + sub_module2.set_getter_fn("doubled", |x: &mut INT| Ok(*x * 2)); sub_module.set_sub_module("universe", sub_module2); module.set_sub_module("life", sub_module); @@ -57,6 +59,11 @@ fn test_module_sub_module() -> Result<(), Box> { assert!(engine .eval::("inc(question::life::universe::answer)") .is_err()); + #[cfg(not(feature = "no_object"))] + assert_eq!( + engine.eval::("question::life::universe::answer.doubled")?, + 82 + ); assert_eq!( engine.eval::("super_inc(question::life::universe::answer)")?, 42 @@ -72,17 +79,16 @@ fn test_module_resolver() -> Result<(), Box> { let mut module = Module::new(); module.set_var("answer", 42 as INT); - module.set_fn_4("sum".to_string(), |x: INT, y: INT, z: INT, w: INT| { - Ok(x + y + z + w) - }); - module.set_fn_1_mut("double".to_string(), |x: &mut INT| { + module.set_fn_4("sum", |x: INT, y: INT, z: INT, w: INT| Ok(x + y + z + w)); + module.set_fn_1_mut("double", FnNamespace::Global, |x: &mut INT| { *x *= 2; Ok(()) }); #[cfg(not(feature = "no_float"))] module.set_fn_4_mut( - "sum_of_three_args".to_string(), + "sum_of_three_args", + FnNamespace::Internal, |target: &mut INT, a: INT, b: INT, c: rhai::FLOAT| { *target = a + b + c as INT; Ok(()) @@ -105,6 +111,15 @@ fn test_module_resolver() -> Result<(), Box> { 42 ); + assert!(engine + .eval::( + r#" + import "hello" as h; + sum(h::answer, -10, 3, 7) + "# + ) + .is_err()); + assert_eq!( engine.eval::( r#" @@ -141,6 +156,17 @@ fn test_module_resolver() -> Result<(), Box> { )?, 42 ); + assert_eq!( + engine.eval::( + r#" + import "hello" as h; + let x = 21; + double(x); + x + "# + )?, + 42 + ); #[cfg(not(feature = "no_float"))] { assert_eq!(