Getters/setters and indexers in a plugin module are default global.
This commit is contained in:
parent
2e33f35f48
commit
65a4ceb3be
10
RELEASES.md
10
RELEASES.md
@ -4,6 +4,16 @@ Rhai Release Notes
|
||||
Version 0.19.7
|
||||
==============
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
* Fixes compilation errors with certain feature flag combinations.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* Property getters/setters and indexers defined in a plugin module are by default `#[rhai_fn(global)]`.
|
||||
|
||||
|
||||
Version 0.19.6
|
||||
==============
|
||||
|
@ -4,7 +4,8 @@ use quote::{quote, ToTokens};
|
||||
|
||||
use crate::attrs::ExportScope;
|
||||
use crate::function::{
|
||||
flatten_type_groups, print_type, ExportedFn, FnNamespaceAccess, FnSpecialAccess,
|
||||
flatten_type_groups, print_type, ExportedFn, FnNamespaceAccess, FnSpecialAccess, FN_GET,
|
||||
FN_IDX_GET, FN_IDX_SET, FN_SET,
|
||||
};
|
||||
use crate::module::Module;
|
||||
|
||||
@ -81,7 +82,6 @@ pub(crate) fn generate_body(
|
||||
function.name().span(),
|
||||
);
|
||||
let reg_names = function.exported_names();
|
||||
let mut namespace = FnNamespaceAccess::Internal;
|
||||
|
||||
let fn_input_names: Vec<String> = function
|
||||
.arg_list()
|
||||
@ -141,11 +141,27 @@ pub(crate) fn generate_body(
|
||||
.map(print_type)
|
||||
.unwrap_or_else(|| "()".to_string());
|
||||
|
||||
if let Some(ns) = function.params().namespace {
|
||||
namespace = ns;
|
||||
}
|
||||
|
||||
for fn_literal in reg_names {
|
||||
let mut namespace = FnNamespaceAccess::Internal;
|
||||
|
||||
match function.params().special {
|
||||
FnSpecialAccess::None => {}
|
||||
FnSpecialAccess::Index(_) | FnSpecialAccess::Property(_) => {
|
||||
let reg_name = fn_literal.value();
|
||||
if reg_name.starts_with(FN_GET)
|
||||
|| reg_name.starts_with(FN_SET)
|
||||
|| reg_name == FN_IDX_GET
|
||||
|| reg_name == FN_IDX_SET
|
||||
{
|
||||
namespace = FnNamespaceAccess::Global;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ns) = function.params().namespace {
|
||||
namespace = ns;
|
||||
}
|
||||
|
||||
let ns_str = syn::Ident::new(
|
||||
match namespace {
|
||||
FnNamespaceAccess::Global => "Global",
|
||||
|
@ -1519,7 +1519,7 @@ mod generate_tests {
|
||||
}
|
||||
#[allow(unused_mut)]
|
||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||
m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "u64"]),
|
||||
m.set_fn("get$square", FnNamespace::Global, FnAccess::Public, Some(&["x: &mut u64", "u64"]),
|
||||
&[core::any::TypeId::of::<u64>()],
|
||||
int_foo_token().into());
|
||||
if flatten {} else {}
|
||||
@ -1597,7 +1597,7 @@ mod generate_tests {
|
||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||
m.set_fn("square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "u64"]), &[core::any::TypeId::of::<u64>()],
|
||||
int_foo_token().into());
|
||||
m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "u64"]), &[core::any::TypeId::of::<u64>()],
|
||||
m.set_fn("get$square", FnNamespace::Global, FnAccess::Public, Some(&["x: &mut u64", "u64"]), &[core::any::TypeId::of::<u64>()],
|
||||
int_foo_token().into());
|
||||
if flatten {} else {}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ mod MyEnumModule {
|
||||
MyEnum::Baz(val1, val2)
|
||||
}
|
||||
// Access to fields
|
||||
#[rhai_fn(global, get = "enum_type")]
|
||||
#[rhai_fn(get = "enum_type")]
|
||||
pub fn get_type(a: &mut MyEnum) -> String {
|
||||
match a {
|
||||
MyEnum::Foo => "Foo".to_string(),
|
||||
@ -45,7 +45,7 @@ mod MyEnumModule {
|
||||
MyEnum::Baz(_, _) => "Baz".to_string()
|
||||
}
|
||||
}
|
||||
#[rhai_fn(global, get = "field_0")]
|
||||
#[rhai_fn(get = "field_0")]
|
||||
pub fn get_field_0(a: &mut MyEnum) -> Dynamic {
|
||||
match a {
|
||||
MyEnum::Foo => Dynamic::UNIT,
|
||||
@ -53,7 +53,7 @@ mod MyEnumModule {
|
||||
MyEnum::Baz(x, _) => Dynamic::from(x)
|
||||
}
|
||||
}
|
||||
#[rhai_fn(global, get = "field_1")]
|
||||
#[rhai_fn(get = "field_1")]
|
||||
pub fn get_field_1(a: &mut MyEnum) -> Dynamic {
|
||||
match a {
|
||||
MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT,
|
||||
|
@ -54,6 +54,33 @@ struct Handler {
|
||||
}
|
||||
```
|
||||
|
||||
### Register API for Any Custom Type
|
||||
|
||||
[Custom types] are often used to hold state. The easiest way to register an entire API is via a [plugin module].
|
||||
|
||||
```rust
|
||||
use rhai::plugin::*;
|
||||
|
||||
// A custom type to a hold state value.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
||||
pub struct SomeType {
|
||||
data: i64;
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
mod SomeTypeAPI {
|
||||
#[rhai_fn(global)]
|
||||
pub func1(obj: &mut SomeType) -> bool { ... }
|
||||
#[rhai_fn(global)]
|
||||
pub func2(obj: &mut SomeType) -> bool { ... }
|
||||
pub process(data: i64) -> i64 { ... }
|
||||
#[rhai_fn(get = "value")]
|
||||
pub get_value(obj: &mut SomeType) -> i64 { obj.data }
|
||||
#[rhai_fn(set = "value")]
|
||||
pub set_value(obj: &mut SomeType, value: i64) { obj.data = value; }
|
||||
}
|
||||
```
|
||||
|
||||
### Initialize Handler Object
|
||||
|
||||
Steps to initialize the event handler:
|
||||
@ -70,16 +97,10 @@ impl Handler {
|
||||
pub new(path: impl Into<PathBuf>) -> Self {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Register API functions here
|
||||
// Register custom types and API's
|
||||
engine
|
||||
.register_fn("func1", func1)
|
||||
.register_fn("func2", func2)
|
||||
.register_fn("func3", func3)
|
||||
.register_type_with_name::<SomeType>("SomeType")
|
||||
.register_get_set("value",
|
||||
|obj: &mut SomeType| obj.data,
|
||||
|obj: &mut SomeType, value: i64| obj.data = value
|
||||
);
|
||||
.load_package(exported_module!(SomeTypeAPI));
|
||||
|
||||
// Create a custom 'Scope' to hold state
|
||||
let mut scope = Scope::new();
|
||||
@ -112,36 +133,30 @@ Mapping an event from the system into a scripted handler is straight-forward:
|
||||
impl Handler {
|
||||
// Say there are three events: 'start', 'end', 'update'.
|
||||
// In a real application you'd be handling errors...
|
||||
pub fn on_event(&mut self, event_name: &str, event_data: i64) {
|
||||
pub fn on_event(&mut self, event_name: &str, event_data: i64) -> Result<(), Error> {
|
||||
match event_name {
|
||||
// The 'start' event maps to function 'start'.
|
||||
// In a real application you'd be handling errors...
|
||||
"start" =>
|
||||
self.engine
|
||||
.call_fn(&mut self.scope, &self.ast, "start", (event_data,)).unwrap(),
|
||||
"start" => self.engine.call_fn(&mut self.scope, &self.ast, "start", (event_data,))?,
|
||||
|
||||
// The 'end' event maps to function 'end'.
|
||||
// In a real application you'd be handling errors...
|
||||
"end" =>
|
||||
self.engine
|
||||
.call_fn(&mut self.scope, &self.ast, "end", (event_data,)).unwrap(),
|
||||
"end" => self.engine.call_fn(&mut self.scope, &self.ast, "end", (event_data,))?,
|
||||
|
||||
// The 'update' event maps to function 'update'.
|
||||
// This event provides a default implementation when the scripted function
|
||||
// is not found.
|
||||
"update" =>
|
||||
self.engine
|
||||
.call_fn(&mut self.scope, &self.ast, "update", (event_data,))
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "update" => {
|
||||
// Default implementation of 'update' event handler
|
||||
self.scope.set_value("state2", SomeType::new(42));
|
||||
// Turn function-not-found into a success
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
_ => Err(err.into())
|
||||
})
|
||||
.unwrap()
|
||||
"update" => self.engine
|
||||
.call_fn(&mut self.scope, &self.ast, "update", (event_data,))
|
||||
.or_else(|err| match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "update" => {
|
||||
// Default implementation of 'update' event handler
|
||||
self.scope.set_value("state2", SomeType::new(42));
|
||||
// Turn function-not-found into a success
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
_ => Err(err.into())
|
||||
})?
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -159,24 +174,25 @@ fn start(data) {
|
||||
if state1 {
|
||||
throw "Already started!";
|
||||
}
|
||||
if func1(state2) || func2() {
|
||||
if state2.func1() || state2.func2() {
|
||||
throw "Conditions not yet ready to start!";
|
||||
}
|
||||
state1 = true;
|
||||
state2.value = 0;
|
||||
state2.value = data;
|
||||
}
|
||||
|
||||
fn end(data) {
|
||||
if !state1 {
|
||||
throw "Not yet started!";
|
||||
}
|
||||
if func1(state2) || func2() {
|
||||
if state2.func1() || state2.func2() {
|
||||
throw "Conditions not yet ready to start!";
|
||||
}
|
||||
state1 = false;
|
||||
state2.value = data;
|
||||
}
|
||||
|
||||
fn update(data) {
|
||||
state2.value += func3(data);
|
||||
state2.value += process(data);
|
||||
}
|
||||
```
|
||||
|
@ -166,6 +166,9 @@ as well as all _type iterators_, are automatically exposed to the _global_ names
|
||||
[iteration]({{rootUrl}}/language/for.md), [getters/setters] and [indexers] for [custom types]
|
||||
can work as expected.
|
||||
|
||||
In fact, the default for all [getters/setters] and [indexers] defined in a plugin module
|
||||
is `#[rhai_fn(global)]` unless specifically overridden by `#[rhai_fn(internal)]`.
|
||||
|
||||
Therefore, in the example above, the `increment` method (defined with `#[rhai_fn(global)]`)
|
||||
works fine when called in method-call style:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user