Update docs regarding modules.

This commit is contained in:
Stephen Chung
2020-07-08 09:48:25 +08:00
parent 236ba40784
commit 150f02d8b7
15 changed files with 134 additions and 59 deletions

View File

@@ -136,10 +136,10 @@ with a special "pretty-print" name, [`type_of()`] will return that name instead.
engine.register_type::<TestStruct>();
engine.register_fn("new_ts", TestStruct::new);
let x = new_ts();
print(x.type_of()); // prints "path::to::module::TestStruct"
x.type_of() == "path::to::module::TestStruct";
engine.register_type_with_name::<TestStruct>("Hello");
engine.register_fn("new_ts", TestStruct::new);
let x = new_ts();
print(x.type_of()); // prints "Hello"
x.type_of() == "Hello";
```

View File

@@ -0,0 +1,56 @@
Implement a Custom Module Resolver
=================================
{{#include ../../links.md}}
For many applications in which Rhai is embedded, it is necessary to customize the way that modules
are resolved. For instance, modules may need to be loaded from script texts stored in a database,
not in the file system.
A module resolver must implement the trait [`rhai::ModuleResolver`][traits],
which contains only one function: `resolve`.
When Rhai prepares to load a module, `ModuleResolver::resolve` is called with the name
of the _module path_ (i.e. the path specified in the [`import`] statement). Upon success, it should
return a [`Module`]; if the module cannot be load, return `EvalAltResult::ErrorModuleNotFound`.
Example
-------
```rust
use rhai::{ModuleResolver, Module, Engine, EvalAltResult};
// Define a custom module resolver.
struct MyModuleResolver {}
// Implement the 'ModuleResolver' trait.
impl ModuleResolver for MyModuleResolver {
// Only required function.
fn resolve(
&self,
engine: &Engine, // reference to the current 'Engine'
path: &str, // the module path
pos: Position, // location of the 'import' statement
) -> Result<Module, Box<EvalAltResult>> {
// Check module path.
if is_valid_module_path(path) {
// Load the custom module.
let module: Module = load_secret_module(path);
Ok(module)
} else {
Err(Box::new(EvalAltResult::ErrorModuleNotFound(path.into(), pos)))
}
}
}
let mut engine = Engine::new();
// Set the custom module resolver into the 'Engine'.
engine.set_module_resolver(Some(MyModuleResolver {}));
engine.consume(r#"
import "hello" as foo; // this 'import' statement will call
// 'MyModuleResolver::resolve' with "hello" as path
foo:bar();
"#)?;
```

View File

@@ -0,0 +1,45 @@
Create a Module from Rust
========================
{{#include ../../links.md}}
Manually creating a [`Module`] is possible via the `Module` API.
For the complete `Module` API, refer to the [documentation](https://docs.rs/rhai/{{version}}/rhai/struct.Module.html) online.
Make the Module Available to the Engine
--------------------------------------
In order to _use_ a custom module, there must be a [module resolver].
The easiest way is to use, for example, the [`StaticModuleResolver`][module resolver] to hold such
a custom module.
```rust
use rhai::{Engine, Scope, Module, i64};
use rhai::module_resolvers::StaticModuleResolver;
let mut engine = Engine::new();
let mut scope = Scope::new();
let mut module = Module::new(); // new module
module.set_var("answer", 41_i64); // variable 'answer' under module
module.set_fn_1("inc", |x: i64| Ok(x+1)); // use the 'set_fn_XXX' API to add functions
// Create the module resolver
let mut resolver = StaticModuleResolver::new();
// Add the module into the module resolver under the name 'question'
// They module can then be accessed via: 'import "question" as q;'
resolver.insert("question", module);
// Set the module resolver into the 'Engine'
engine.set_module_resolver(Some(resolver));
// Use module-qualified variables
engine.eval::<i64>(&scope, r#"import "question" as q; q::answer + 1"#)? == 42;
// Call module-qualified functions
engine.eval::<i64>(&scope, r#"import "question" as q; q::inc(q::answer)"#)? == 42;
```

View File

@@ -0,0 +1,38 @@
Module Resolvers
================
{{#include ../../links.md}}
When encountering an [`import`] statement, Rhai attempts to _resolve_ the module based on the path string.
_Module Resolvers_ are service types that implement the [`ModuleResolver`][traits] trait.
Built-In Module Resolvers
------------------------
There are a number of standard resolvers built into Rhai, the default being the `FileModuleResolver`
which simply loads a script file based on the path (with `.rhai` extension attached) and execute it to form a module.
Built-in module resolvers are grouped under the `rhai::module_resolvers` module namespace.
| Module Resolver | Description |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FileModuleResolver` | The default module resolution service, not available under [`no_std`] or [WASM] builds. Loads a script file (based off the current directory) with `.rhai` extension.<br/>The base directory can be changed via the `FileModuleResolver::new_with_path()` constructor function.<br/>`FileModuleResolver::create_module()` loads a script file and returns a module. |
| `StaticModuleResolver` | Loads modules that are statically added. This can be used under [`no_std`]. |
| `ModuleResolversCollection` | A collection of module resolvers. Modules will be resolved from each resolver in sequential order.<br/>This is useful when multiple types of modules are needed simultaneously. |
Set into `Engine`
-----------------
An [`Engine`]'s module resolver is set via a call to `Engine::set_module_resolver`:
```rust
// Use the 'StaticModuleResolver'
let resolver = rhai::module_resolvers::StaticModuleResolver::new();
engine.set_module_resolver(Some(resolver));
// Effectively disable 'import' statements by setting module resolver to 'None'
engine.set_module_resolver(None);
```

View File

@@ -39,17 +39,19 @@ engine.register_fn("+", strange_add); // overload '+' operator for
let result: i64 = engine.eval("1 + 0"); // the overloading version is used
println!("result: {}", result); // prints 42
result == 42;
let result: f64 = engine.eval("1.0 + 0.0"); // '+' operator for two floats not overloaded
println!("result: {}", result); // prints 1.0
result == 1.0;
fn mixed_add(a: i64, b: f64) -> f64 { (a as f64) + b }
engine.register_fn("+", mixed_add); // register '+' operator for an integer and a float
let result: i64 = engine.eval("1 + 1.0"); // prints 2.0 (normally an error)
let result: i64 = engine.eval("1 + 1.0"); // <- normally an error...
result == 2.0; // ... but not now
```

View File

@@ -75,15 +75,18 @@ Closure Signature
The closure passed to `Engine::register_raw_fn` takes the following form:
`Fn(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> + 'static`
`Fn(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>> + 'static`
where:
* `engine` - a reference to the current [`Engine`], with all configurations and settings.
* `T : Variant + Clone` - return type of the function.
* `lib` - a reference to the current collection of script-defined functions, as a [`Module`].
* `engine : &Engine` - the current [`Engine`], with all configurations and settings.
* `args` - a reference to a slice containing `&mut` references to [`Dynamic`] values.
* `lib : &Module` - the current global library of script-defined functions, as a [`Module`].
This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`].
* `args : &mut [&mut Dynamic]` - a slice containing `&mut` references to [`Dynamic`] values.
The slice is guaranteed to contain enough arguments _of the correct types_.
Remember, in Rhai, all arguments _except_ the _first_ one are always passed by _value_ (i.e. cloned).
@@ -100,13 +103,54 @@ To extract an argument from the `args` parameter (`&mut [&mut Dynamic]`), use th
| ------------------------------ | -------------------------------------- | ---------------------------------------------------------- |
| [Primary type][standard types] | `args[n].clone().cast::<T>()` | Copy of value. |
| Custom type | `args[n].downcast_ref::<T>().unwrap()` | Immutable reference to value. |
| Custom type (consumed) | `mem::take(args[n]).cast::<T>()` | The _consumed_ value.<br/>The original value becomes `()`. |
| Custom type (consumed) | `std::mem::take(args[n]).cast::<T>()` | The _consumed_ value.<br/>The original value becomes `()`. |
| `this` object | `args[0].downcast_mut::<T>().unwrap()` | Mutable reference to value. |
When there is a mutable reference to the `this` object (i.e. the first argument),
there can be no other immutable references to `args`, otherwise the Rust borrow checker will complain.
Example - Passing a Function Pointer to a Rust Function
------------------------------------------------------
```rust
use rhai::{Engine, Module, Dynamic, FnPtr};
let mut engine = Engine::new();
// Register a Rust function
engine.register_raw_fn(
"bar",
&[
std::any::TypeId::of::<i64>(), // parameter types
std::any::TypeId::of::<FnPtr>(),
std::any::TypeId::of::<i64>(),
],
move |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
// 'args' is guaranteed to contain enough arguments of the correct types
let fp = std::mem::take(args[1]).cast::<FnPtr>(); // 2nd argument - function pointer
let value = args[2].clone(); // 3rd argument - function argument
let this_ptr = args.get_mut(0).unwrap(); // 1st argument - this pointer
// Use 'call_fn_dynamic' to call the function name.
// Pass 'lib' as the current global library of functions.
engine.call_fn_dynamic(&mut Scope::new(), lib, fp.fn_name(), Some(this_ptr), [value])?;
Ok(())
},
);
let result = engine.eval::<i64>(r#"
fn foo(x) { this += x; } // script-defined function 'foo'
let x = 41; // object
x.bar(Fn("foo"), 1); // pass 'foo' as function pointer
x
"#)?;
```
Hold Multiple References
------------------------