Make API chainable.
This commit is contained in:
parent
2a8d63fd5f
commit
8449f8c55e
@ -22,6 +22,7 @@ Breaking changes
|
||||
* `EvalAltResult::ErrorMismatchOutputType` has an extra argument containing the name of the requested type.
|
||||
* `Engine::call_fn_dynamic` take an extra argument, allowing a `Dynamic` value to be bound to the `this` pointer.
|
||||
* Precedence of the `%` (modulo) operator is lowered to below `<<` ad `>>`. This is to handle the case of `x << 3 % 10`.
|
||||
* Many configuration/setting API's now returns `&mut Self` so that the calls can be chained. This should not affect most code.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
@ -17,12 +17,12 @@ use rhai::{Engine, RegisterFn};
|
||||
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Register a custom operator called 'foo' and give it
|
||||
// a precedence of 160 (i.e. between +|- and *|/)
|
||||
engine.register_custom_operator("foo", 160).unwrap();
|
||||
|
||||
// Register the implementation of the customer operator as a function
|
||||
engine.register_fn("foo", |x: i64, y: i64| (x * y) - (x + y));
|
||||
// Register a custom operator named 'foo' and give it a precedence of 160
|
||||
// (i.e. between +|- and *|/)
|
||||
// Also register the implementation of the customer operator as a function
|
||||
engine
|
||||
.register_custom_operator("foo", 160).unwrap()
|
||||
.register_fn("foo", |x: i64, y: i64| (x * y) - (x + y));
|
||||
|
||||
// The custom operator can be used in expressions
|
||||
let result = engine.eval_expression::<i64>("1 + 2 * 3 foo 4 - 5 / 6")?;
|
||||
@ -71,9 +71,9 @@ All custom operators must be _binary_ (i.e. they take two operands).
|
||||
_Unary_ custom operators are not supported.
|
||||
|
||||
```rust
|
||||
engine.register_custom_operator("foo", 160).unwrap();
|
||||
|
||||
engine.register_fn("foo", |x: i64| x * x);
|
||||
engine
|
||||
.register_custom_operator("foo", 160).unwrap()
|
||||
.register_fn("foo", |x: i64| x * x);
|
||||
|
||||
engine.eval::<i64>("1 + 2 * 3 foo 4 - 5 / 6")?; // error: function 'foo (i64, i64)' not found
|
||||
```
|
||||
|
@ -13,8 +13,9 @@ use rhai::Engine;
|
||||
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.disable_symbol("if"); // disable the 'if' keyword
|
||||
engine.disable_symbol("+="); // disable the '+=' operator
|
||||
engine
|
||||
.disable_symbol("if") // disable the 'if' keyword
|
||||
.disable_symbol("+="); // disable the '+=' operator
|
||||
|
||||
// The following all return parse errors.
|
||||
|
||||
|
@ -9,7 +9,7 @@ Support for custom types can be turned off via the [`no_object`] feature.
|
||||
|
||||
```rust
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
use rhai::RegisterFn;
|
||||
use rhai::RegisterFn; // remember 'RegisterFn' is needed
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TestStruct {
|
||||
@ -28,14 +28,14 @@ impl TestStruct {
|
||||
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.register_type::<TestStruct>();
|
||||
|
||||
engine.register_fn("update", TestStruct::update);
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
engine
|
||||
.register_type::<TestStruct>() // most API's can be chained up
|
||||
.register_fn("update", TestStruct::update)
|
||||
.register_fn("new_ts", TestStruct::new);
|
||||
|
||||
let result = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?;
|
||||
|
||||
println!("result: {}", result.field); // prints 42
|
||||
println!("result: {}", result.field); // prints 42
|
||||
```
|
||||
|
||||
Register a Custom Type
|
||||
@ -52,7 +52,7 @@ struct TestStruct {
|
||||
}
|
||||
|
||||
impl TestStruct {
|
||||
fn update(&mut self) { // methods take &mut as first parameter
|
||||
fn update(&mut self) { // methods take &mut as first parameter
|
||||
self.field += 41;
|
||||
}
|
||||
|
||||
@ -75,8 +75,9 @@ using one of the `Engine::register_XXX` API.
|
||||
Below, the `update` and `new` methods are registered using `Engine::register_fn`.
|
||||
|
||||
```rust
|
||||
engine.register_fn("update", TestStruct::update); // registers 'update(&mut TestStruct)'
|
||||
engine.register_fn("new_ts", TestStruct::new); // registers 'new()'
|
||||
engine
|
||||
.register_fn("update", TestStruct::update) // registers 'update(&mut TestStruct)'
|
||||
.register_fn("new_ts", TestStruct::new); // registers 'new()'
|
||||
```
|
||||
|
||||
***Note**: Rhai follows the convention that methods of custom types take a `&mut` first parameter
|
||||
@ -107,13 +108,13 @@ fn foo(ts: &mut TestStruct) -> i64 {
|
||||
ts.field
|
||||
}
|
||||
|
||||
engine.register_fn("foo", foo); // register a Rust native function
|
||||
engine.register_fn("foo", foo); // register a Rust native function
|
||||
|
||||
let result = engine.eval::<i64>(
|
||||
"let x = new_ts(); x.foo()" // 'foo' can be called like a method on 'x'
|
||||
"let x = new_ts(); x.foo()" // 'foo' can be called like a method on 'x'
|
||||
)?;
|
||||
|
||||
println!("result: {}", result); // prints 1
|
||||
println!("result: {}", result); // prints 1
|
||||
```
|
||||
|
||||
Under [`no_object`], however, the _method_ style of function calls
|
||||
@ -133,13 +134,17 @@ If `Engine::register_type_with_name` is used to register the custom type
|
||||
with a special "pretty-print" name, [`type_of()`] will return that name instead.
|
||||
|
||||
```rust
|
||||
engine.register_type::<TestStruct>();
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
engine
|
||||
.register_type::<TestStruct>()
|
||||
.register_fn("new_ts", TestStruct::new);
|
||||
|
||||
let x = new_ts();
|
||||
x.type_of() == "path::to::module::TestStruct";
|
||||
|
||||
engine.register_type_with_name::<TestStruct>("Hello");
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
engine
|
||||
.register_type_with_name::<TestStruct>("Hello")
|
||||
.register_fn("new_ts", TestStruct::new);
|
||||
|
||||
let x = new_ts();
|
||||
x.type_of() == "Hello";
|
||||
```
|
||||
|
@ -31,8 +31,9 @@ fn get_any_value() -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.register_fn("add", add_len);
|
||||
engine.register_fn("add_str", add_len_str);
|
||||
engine
|
||||
.register_fn("add", add_len)
|
||||
.register_fn("add_str", add_len_str);
|
||||
|
||||
let result = engine.eval::<i64>(r#"add(40, "xx")"#)?;
|
||||
|
||||
|
@ -19,9 +19,10 @@ fn show_it<T: Display>(x: &mut T) {
|
||||
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.register_fn("print", show_it::<i64>);
|
||||
engine.register_fn("print", show_it::<bool>);
|
||||
engine.register_fn("print", show_it::<ImmutableString>);
|
||||
engine
|
||||
.register_fn("print", show_it::<i64>)
|
||||
.register_fn("print", show_it::<bool>)
|
||||
.register_fn("print", show_it::<ImmutableString>);
|
||||
```
|
||||
|
||||
The above example shows how to register multiple functions
|
||||
|
@ -30,10 +30,10 @@ impl TestStruct {
|
||||
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.register_type::<TestStruct>();
|
||||
|
||||
engine.register_get_set("xyz", TestStruct::get_field, TestStruct::set_field);
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
engine
|
||||
.register_type::<TestStruct>()
|
||||
.register_get_set("xyz", TestStruct::get_field, TestStruct::set_field)
|
||||
.register_fn("new_ts", TestStruct::new);
|
||||
|
||||
// Return result can be 'String' - Rhai will automatically convert it from 'ImmutableString'
|
||||
let result = engine.eval::<String>(r#"let a = new_ts(); a.xyz = "42"; a.xyz"#)?;
|
||||
|
@ -38,8 +38,9 @@ engine.register_type::<TestStruct>();
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
|
||||
// Shorthand: engine.register_indexer_get_set(TestStruct::get_field, TestStruct::set_field);
|
||||
engine.register_indexer_get(TestStruct::get_field);
|
||||
engine.register_indexer_set(TestStruct::set_field);
|
||||
engine
|
||||
.register_indexer_get(TestStruct::get_field)
|
||||
.register_indexer_set(TestStruct::set_field);
|
||||
|
||||
let result = engine.eval::<i64>("let a = new_ts(); a[2] = 42; a[2]")?;
|
||||
|
||||
|
@ -15,3 +15,4 @@ A number of other configuration options are available from the `Engine` to fine-
|
||||
| `set_max_string_size` | [`unchecked`] | Set the maximum length (in UTF-8 bytes) for [strings]. See [maximum length of strings]. |
|
||||
| `set_max_array_size` | [`unchecked`], [`no_index`] | Set the maximum size for [arrays]. See [maximum size of arrays]. |
|
||||
| `set_max_map_size` | [`unchecked`], [`no_object`] | Set the maximum number of properties for [object maps]. See [maximum size of object maps]. |
|
||||
| `disable_symbol` | | Disable a certain keyword or operator. See [disable keywords and operators]. |
|
||||
|
@ -28,11 +28,11 @@ let mut scope = Scope::new();
|
||||
// Then push (i.e. add) some initialized variables into the state.
|
||||
// Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64.
|
||||
// Better stick to them or it gets hard working with the script.
|
||||
scope.push("y", 42_i64);
|
||||
scope.push("z", 999_i64);
|
||||
|
||||
// 'set_value' adds a variable when one doesn't exist
|
||||
scope.set_value("s", "hello, world!".to_string()); // remember to use 'String', not '&str'
|
||||
scope
|
||||
.push("y", 42_i64)
|
||||
.push("z", 999_i64)
|
||||
.set_value("s", "hello, world!".to_string()); //'set_value' adds a variable when one doesn't exist
|
||||
// remember to use 'String', not '&str'
|
||||
|
||||
// First invocation
|
||||
engine.eval_with_scope::<()>(&mut scope, r"
|
||||
|
@ -11,9 +11,10 @@ fn get_len1(s: String) -> i64 { s.len() as i64 } // <- Rhai will not
|
||||
fn get_len2(s: &str) -> i64 { s.len() as i64 } // <- Rhai finds this function fine
|
||||
fn get_len3(s: ImmutableString) -> i64 { s.len() as i64 } // <- the above is equivalent to this
|
||||
|
||||
engine.register_fn("len1", get_len1);
|
||||
engine.register_fn("len2", get_len2);
|
||||
engine.register_fn("len3", get_len3);
|
||||
engine
|
||||
.register_fn("len1", get_len1)
|
||||
.register_fn("len2", get_len2)
|
||||
.register_fn("len3", get_len3);
|
||||
|
||||
let len = engine.eval::<i64>("x.len1()")?; // error: function 'len1 (&str | ImmutableString)' not found
|
||||
let len = engine.eval::<i64>("x.len2()")?; // works fine
|
||||
|
73
src/api.rs
73
src/api.rs
@ -54,8 +54,9 @@ impl Engine {
|
||||
name: &str,
|
||||
arg_types: &[TypeId],
|
||||
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
|
||||
) {
|
||||
) -> &mut Self {
|
||||
self.global_module.set_raw_fn(name, arg_types, func);
|
||||
self
|
||||
}
|
||||
|
||||
/// Register a function of no parameters with the `Engine`.
|
||||
@ -68,8 +69,9 @@ impl Engine {
|
||||
&mut self,
|
||||
name: &str,
|
||||
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
|
||||
) {
|
||||
) -> &mut Self {
|
||||
self.global_module.set_raw_fn(name, &[], func);
|
||||
self
|
||||
}
|
||||
|
||||
/// Register a function of one parameter with the `Engine`.
|
||||
@ -92,9 +94,10 @@ impl Engine {
|
||||
&mut self,
|
||||
name: &str,
|
||||
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
|
||||
) {
|
||||
) -> &mut Self {
|
||||
self.global_module
|
||||
.set_raw_fn(name, &[TypeId::of::<A>()], func);
|
||||
self
|
||||
}
|
||||
|
||||
/// Register a function of two parameters with the `Engine`.
|
||||
@ -117,9 +120,10 @@ impl Engine {
|
||||
&mut self,
|
||||
name: &str,
|
||||
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
|
||||
) {
|
||||
) -> &mut Self {
|
||||
self.global_module
|
||||
.set_raw_fn(name, &[TypeId::of::<A>(), TypeId::of::<B>()], func);
|
||||
self
|
||||
}
|
||||
|
||||
/// Register a function of three parameters with the `Engine`.
|
||||
@ -147,12 +151,13 @@ impl Engine {
|
||||
&mut self,
|
||||
name: &str,
|
||||
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
|
||||
) {
|
||||
) -> &mut Self {
|
||||
self.global_module.set_raw_fn(
|
||||
name,
|
||||
&[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()],
|
||||
func,
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
/// Register a function of four parameters with the `Engine`.
|
||||
@ -181,7 +186,7 @@ impl Engine {
|
||||
&mut self,
|
||||
name: &str,
|
||||
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
|
||||
) {
|
||||
) -> &mut Self {
|
||||
self.global_module.set_raw_fn(
|
||||
name,
|
||||
&[
|
||||
@ -192,6 +197,7 @@ impl Engine {
|
||||
],
|
||||
func,
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
/// Register a custom type for use with the `Engine`.
|
||||
@ -231,8 +237,8 @@ impl Engine {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub fn register_type<T: Variant + Clone>(&mut self) {
|
||||
self.register_type_with_name::<T>(type_name::<T>());
|
||||
pub fn register_type<T: Variant + Clone>(&mut self) -> &mut Self {
|
||||
self.register_type_with_name::<T>(type_name::<T>())
|
||||
}
|
||||
|
||||
/// Register a custom type for use with the `Engine`, with a pretty-print name
|
||||
@ -279,7 +285,7 @@ impl Engine {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) {
|
||||
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) -> &mut Self {
|
||||
if self.type_names.is_none() {
|
||||
self.type_names = Some(Default::default());
|
||||
}
|
||||
@ -288,12 +294,14 @@ impl Engine {
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(type_name::<T>().to_string(), name.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Register an iterator adapter for a type with the `Engine`.
|
||||
/// This is an advanced feature.
|
||||
pub fn register_iterator<T: Variant + Clone>(&mut self, f: IteratorFn) {
|
||||
pub fn register_iterator<T: Variant + Clone>(&mut self, f: IteratorFn) -> &mut Self {
|
||||
self.global_module.set_iter(TypeId::of::<T>(), f);
|
||||
self
|
||||
}
|
||||
|
||||
/// Register a getter function for a member of a registered type with the `Engine`.
|
||||
@ -337,11 +345,12 @@ impl Engine {
|
||||
&mut self,
|
||||
name: &str,
|
||||
callback: impl Fn(&mut T) -> U + SendSync + 'static,
|
||||
) where
|
||||
) -> &mut Self
|
||||
where
|
||||
T: Variant + Clone,
|
||||
U: Variant + Clone,
|
||||
{
|
||||
self.register_fn(&make_getter(name), callback);
|
||||
self.register_fn(&make_getter(name), callback)
|
||||
}
|
||||
|
||||
/// Register a setter function for a member of a registered type with the `Engine`.
|
||||
@ -385,11 +394,12 @@ impl Engine {
|
||||
&mut self,
|
||||
name: &str,
|
||||
callback: impl Fn(&mut T, U) + SendSync + 'static,
|
||||
) where
|
||||
) -> &mut Self
|
||||
where
|
||||
T: Variant + Clone,
|
||||
U: Variant + Clone,
|
||||
{
|
||||
self.register_fn(&make_setter(name), callback);
|
||||
self.register_fn(&make_setter(name), callback)
|
||||
}
|
||||
|
||||
/// Shorthand for registering both getter and setter functions
|
||||
@ -436,12 +446,12 @@ impl Engine {
|
||||
name: &str,
|
||||
get_fn: impl Fn(&mut T) -> U + SendSync + 'static,
|
||||
set_fn: impl Fn(&mut T, U) + SendSync + 'static,
|
||||
) where
|
||||
) -> &mut Self
|
||||
where
|
||||
T: Variant + Clone,
|
||||
U: Variant + Clone,
|
||||
{
|
||||
self.register_get(name, get_fn);
|
||||
self.register_set(name, set_fn);
|
||||
self.register_get(name, get_fn).register_set(name, set_fn)
|
||||
}
|
||||
|
||||
/// Register an index getter for a registered type with the `Engine`.
|
||||
@ -485,12 +495,13 @@ impl Engine {
|
||||
pub fn register_indexer_get<T, X, U>(
|
||||
&mut self,
|
||||
callback: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
||||
) where
|
||||
) -> &mut Self
|
||||
where
|
||||
T: Variant + Clone,
|
||||
U: Variant + Clone,
|
||||
X: Variant + Clone,
|
||||
{
|
||||
self.register_fn(FN_IDX_GET, callback);
|
||||
self.register_fn(FN_IDX_GET, callback)
|
||||
}
|
||||
|
||||
/// Register an index setter for a registered type with the `Engine`.
|
||||
@ -533,12 +544,13 @@ impl Engine {
|
||||
pub fn register_indexer_set<T, X, U>(
|
||||
&mut self,
|
||||
callback: impl Fn(&mut T, X, U) -> () + SendSync + 'static,
|
||||
) where
|
||||
) -> &mut Self
|
||||
where
|
||||
T: Variant + Clone,
|
||||
U: Variant + Clone,
|
||||
X: Variant + Clone,
|
||||
{
|
||||
self.register_fn(FN_IDX_SET, callback);
|
||||
self.register_fn(FN_IDX_SET, callback)
|
||||
}
|
||||
|
||||
/// Shorthand for register both index getter and setter functions for a registered type with the `Engine`.
|
||||
@ -580,13 +592,14 @@ impl Engine {
|
||||
&mut self,
|
||||
getter: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
||||
setter: impl Fn(&mut T, X, U) -> () + SendSync + 'static,
|
||||
) where
|
||||
) -> &mut Self
|
||||
where
|
||||
T: Variant + Clone,
|
||||
U: Variant + Clone,
|
||||
X: Variant + Clone,
|
||||
{
|
||||
self.register_indexer_get(getter);
|
||||
self.register_indexer_set(setter);
|
||||
self.register_indexer_get(getter)
|
||||
.register_indexer_set(setter)
|
||||
}
|
||||
|
||||
/// Compile a string into an `AST`, which can be used later for evaluation.
|
||||
@ -1466,8 +1479,12 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn on_progress(&mut self, callback: impl Fn(&u64) -> bool + SendSync + 'static) {
|
||||
pub fn on_progress(
|
||||
&mut self,
|
||||
callback: impl Fn(&u64) -> bool + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.progress = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
|
||||
/// Override default action of `print` (print to stdout using `println!`)
|
||||
@ -1494,8 +1511,9 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn on_print(&mut self, callback: impl Fn(&str) + SendSync + 'static) {
|
||||
pub fn on_print(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self {
|
||||
self.print = Box::new(callback);
|
||||
self
|
||||
}
|
||||
|
||||
/// Override default action of `debug` (print to stdout using `println!`)
|
||||
@ -1522,7 +1540,8 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn on_debug(&mut self, callback: impl Fn(&str) + SendSync + 'static) {
|
||||
pub fn on_debug(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self {
|
||||
self.debug = Box::new(callback);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ pub trait RegisterFn<FN, ARGS, RET> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn register_fn(&mut self, name: &str, f: FN);
|
||||
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self;
|
||||
}
|
||||
|
||||
/// Trait to register fallible custom functions returning `Result<Dynamic, Box<EvalAltResult>>` with the `Engine`.
|
||||
@ -70,7 +70,7 @@ pub trait RegisterResultFn<FN, ARGS> {
|
||||
/// engine.eval::<i64>("div(42, 0)")
|
||||
/// .expect_err("expecting division by zero error!");
|
||||
/// ```
|
||||
fn register_result_fn(&mut self, name: &str, f: FN);
|
||||
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self;
|
||||
}
|
||||
|
||||
// These types are used to build a unique _marker_ tuple type for each combination
|
||||
@ -182,11 +182,12 @@ macro_rules! def_register {
|
||||
RET: Variant + Clone
|
||||
> RegisterFn<FN, ($($mark,)*), RET> for Engine
|
||||
{
|
||||
fn register_fn(&mut self, name: &str, f: FN) {
|
||||
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||
self.global_module.set_fn(name, FnAccess::Public,
|
||||
&[$(map_type_id::<$par>()),*],
|
||||
CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $clone),*))
|
||||
);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,11 +196,12 @@ macro_rules! def_register {
|
||||
FN: Fn($($param),*) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
||||
> RegisterResultFn<FN, ($($mark,)*)> for Engine
|
||||
{
|
||||
fn register_result_fn(&mut self, name: &str, f: FN) {
|
||||
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||
self.global_module.set_fn(name, FnAccess::Public,
|
||||
&[$(map_type_id::<$par>()),*],
|
||||
CallableFunction::$abi(make_func!(f : map_result ; $($par => $clone),*))
|
||||
);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,9 +214,10 @@ impl Module {
|
||||
/// module.set_var("answer", 42_i64);
|
||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||
/// ```
|
||||
pub fn set_var(&mut self, name: impl Into<String>, value: impl Variant + Clone) {
|
||||
pub fn set_var(&mut self, name: impl Into<String>, value: impl Variant + Clone) -> &mut Self {
|
||||
self.variables.insert(name.into(), Dynamic::from(value));
|
||||
self.indexed = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Get a mutable reference to a modules-qualified variable.
|
||||
@ -239,7 +240,7 @@ impl Module {
|
||||
///
|
||||
/// If there is an existing function of the same name and number of arguments, it is replaced.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub(crate) fn set_script_fn(&mut self, fn_def: ScriptFnDef) {
|
||||
pub(crate) fn set_script_fn(&mut self, fn_def: ScriptFnDef) -> &mut Self {
|
||||
// None + function name + number of arguments.
|
||||
let hash_script = calc_fn_hash(empty(), &fn_def.name, fn_def.params.len(), empty());
|
||||
self.functions.insert(
|
||||
@ -252,6 +253,7 @@ impl Module {
|
||||
),
|
||||
);
|
||||
self.indexed = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Does a sub-module exist in the module?
|
||||
@ -316,9 +318,10 @@ impl Module {
|
||||
/// module.set_sub_module("question", sub_module);
|
||||
/// assert!(module.get_sub_module("question").is_some());
|
||||
/// ```
|
||||
pub fn set_sub_module(&mut self, name: impl Into<String>, sub_module: Module) {
|
||||
pub fn set_sub_module(&mut self, name: impl Into<String>, sub_module: Module) -> &mut Self {
|
||||
self.modules.insert(name.into(), sub_module.into());
|
||||
self.indexed = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Does the particular Rust function exist in the module?
|
||||
@ -865,7 +868,7 @@ impl Module {
|
||||
}
|
||||
|
||||
/// Merge another module into this module.
|
||||
pub fn merge(&mut self, other: &Self) {
|
||||
pub fn merge(&mut self, other: &Self) -> &mut Self {
|
||||
self.merge_filtered(other, |_, _, _| true)
|
||||
}
|
||||
|
||||
@ -874,7 +877,7 @@ impl Module {
|
||||
&mut self,
|
||||
other: &Self,
|
||||
filter: impl Fn(FnAccess, &str, usize) -> bool,
|
||||
) {
|
||||
) -> &mut Self {
|
||||
self.variables
|
||||
.extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||
|
||||
@ -896,11 +899,15 @@ impl Module {
|
||||
self.all_functions.clear();
|
||||
self.all_variables.clear();
|
||||
self.indexed = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Filter out the functions, retaining only some based on a filter predicate.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub(crate) fn retain_functions(&mut self, filter: impl Fn(FnAccess, &str, usize) -> bool) {
|
||||
pub(crate) fn retain_functions(
|
||||
&mut self,
|
||||
filter: impl Fn(FnAccess, &str, usize) -> bool,
|
||||
) -> &mut Self {
|
||||
self.functions.retain(|_, (_, _, _, v)| match v {
|
||||
Func::Script(ref f) => filter(f.access, f.name.as_str(), f.params.len()),
|
||||
_ => true,
|
||||
@ -909,6 +916,7 @@ impl Module {
|
||||
self.all_functions.clear();
|
||||
self.all_variables.clear();
|
||||
self.indexed = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the number of variables in the module.
|
||||
@ -1071,9 +1079,10 @@ impl Module {
|
||||
}
|
||||
|
||||
/// Set a type iterator into the module.
|
||||
pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) {
|
||||
pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) -> &mut Self {
|
||||
self.type_iterators.insert(typ, func);
|
||||
self.indexed = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the specified type iterator.
|
||||
|
@ -732,7 +732,9 @@ pub fn optimize_into_ast(
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.for_each(|fn_def| lib2.set_script_fn(fn_def));
|
||||
.for_each(|fn_def| {
|
||||
lib2.set_script_fn(fn_def);
|
||||
});
|
||||
|
||||
functions
|
||||
.into_iter()
|
||||
@ -761,11 +763,13 @@ pub fn optimize_into_ast(
|
||||
};
|
||||
fn_def.into()
|
||||
})
|
||||
.for_each(|fn_def| module.set_script_fn(fn_def));
|
||||
.for_each(|fn_def| {
|
||||
module.set_script_fn(fn_def);
|
||||
});
|
||||
} else {
|
||||
functions
|
||||
.into_iter()
|
||||
.for_each(|fn_def| module.set_script_fn(fn_def));
|
||||
functions.into_iter().for_each(|fn_def| {
|
||||
module.set_script_fn(fn_def);
|
||||
});
|
||||
}
|
||||
|
||||
module
|
||||
|
50
src/scope.rs
50
src/scope.rs
@ -97,8 +97,9 @@ impl<'a> Scope<'a> {
|
||||
/// assert_eq!(my_scope.len(), 0);
|
||||
/// assert!(my_scope.is_empty());
|
||||
/// ```
|
||||
pub fn clear(&mut self) {
|
||||
pub fn clear(&mut self) -> &mut Self {
|
||||
self.0.clear();
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the number of entries inside the Scope.
|
||||
@ -147,8 +148,12 @@ impl<'a> Scope<'a> {
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||
/// ```
|
||||
pub fn push<K: Into<Cow<'a, str>>, T: Variant + Clone>(&mut self, name: K, value: T) {
|
||||
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value), false);
|
||||
pub fn push<K: Into<Cow<'a, str>>, T: Variant + Clone>(
|
||||
&mut self,
|
||||
name: K,
|
||||
value: T,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value), false)
|
||||
}
|
||||
|
||||
/// Add (push) a new `Dynamic` entry to the Scope.
|
||||
@ -163,8 +168,8 @@ impl<'a> Scope<'a> {
|
||||
/// my_scope.push_dynamic("x", Dynamic::from(42_i64));
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||
/// ```
|
||||
pub fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) {
|
||||
self.push_dynamic_value(name, EntryType::Normal, value, false);
|
||||
pub fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) -> &mut Self {
|
||||
self.push_dynamic_value(name, EntryType::Normal, value, false)
|
||||
}
|
||||
|
||||
/// Add (push) a new constant to the Scope.
|
||||
@ -185,8 +190,12 @@ impl<'a> Scope<'a> {
|
||||
/// my_scope.push_constant("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||
/// ```
|
||||
pub fn push_constant<K: Into<Cow<'a, str>>, T: Variant + Clone>(&mut self, name: K, value: T) {
|
||||
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value), true);
|
||||
pub fn push_constant<K: Into<Cow<'a, str>>, T: Variant + Clone>(
|
||||
&mut self,
|
||||
name: K,
|
||||
value: T,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value), true)
|
||||
}
|
||||
|
||||
/// Add (push) a new constant with a `Dynamic` value to the Scope.
|
||||
@ -208,8 +217,12 @@ impl<'a> Scope<'a> {
|
||||
/// my_scope.push_constant_dynamic("x", Dynamic::from(42_i64));
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||
/// ```
|
||||
pub fn push_constant_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) {
|
||||
self.push_dynamic_value(name, EntryType::Constant, value, true);
|
||||
pub fn push_constant_dynamic<K: Into<Cow<'a, str>>>(
|
||||
&mut self,
|
||||
name: K,
|
||||
value: Dynamic,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, EntryType::Constant, value, true)
|
||||
}
|
||||
|
||||
/// Add (push) a new entry with a `Dynamic` value to the Scope.
|
||||
@ -219,7 +232,7 @@ impl<'a> Scope<'a> {
|
||||
entry_type: EntryType,
|
||||
value: Dynamic,
|
||||
map_expr: bool,
|
||||
) {
|
||||
) -> &mut Self {
|
||||
let expr = if map_expr {
|
||||
map_dynamic_to_expr(value.clone(), Position::none()).map(Box::new)
|
||||
} else {
|
||||
@ -233,6 +246,8 @@ impl<'a> Scope<'a> {
|
||||
value: value.into(),
|
||||
expr,
|
||||
});
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Truncate (rewind) the Scope to a previous size.
|
||||
@ -261,8 +276,9 @@ impl<'a> Scope<'a> {
|
||||
/// assert_eq!(my_scope.len(), 0);
|
||||
/// assert!(my_scope.is_empty());
|
||||
/// ```
|
||||
pub fn rewind(&mut self, size: usize) {
|
||||
pub fn rewind(&mut self, size: usize) -> &mut Self {
|
||||
self.0.truncate(size);
|
||||
self
|
||||
}
|
||||
|
||||
/// Does the scope contain the entry?
|
||||
@ -341,14 +357,17 @@ impl<'a> Scope<'a> {
|
||||
/// my_scope.set_value("x", 0_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
|
||||
/// ```
|
||||
pub fn set_value<T: Variant + Clone>(&mut self, name: &'a str, value: T) {
|
||||
pub fn set_value<T: Variant + Clone>(&mut self, name: &'a str, value: T) -> &mut Self {
|
||||
match self.get_index(name) {
|
||||
None => self.push(name, value),
|
||||
None => {
|
||||
self.push(name, value);
|
||||
}
|
||||
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
|
||||
Some((index, EntryType::Normal)) => {
|
||||
self.0.get_mut(index).unwrap().value = Dynamic::from(value)
|
||||
self.0.get_mut(index).unwrap().value = Dynamic::from(value);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Get a mutable reference to an entry in the Scope.
|
||||
@ -358,9 +377,10 @@ impl<'a> Scope<'a> {
|
||||
}
|
||||
|
||||
/// Update the access type of an entry in the Scope.
|
||||
pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) {
|
||||
pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) -> &mut Self {
|
||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||
entry.alias = Some(Box::new(alias));
|
||||
self
|
||||
}
|
||||
|
||||
/// Get an iterator to entries in the Scope.
|
||||
|
@ -9,26 +9,19 @@ impl Engine {
|
||||
///
|
||||
/// When searching for functions, packages loaded later are preferred.
|
||||
/// In other words, loaded packages are searched in reverse order.
|
||||
pub fn load_package(&mut self, package: PackageLibrary) {
|
||||
// Push the package to the top - packages are searched in reverse order
|
||||
self.packages.push(package);
|
||||
}
|
||||
|
||||
/// Load a new package into the `Engine`.
|
||||
///
|
||||
/// When searching for functions, packages loaded later are preferred.
|
||||
/// In other words, loaded packages are searched in reverse order.
|
||||
pub fn load_packages(&mut self, package: PackageLibrary) {
|
||||
pub fn load_package(&mut self, package: PackageLibrary) -> &mut Self {
|
||||
// Push the package to the top - packages are searched in reverse order
|
||||
self.packages.push(package);
|
||||
self
|
||||
}
|
||||
|
||||
/// Control whether and how the `Engine` will optimize an AST after compilation.
|
||||
///
|
||||
/// Not available under the `no_optimize` feature.
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
pub fn set_optimization_level(&mut self, optimization_level: OptimizationLevel) {
|
||||
self.optimization_level = optimization_level
|
||||
pub fn set_optimization_level(&mut self, optimization_level: OptimizationLevel) -> &mut Self {
|
||||
self.optimization_level = optimization_level;
|
||||
self
|
||||
}
|
||||
|
||||
/// The current optimization level.
|
||||
@ -43,8 +36,9 @@ impl Engine {
|
||||
/// Set the maximum levels of function calls allowed for a script in order to avoid
|
||||
/// infinite recursion and stack overflows.
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
pub fn set_max_call_levels(&mut self, levels: usize) {
|
||||
self.max_call_stack_depth = levels
|
||||
pub fn set_max_call_levels(&mut self, levels: usize) -> &mut Self {
|
||||
self.max_call_stack_depth = levels;
|
||||
self
|
||||
}
|
||||
|
||||
/// The maximum levels of function calls allowed for a script.
|
||||
@ -56,12 +50,13 @@ impl Engine {
|
||||
/// Set the maximum number of operations allowed for a script to run to avoid
|
||||
/// consuming too much resources (0 for unlimited).
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
pub fn set_max_operations(&mut self, operations: u64) {
|
||||
pub fn set_max_operations(&mut self, operations: u64) -> &mut Self {
|
||||
self.max_operations = if operations == u64::MAX {
|
||||
0
|
||||
} else {
|
||||
operations
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
/// The maximum number of operations allowed for a script to run (0 for unlimited).
|
||||
@ -72,8 +67,9 @@ impl Engine {
|
||||
|
||||
/// Set the maximum number of imported modules allowed for a script.
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
pub fn set_max_modules(&mut self, modules: usize) {
|
||||
pub fn set_max_modules(&mut self, modules: usize) -> &mut Self {
|
||||
self.max_modules = modules;
|
||||
self
|
||||
}
|
||||
|
||||
/// The maximum number of imported modules allowed for a script.
|
||||
@ -84,7 +80,11 @@ impl Engine {
|
||||
|
||||
/// Set the depth limits for expressions (0 for unlimited).
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
pub fn set_max_expr_depths(&mut self, max_expr_depth: usize, max_function_expr_depth: usize) {
|
||||
pub fn set_max_expr_depths(
|
||||
&mut self,
|
||||
max_expr_depth: usize,
|
||||
max_function_expr_depth: usize,
|
||||
) -> &mut Self {
|
||||
self.max_expr_depth = if max_expr_depth == usize::MAX {
|
||||
0
|
||||
} else {
|
||||
@ -95,6 +95,7 @@ impl Engine {
|
||||
} else {
|
||||
max_function_expr_depth
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
/// The depth limit for expressions (0 for unlimited).
|
||||
@ -111,8 +112,9 @@ impl Engine {
|
||||
|
||||
/// Set the maximum length of strings (0 for unlimited).
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
pub fn set_max_string_size(&mut self, max_size: usize) {
|
||||
pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self {
|
||||
self.max_string_size = if max_size == usize::MAX { 0 } else { max_size };
|
||||
self
|
||||
}
|
||||
|
||||
/// The maximum length of strings (0 for unlimited).
|
||||
@ -124,8 +126,9 @@ impl Engine {
|
||||
/// Set the maximum length of arrays (0 for unlimited).
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub fn set_max_array_size(&mut self, max_size: usize) {
|
||||
pub fn set_max_array_size(&mut self, max_size: usize) -> &mut Self {
|
||||
self.max_array_size = if max_size == usize::MAX { 0 } else { max_size };
|
||||
self
|
||||
}
|
||||
|
||||
/// The maximum length of arrays (0 for unlimited).
|
||||
@ -138,8 +141,9 @@ impl Engine {
|
||||
/// Set the maximum length of object maps (0 for unlimited).
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub fn set_max_map_size(&mut self, max_size: usize) {
|
||||
pub fn set_max_map_size(&mut self, max_size: usize) -> &mut Self {
|
||||
self.max_map_size = if max_size == usize::MAX { 0 } else { max_size };
|
||||
self
|
||||
}
|
||||
|
||||
/// The maximum length of object maps (0 for unlimited).
|
||||
@ -153,8 +157,12 @@ impl Engine {
|
||||
///
|
||||
/// Not available under the `no_module` feature.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub fn set_module_resolver(&mut self, resolver: Option<impl ModuleResolver + 'static>) {
|
||||
pub fn set_module_resolver(
|
||||
&mut self,
|
||||
resolver: Option<impl ModuleResolver + 'static>,
|
||||
) -> &mut Self {
|
||||
self.module_resolver = resolver.map(|f| Box::new(f) as Box<dyn ModuleResolver>);
|
||||
self
|
||||
}
|
||||
|
||||
/// Disable a particular keyword or operator in the language.
|
||||
@ -194,7 +202,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn disable_symbol(&mut self, symbol: &str) {
|
||||
pub fn disable_symbol(&mut self, symbol: &str) -> &mut Self {
|
||||
if self.disabled_symbols.is_none() {
|
||||
self.disabled_symbols = Some(Default::default());
|
||||
}
|
||||
@ -203,6 +211,8 @@ impl Engine {
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(symbol.into());
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Register a custom operator into the language.
|
||||
@ -235,7 +245,7 @@ impl Engine {
|
||||
&mut self,
|
||||
keyword: &str,
|
||||
precedence: u8,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<&mut Self, String> {
|
||||
if !is_valid_identifier(keyword.chars()) {
|
||||
return Err(format!("not a valid identifier: '{}'", keyword).into());
|
||||
}
|
||||
@ -249,6 +259,6 @@ impl Engine {
|
||||
.unwrap()
|
||||
.insert(keyword.into(), precedence);
|
||||
|
||||
Ok(())
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ pub type FnCustomSyntaxEval = dyn Fn(
|
||||
&mut State,
|
||||
&Module,
|
||||
&mut Option<&mut Dynamic>,
|
||||
&[Expr],
|
||||
&[Expression],
|
||||
usize,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
@ -75,7 +75,7 @@ impl Engine {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ SendSync
|
||||
+ 'static,
|
||||
) -> Result<(), Box<LexError>> {
|
||||
) -> Result<self, Box<LexError>> {
|
||||
if value.is_empty() {
|
||||
return Err(Box::new(LexError::ImproperSymbol("".to_string())));
|
||||
}
|
||||
@ -145,6 +145,6 @@ impl Engine {
|
||||
.unwrap()
|
||||
.insert(key, syntax.into());
|
||||
|
||||
Ok(())
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +21,10 @@ fn test_method_call() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.register_type::<TestStruct>();
|
||||
|
||||
engine.register_fn("update", TestStruct::update);
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
engine
|
||||
.register_type::<TestStruct>()
|
||||
.register_fn("update", TestStruct::update)
|
||||
.register_fn("new_ts", TestStruct::new);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<TestStruct>("let x = new_ts(); x.update(1000); x")?,
|
||||
|
@ -25,8 +25,10 @@ fn test_mismatched_op_custom_type() {
|
||||
}
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.register_type_with_name::<TestStruct>("TestStruct");
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
|
||||
engine
|
||||
.register_type_with_name::<TestStruct>("TestStruct")
|
||||
.register_fn("new_ts", TestStruct::new);
|
||||
|
||||
assert!(matches!(
|
||||
*engine.eval::<INT>("60 + new_ts()").expect_err("should error"),
|
||||
|
@ -69,6 +69,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut resolver = StaticModuleResolver::new();
|
||||
|
||||
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)
|
||||
|
@ -3,16 +3,17 @@ use std::sync::{Arc, RwLock};
|
||||
|
||||
#[test]
|
||||
fn test_print() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
let logbook = Arc::new(RwLock::new(Vec::<String>::new()));
|
||||
|
||||
// Redirect print/debug output to 'log'
|
||||
let log = logbook.clone();
|
||||
engine.on_print(move |s| log.write().unwrap().push(format!("entry: {}", s)));
|
||||
let log1 = logbook.clone();
|
||||
let log2 = logbook.clone();
|
||||
|
||||
let log = logbook.clone();
|
||||
engine.on_debug(move |s| log.write().unwrap().push(format!("DEBUG: {}", s)));
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine
|
||||
.on_print(move |s| log1.write().unwrap().push(format!("entry: {}", s)))
|
||||
.on_debug(move |s| log2.write().unwrap().push(format!("DEBUG: {}", s)));
|
||||
|
||||
// Evaluate script
|
||||
engine.eval::<()>("print(40 + 2)")?;
|
||||
|
Loading…
Reference in New Issue
Block a user