reorganize module
This commit is contained in:
		@@ -1,88 +0,0 @@
 | 
			
		||||
Create a Module from an AST
 | 
			
		||||
===========================
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
`Module::eval_ast_as_new`
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
```admonish info.side.wide "Encapsulated environment"
 | 
			
		||||
 | 
			
		||||
`Module::eval_ast_as_new` encapsulates the entire [`AST`] into each function call, merging the
 | 
			
		||||
[module namespace][function namespace] with the [global namespace][function namespace].
 | 
			
		||||
 | 
			
		||||
Therefore, [functions] defined within the same [module] script can cross-call each other.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish info.side.wide "See also"
 | 
			
		||||
 | 
			
		||||
See [_Export Variables, Functions and Sub-Modules from Script_][`export`] for details on how to prepare
 | 
			
		||||
a Rhai script for this purpose as well as to control which [functions]/[variables] to export.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
A [module] can be created from a single script (or pre-compiled [`AST`]) containing global
 | 
			
		||||
[variables], [functions] and [sub-modules][module] via `Module::eval_ast_as_new`.
 | 
			
		||||
 | 
			
		||||
When given an [`AST`], it is first evaluated (usually to [import][`import`] [modules] and set up global
 | 
			
		||||
[constants] used by [functions]), then the following items are exposed as members of the new [module]:
 | 
			
		||||
 | 
			
		||||
* global [variables] and [constants] – all [variables] and [constants] exported via the
 | 
			
		||||
  [`export`] statement (those not exported remain hidden),
 | 
			
		||||
 | 
			
		||||
* [functions] not specifically marked [`private`],
 | 
			
		||||
 | 
			
		||||
* imported [modules] that remain in the [`Scope`] at the end of a script run become sub-modules.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
Don't forget the [`export`] statement, otherwise there will be no [variables] exposed by the
 | 
			
		||||
[module] other than non-[`private`] [functions] (unless that's intentional).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::{Engine, Module};
 | 
			
		||||
 | 
			
		||||
let engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
// Compile a script into an 'AST'
 | 
			
		||||
let ast = engine.compile(
 | 
			
		||||
r#"
 | 
			
		||||
    // Functions become module functions
 | 
			
		||||
    fn calc(x) {
 | 
			
		||||
        x + add_len(x, 1)       // functions within the same module
 | 
			
		||||
                                // can always cross-call each other!
 | 
			
		||||
    }
 | 
			
		||||
    fn add_len(x, y) {
 | 
			
		||||
        x + y.len
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Imported modules become sub-modules
 | 
			
		||||
    import "another module" as extra;
 | 
			
		||||
 | 
			
		||||
    // Variables defined at global level can become module variables
 | 
			
		||||
    const x = 123;
 | 
			
		||||
    let foo = 41;
 | 
			
		||||
    let hello;
 | 
			
		||||
 | 
			
		||||
    // Variable values become constant module variable values
 | 
			
		||||
    foo = calc(foo);
 | 
			
		||||
    hello = `hello, ${foo} worlds!`;
 | 
			
		||||
 | 
			
		||||
    // Finally, export the variables and modules
 | 
			
		||||
    export x as abc;            // aliased variable name
 | 
			
		||||
    export foo;
 | 
			
		||||
    export hello;
 | 
			
		||||
"#)?;
 | 
			
		||||
 | 
			
		||||
// Convert the 'AST' into a module, using the 'Engine' to evaluate it first
 | 
			
		||||
// A copy of the entire 'AST' is encapsulated into each function,
 | 
			
		||||
// allowing functions in the module script to cross-call each other.
 | 
			
		||||
let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
 | 
			
		||||
 | 
			
		||||
// 'module' now contains:
 | 
			
		||||
//   - sub-module: 'extra'
 | 
			
		||||
//   - functions: 'calc', 'add_len'
 | 
			
		||||
//   - constants: 'abc' (renamed from 'x'), 'foo', 'hello'
 | 
			
		||||
```
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
Create a Module in Rust
 | 
			
		||||
=======================
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The Easy Way – Plugin
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
By far the simplest way to create a [module] is via a [plugin module]
 | 
			
		||||
which converts a normal Rust module into a Rhai [module] via procedural macros.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The Hard Way – `Module` API
 | 
			
		||||
---------------------------------
 | 
			
		||||
 | 
			
		||||
Manually creating a [module] is possible via the [`Module`] public API, which is volatile and may
 | 
			
		||||
change from time to time.
 | 
			
		||||
 | 
			
		||||
~~~admonish info.small "`Module` public API"
 | 
			
		||||
 | 
			
		||||
For the complete [`Module`] public API, refer to the
 | 
			
		||||
[documentation](https://docs.rs/rhai/{{version}}/rhai/struct.Module.html) online.
 | 
			
		||||
~~~
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
Modules
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
Rhai allows organizing functionalities ([functions], both Rust-based and scripted, and
 | 
			
		||||
[variables]) into independent _modules_.
 | 
			
		||||
 | 
			
		||||
A module has the type `rhai::Module` and holds a collection of [functions], [variables], [type
 | 
			
		||||
iterators] and sub-modules.
 | 
			
		||||
 | 
			
		||||
It may contain entirely Rust functions, or it may encapsulate a Rhai script together with
 | 
			
		||||
all the [functions] and [variables] defined by that script.
 | 
			
		||||
 | 
			
		||||
Other scripts then load this module and use the [functions] and [variables] [exported][`export`].
 | 
			
		||||
 | 
			
		||||
Alternatively, modules can be registered directly into an [`Engine`] and made available to scripts,
 | 
			
		||||
either globally or under individual static module [_namespaces_][function namespaces].
 | 
			
		||||
 | 
			
		||||
Modules can be disabled via the [`no_module`] feature.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Usage Patterns
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
| Usage          |                API                |          Lookup          | Sub-modules? | Variables? |
 | 
			
		||||
| -------------- | :-------------------------------: | :----------------------: | :----------: | :--------: |
 | 
			
		||||
| Global module  | `Engine:: register_global_module` |       simple name        |   ignored    |    yes     |
 | 
			
		||||
| Static module  | `Engine:: register_static_module` | namespace-qualified name |     yes      |    yes     |
 | 
			
		||||
| Dynamic module |       [`import`] statement        | namespace-qualified name |     yes      |    yes     |
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
Built-in Module Resolvers
 | 
			
		||||
=========================
 | 
			
		||||
 | 
			
		||||
{{#include ../../../links.md}}
 | 
			
		||||
 | 
			
		||||
There are a number of standard [module resolvers] built into Rhai, the default being the
 | 
			
		||||
[`FileModuleResolver`](file.md) 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.
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
`ModuleResolversCollection`
 | 
			
		||||
===========================
 | 
			
		||||
 | 
			
		||||
{{#include ../../../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
A collection of [module resolvers].
 | 
			
		||||
 | 
			
		||||
[Modules] are resolved from each resolver in sequential order.
 | 
			
		||||
 | 
			
		||||
This is useful when multiple types of [modules] are needed simultaneously.
 | 
			
		||||
@@ -1,95 +0,0 @@
 | 
			
		||||
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 [`ModuleResolver`][traits] trait, which contains only one
 | 
			
		||||
required 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).
 | 
			
		||||
 | 
			
		||||
```admonish success
 | 
			
		||||
Upon success, it should return a shared [module] wrapped by `Rc` (or `Arc` under [`sync`]).
 | 
			
		||||
  
 | 
			
		||||
The module resolver should call `Module::build_index` on the target [module] before returning it.
 | 
			
		||||
* This method flattens the entire module tree and _indexes_ it for fast function name resolution.
 | 
			
		||||
* If the module is already indexed, calling this method has no effect.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish failure
 | 
			
		||||
 | 
			
		||||
* If the path does not resolve to a valid module, return `EvalAltResult::ErrorModuleNotFound`.
 | 
			
		||||
 | 
			
		||||
* If the module failed to load, return `EvalAltResult::ErrorInModule`.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Example of a Custom Module Resolver
 | 
			
		||||
-----------------------------------
 | 
			
		||||
 | 
			
		||||
```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'
 | 
			
		||||
        source_path: Option<&str>,              // path of the parent module
 | 
			
		||||
        path: &str,                             // the module path
 | 
			
		||||
        pos: Position,                          // position of the 'import' statement
 | 
			
		||||
    ) -> Result<Rc<Module>, Box<EvalAltResult>> {
 | 
			
		||||
        // Check module path.
 | 
			
		||||
        if is_valid_module_path(path) {
 | 
			
		||||
            // Load the custom module
 | 
			
		||||
            match load_secret_module(path) {
 | 
			
		||||
                Ok(my_module) => {
 | 
			
		||||
                    my_module.build_index();    // index it
 | 
			
		||||
                    Rc::new(my_module)          // make it shared
 | 
			
		||||
                },
 | 
			
		||||
                // Return 'EvalAltResult::ErrorInModule' upon loading error
 | 
			
		||||
                Err(err) => Err(EvalAltResult::ErrorInModule(path.into(), Box::new(err), pos).into())
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // Return 'EvalAltResult::ErrorModuleNotFound' if the path is invalid
 | 
			
		||||
            Err(EvalAltResult::ErrorModuleNotFound(path.into(), pos).into())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
// Set the custom module resolver into the 'Engine'.
 | 
			
		||||
engine.set_module_resolver(MyModuleResolver {});
 | 
			
		||||
 | 
			
		||||
engine.run(
 | 
			
		||||
r#"
 | 
			
		||||
    import "hello" as foo;  // this 'import' statement will call
 | 
			
		||||
                            // 'MyModuleResolver::resolve' with "hello" as 'path'
 | 
			
		||||
    foo:bar();
 | 
			
		||||
"#)?;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Advanced – `ModuleResolver::resolve_ast`
 | 
			
		||||
----------------------------------------------
 | 
			
		||||
 | 
			
		||||
There is another function in the [`ModuleResolver`][traits] trait, `resolve_ast`, which is a
 | 
			
		||||
low-level API intended for advanced usage scenarios.
 | 
			
		||||
 | 
			
		||||
`ModuleResolver::resolve_ast` has a default implementation that simply returns `None`,
 | 
			
		||||
which indicates that this API is not supported by the [module resolver].
 | 
			
		||||
 | 
			
		||||
Any [module resolver] that serves [modules] based on Rhai scripts should implement
 | 
			
		||||
`ModuleResolver::resolve_ast`. When called, the compiled [`AST`] of the script should be returned.
 | 
			
		||||
 | 
			
		||||
`ModuleResolver::resolve_ast` should not return an error if `ModuleResolver::resolve` will not.
 | 
			
		||||
On the other hand, the same error should be returned if `ModuleResolver::resolve` will return one.
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
`DummyModuleResolver`
 | 
			
		||||
=====================
 | 
			
		||||
 | 
			
		||||
{{#include ../../../links.md}}
 | 
			
		||||
 | 
			
		||||
```admonish abstract.small "Default"
 | 
			
		||||
 | 
			
		||||
`DummyModuleResolver` is the default for [`no_std`] or [`Engine::new_raw`][raw `Engine`].
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This [module resolver] acts as a _dummy_ and fails all module resolution calls.
 | 
			
		||||
@@ -1,64 +0,0 @@
 | 
			
		||||
`DylibModuleResolver`
 | 
			
		||||
=====================
 | 
			
		||||
 | 
			
		||||
{{#include ../../../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
~~~admonish warning.small "Requires external crate `rhai-dylib`"
 | 
			
		||||
 | 
			
		||||
`DylibModuleResolver` resides in the [`rhai-dylib`] crate which must be specified
 | 
			
		||||
as a dependency:
 | 
			
		||||
 | 
			
		||||
```toml
 | 
			
		||||
[dependencies]
 | 
			
		||||
rhai-dylib = { version = "0.1" }
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
```admonish danger.small "Linux or Windows only"
 | 
			
		||||
 | 
			
		||||
[`rhai-dylib`] currently supports only Linux and Windows.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Parallel to how the [`FileModuleResolver`](file.md) works, `DylibModuleResolver` loads external
 | 
			
		||||
native Rust [modules] from compiled _dynamic shared libraries_ (e.g. `.so` in Linux and `.dll` in
 | 
			
		||||
Windows).
 | 
			
		||||
 | 
			
		||||
Therefore, [`FileModuleResolver`](file.md`) loads Rhai script files while `DylibModuleResolver`
 | 
			
		||||
loads native Rust shared libraries.  It is very common to have the two work together.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Example
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::{Engine, Module};
 | 
			
		||||
use rhai::module_resolvers::{FileModuleResolver, ModuleResolversCollection};
 | 
			
		||||
use rhai_dylib::module_resolvers::DylibModuleResolver;
 | 
			
		||||
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
// Use a module resolvers collection
 | 
			
		||||
let mut resolvers = ModuleResolversCollection::new();
 | 
			
		||||
 | 
			
		||||
// First search for script files in the file system
 | 
			
		||||
resolvers += FileModuleResolver::new();
 | 
			
		||||
 | 
			
		||||
// Then search for shared-library plugins in the file system
 | 
			
		||||
resolvers += DylibModuleResolver::new();
 | 
			
		||||
 | 
			
		||||
// Set the module resolver into the engine
 | 
			
		||||
engine.set_module_resolver(resolvers);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌─────────────┐
 | 
			
		||||
│ Rhai Script │
 | 
			
		||||
└─────────────┘
 | 
			
		||||
 | 
			
		||||
// If there is 'path/to/my_module.rhai', load it.
 | 
			
		||||
// Otherwise, check for 'path/to/my_module.so' on Linux
 | 
			
		||||
// ('path/to/my_module.dll' on Windows).
 | 
			
		||||
import "path/to/my_module" as m;
 | 
			
		||||
 | 
			
		||||
m::greet();
 | 
			
		||||
```
 | 
			
		||||
@@ -1,183 +0,0 @@
 | 
			
		||||
`FileModuleResolver`
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
{{#include ../../../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish abstract.small "Default"
 | 
			
		||||
 | 
			
		||||
`FileModuleResolver` is the default for [`Engine::new`][`Engine`].
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The _default_ [module] resolution service, not available for [`no_std`] or [WASM] builds.
 | 
			
		||||
Loads a script file (based off the current directory or a specified one) with `.rhai` extension.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Function Namespace
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
All functions in the [_global_ namespace][function namespace], plus all those defined in the same
 | 
			
		||||
[module], are _merged_ into a _unified_ [namespace][function namespace].
 | 
			
		||||
 | 
			
		||||
All [modules] imported at _global_ level via [`import`] statements become sub-[modules],
 | 
			
		||||
which are also available to [functions] defined within the same script file.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Base Directory
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
```admonish tip.side.wide "Tip: Default"
 | 
			
		||||
 | 
			
		||||
If the base directory is not set, then relative paths are based off the directory of the loading script.
 | 
			
		||||
 | 
			
		||||
This allows scripts to simply cross-load each other.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
_Relative_ paths are resolved relative to a _root_ directory, which is usually the base directory.
 | 
			
		||||
 | 
			
		||||
The base directory can be set via `FileModuleResolver::new_with_path` or
 | 
			
		||||
`FileModuleResolver::set_base_path`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Custom [`Scope`]
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
```admonish tip.side.wide "Tip"
 | 
			
		||||
 | 
			
		||||
This [`Scope`] can conveniently hold global [constants] etc.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `set_scope` method adds an optional [`Scope`] which will be used to [optimize][script optimization] [module] scripts.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Caching
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
```admonish tip.side.wide "Tip: Enable/disable caching"
 | 
			
		||||
 | 
			
		||||
Use `enable_cache` to enable/disable the cache.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
By default, [modules] are also _cached_ so a script file is only evaluated _once_, even when
 | 
			
		||||
repeatedly imported.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Unix Shebangs
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
On Unix-like systems, the _shebang_ (`#!`) is used at the very beginning of a script file to mark a
 | 
			
		||||
script with an interpreter (for Rhai this would be [`rhai-run`]({{rootUrl}}/start/bin.md)).
 | 
			
		||||
 | 
			
		||||
If a script file starts with `#!`, the entire first line is skipped.
 | 
			
		||||
Because of this, Rhai scripts with shebangs at the beginning need no special processing.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
#!/home/to/me/bin/rhai-run
 | 
			
		||||
 | 
			
		||||
// This is a Rhai script
 | 
			
		||||
 | 
			
		||||
let answer = 42;
 | 
			
		||||
print(`The answer is: ${answer}`);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Example
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
┌────────────────┐
 | 
			
		||||
│ my_module.rhai │
 | 
			
		||||
└────────────────┘
 | 
			
		||||
 | 
			
		||||
// This function overrides any in the main script.
 | 
			
		||||
private fn inner_message() { "hello! from module!" }
 | 
			
		||||
 | 
			
		||||
fn greet() {
 | 
			
		||||
    print(inner_message());     // call function in module script
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn greet_main() {
 | 
			
		||||
    print(main_message());      // call function not in module script
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌───────────┐
 | 
			
		||||
│ main.rhai │
 | 
			
		||||
└───────────┘
 | 
			
		||||
 | 
			
		||||
// This function is overridden by the module script.
 | 
			
		||||
fn inner_message() { "hi! from main!" }
 | 
			
		||||
 | 
			
		||||
// This function is found by the module script.
 | 
			
		||||
fn main_message() { "main here!" }
 | 
			
		||||
 | 
			
		||||
import "my_module" as m;
 | 
			
		||||
 | 
			
		||||
m::greet();                     // prints "hello! from module!"
 | 
			
		||||
 | 
			
		||||
m::greet_main();                // prints "main here!"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Simulate Virtual Functions
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
When calling a namespace-qualified [function] defined within a [module], other [functions] defined
 | 
			
		||||
within the same module override any similar-named [functions] (with the same number of parameters)
 | 
			
		||||
defined in the [global namespace][function namespace].
 | 
			
		||||
 | 
			
		||||
This is to ensure that a [module] acts as a self-contained unit and [functions] defined in the
 | 
			
		||||
calling script do not override [module] code.
 | 
			
		||||
 | 
			
		||||
In some situations, however, it is actually beneficial to do it in reverse: have [module] [functions] call
 | 
			
		||||
[functions] defined in the calling script (i.e. in the [global namespace][function namespace]) if
 | 
			
		||||
they exist, and only call those defined in the [module] if none are found.
 | 
			
		||||
 | 
			
		||||
One such situation is the need to provide a _default implementation_ to a simulated _virtual_ function:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
┌────────────────┐
 | 
			
		||||
│ my_module.rhai │
 | 
			
		||||
└────────────────┘
 | 
			
		||||
 | 
			
		||||
// Do not do this (it will override the main script):
 | 
			
		||||
// fn message() { "hello! from module!" }
 | 
			
		||||
 | 
			
		||||
// This function acts as the default implementation.
 | 
			
		||||
private fn default_message() { "hello! from module!" }
 | 
			
		||||
 | 
			
		||||
// This function depends on a 'virtual' function 'message'
 | 
			
		||||
// which is not defined in the module script.
 | 
			
		||||
fn greet() {
 | 
			
		||||
    if is_def_fn("message", 0) {    // 'is_def_fn' detects if 'message' is defined.
 | 
			
		||||
        print(message());
 | 
			
		||||
    } else {
 | 
			
		||||
        print(default_message());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌───────────┐
 | 
			
		||||
│ main.rhai │
 | 
			
		||||
└───────────┘
 | 
			
		||||
 | 
			
		||||
// The main script defines 'message' which is needed by the module script.
 | 
			
		||||
fn message() { "hi! from main!" }
 | 
			
		||||
 | 
			
		||||
import "my_module" as m;
 | 
			
		||||
 | 
			
		||||
m::greet();                         // prints "hi! from main!"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌────────────┐
 | 
			
		||||
│ main2.rhai │
 | 
			
		||||
└────────────┘
 | 
			
		||||
 | 
			
		||||
// The main script does not define 'message' which is needed by the module script.
 | 
			
		||||
 | 
			
		||||
import "my_module" as m;
 | 
			
		||||
 | 
			
		||||
m::greet();                         // prints "hello! from module!"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
Module Resolvers
 | 
			
		||||
================
 | 
			
		||||
 | 
			
		||||
{{#include ../../../links.md}}
 | 
			
		||||
 | 
			
		||||
~~~admonish info.side "`import`"
 | 
			
		||||
 | 
			
		||||
See the section on [_Importing Modules_][`import`] for more details.
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Set into `Engine`
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
An [`Engine`]'s module resolver is set via a call to `Engine::set_module_resolver`:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::module_resolvers::{DummyModuleResolver, StaticModuleResolver};
 | 
			
		||||
 | 
			
		||||
// Create a module resolver
 | 
			
		||||
let resolver = StaticModuleResolver::new();
 | 
			
		||||
 | 
			
		||||
// Register functions into 'resolver'...
 | 
			
		||||
 | 
			
		||||
// Use the module resolver
 | 
			
		||||
engine.set_module_resolver(resolver);
 | 
			
		||||
 | 
			
		||||
// Effectively disable 'import' statements by setting module resolver to
 | 
			
		||||
// the 'DummyModuleResolver' which acts as... well... a dummy.
 | 
			
		||||
engine.set_module_resolver(DummyModuleResolver::new());
 | 
			
		||||
```
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
`StaticModuleResolver`
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
{{#include ../../../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
~~~admonish abstract.small "Useful for `no-std`"
 | 
			
		||||
 | 
			
		||||
`StaticModuleResolver` is often used with [`no_std`] in embedded environments
 | 
			
		||||
without a file system.
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
Loads [modules] that are statically added.
 | 
			
		||||
 | 
			
		||||
Functions are searched in the [_global_ namespace][function namespace] by default.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::{Module, module_resolvers::StaticModuleResolver};
 | 
			
		||||
 | 
			
		||||
let module: Module = create_a_module();
 | 
			
		||||
 | 
			
		||||
let mut resolver = StaticModuleResolver::new();
 | 
			
		||||
resolver.insert("my_module", module);
 | 
			
		||||
 | 
			
		||||
engine.set_module_resolver(resolver);
 | 
			
		||||
```
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
Compile to a Self-Contained `AST`
 | 
			
		||||
=================================
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
```admonish tip.side "Tip"
 | 
			
		||||
 | 
			
		||||
It does not matter where the [`import`] statement occurs — e.g. deep within statements blocks
 | 
			
		||||
or within [function] bodies.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
When a script [imports][`import`] external [modules] that may not be available later on, it is
 | 
			
		||||
possible to eagerly [_pre-resolve_][module resolver] these imports and embed them directly into a
 | 
			
		||||
self-contained [`AST`].
 | 
			
		||||
 | 
			
		||||
For instance, a system may periodically connect to a central source (e.g. a database) to load
 | 
			
		||||
scripts and compile them to [`AST`] form. Afterwards, in order to conserve bandwidth (or due to
 | 
			
		||||
other physical limitations), it is disconnected from the central source for self-contained
 | 
			
		||||
operation.
 | 
			
		||||
 | 
			
		||||
Compile a script into a _self-contained_ [`AST`] via `Engine::compile_into_self_contained`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
// Compile script into self-contained AST using the current
 | 
			
		||||
// module resolver (default to `FileModuleResolver`) to pre-resolve
 | 
			
		||||
// 'import' statements.
 | 
			
		||||
let ast = engine.compile_into_self_contained(&mut scope, script)?;
 | 
			
		||||
 | 
			
		||||
// Make sure we can no longer resolve any module!
 | 
			
		||||
engine.set_module_resolver(DummyModuleResolver::new());
 | 
			
		||||
 | 
			
		||||
// The AST still evaluates fine, even with 'import' statements!
 | 
			
		||||
engine.run(&ast)?;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
When such an [`AST`] is evaluated, [`import`] statements within are provided the _pre-resolved_
 | 
			
		||||
[modules] without going through the normal [module resolution][module resolver] process.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Only Static Paths
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
`Engine::compile_into_self_contained` only pre-resolves [`import`] statements in the script
 | 
			
		||||
that are _static_, i.e. with a path that is a [string] literal.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// The following import is pre-resolved.
 | 
			
		||||
import "hello" as h;
 | 
			
		||||
 | 
			
		||||
if some_event() {
 | 
			
		||||
    // The following import is pre-resolved.
 | 
			
		||||
    import "hello" as h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn foo() {
 | 
			
		||||
    // The following import is pre-resolved.
 | 
			
		||||
    import "hello" as h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The following import is also pre-resolved because the expression
 | 
			
		||||
// is usually optimized into a single string during compilation.
 | 
			
		||||
import "he" + "llo" as h;
 | 
			
		||||
 | 
			
		||||
let module_name = "hello";
 | 
			
		||||
 | 
			
		||||
// The following import is NOT pre-resolved.
 | 
			
		||||
import module_name as h;
 | 
			
		||||
```
 | 
			
		||||
@@ -1,183 +0,0 @@
 | 
			
		||||
Make a Module Available to Scripts
 | 
			
		||||
==================================
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Use Case 1 – Make It Globally Available
 | 
			
		||||
---------------------------------------------
 | 
			
		||||
 | 
			
		||||
`Engine::register_global_module` registers a shared [module] into the
 | 
			
		||||
[_global_ namespace][function namespace].
 | 
			
		||||
 | 
			
		||||
This is by far the easiest way to expose a [module]'s functionalities to Rhai.
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip: No qualifiers"
 | 
			
		||||
 | 
			
		||||
All [functions], [variables]/[constants] and [type iterators] can be accessed without
 | 
			
		||||
_namespace qualifiers_.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish warning.small
 | 
			
		||||
 | 
			
		||||
[Sub-modules][module] are **ignored**.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::{Engine, Module, FuncRegistration};
 | 
			
		||||
 | 
			
		||||
let mut module = Module::new();             // new module
 | 
			
		||||
 | 
			
		||||
// Add new function.
 | 
			
		||||
FuncRegistration::new("inc")
 | 
			
		||||
    .with_params_info(&["x: i64", "i64"])
 | 
			
		||||
    .set_into_module(&mut module, |x: i64| x + 1);
 | 
			
		||||
 | 
			
		||||
// Use 'Module::set_var' to add variables.
 | 
			
		||||
module.set_var("MYSTIC_NUMBER", 41_i64);
 | 
			
		||||
 | 
			
		||||
// Register the module into the global namespace of the Engine.
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
engine.register_global_module(module.into());
 | 
			
		||||
 | 
			
		||||
// No need to import module...
 | 
			
		||||
engine.eval::<i64>("inc(MYSTIC_NUMBER)")? == 42;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Equivalent to `Engine::register_XXX`
 | 
			
		||||
 | 
			
		||||
```admonish question.side "Trivia"
 | 
			
		||||
 | 
			
		||||
`Engine::register_fn` etc. are actually implemented by adding functions to an
 | 
			
		||||
internal [module]!
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Registering a [module] via `Engine::register_global_module` is essentially the _same_
 | 
			
		||||
as calling `Engine::register_fn` (or any of the `Engine::register_XXX` API) individually
 | 
			
		||||
on each top-level function within that [module].
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// The above is essentially the same as:
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
engine.register_fn("inc", |x: i64| x + 1);
 | 
			
		||||
 | 
			
		||||
engine.eval::<i64>("inc(41)")? == 42;       // no need to import module
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Use Case 2 – Make It a Static Namespace
 | 
			
		||||
---------------------------------------------
 | 
			
		||||
 | 
			
		||||
`Engine::register_static_module` registers a [module] and under a specific
 | 
			
		||||
[module namespace][function namespace].
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::{Engine, Module, FuncRegistration};
 | 
			
		||||
 | 
			
		||||
let mut module = Module::new();             // new module
 | 
			
		||||
 | 
			
		||||
// Add new function.
 | 
			
		||||
FuncRegistration::new("inc")
 | 
			
		||||
    .with_params_info(&["x: i64", "i64"])
 | 
			
		||||
    .set_into_module(&mut module, |x: i64| x + 1);
 | 
			
		||||
 | 
			
		||||
// Use 'Module::set_var' to add variables.
 | 
			
		||||
module.set_var("MYSTIC_NUMBER", 41_i64);
 | 
			
		||||
 | 
			
		||||
// Register the module into the Engine as the static module namespace path
 | 
			
		||||
// 'services::calc'
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
engine.register_static_module("services::calc", module.into());
 | 
			
		||||
 | 
			
		||||
// Refer to the 'services::calc' module...
 | 
			
		||||
engine.eval::<i64>("services::calc::inc(services::calc::MYSTIC_NUMBER)")? == 42;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Expose functions to the global namespace
 | 
			
		||||
 | 
			
		||||
```admonish tip.side "Tip: Type iterators"
 | 
			
		||||
 | 
			
		||||
[Type iterators] are special — they are _always_ exposed to the
 | 
			
		||||
[_global_ namespace][function namespace].
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The [`Module`] API can optionally expose functions to the [_global_ namespace][function namespace]
 | 
			
		||||
by setting the `namespace` parameter to `FnNamespace::Global`.
 | 
			
		||||
 | 
			
		||||
This way, [getters/setters] and [indexers] for [custom types] can work as expected.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::{Engine, Module, FuncRegistration, FnNamespace};
 | 
			
		||||
 | 
			
		||||
let mut module = Module::new();             // new module
 | 
			
		||||
 | 
			
		||||
// Add new function.
 | 
			
		||||
FuncRegistration::new("inc")
 | 
			
		||||
    .with_params_info(&["x: i64", "i64"])
 | 
			
		||||
    .with_namespace(FnNamespace::Global)    // <- global namespace
 | 
			
		||||
    .set_into_module(&mut module, |x: i64| x + 1);
 | 
			
		||||
 | 
			
		||||
// Use 'Module::set_var' to add variables.
 | 
			
		||||
module.set_var("MYSTIC_NUMBER", 41_i64);
 | 
			
		||||
 | 
			
		||||
// Register the module into the Engine as a static module namespace 'calc'
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
engine.register_static_module("calc", module.into());
 | 
			
		||||
 | 
			
		||||
// 'inc' works when qualified by the namespace
 | 
			
		||||
engine.eval::<i64>("calc::inc(calc::MYSTIC_NUMBER)")? == 42;
 | 
			
		||||
 | 
			
		||||
// 'inc' also works without a namespace qualifier
 | 
			
		||||
// because it is exposed to the global namespace
 | 
			
		||||
engine.eval::<i64>("let x = calc::MYSTIC_NUMBER; x.inc()")? == 42;
 | 
			
		||||
engine.eval::<i64>("let x = calc::MYSTIC_NUMBER; inc(x)")? == 42;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Use Case 3 – Make It Dynamically Loadable
 | 
			
		||||
-----------------------------------------------
 | 
			
		||||
 | 
			
		||||
In order to dynamically load a custom module, there must be a [module resolver] which serves
 | 
			
		||||
the module when loaded via `import` statements.
 | 
			
		||||
 | 
			
		||||
The easiest way is to use, for example, the [`StaticModuleResolver`][module resolver] to hold such
 | 
			
		||||
a custom module.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::{Engine, Scope, Module, FuncRegistration};
 | 
			
		||||
use rhai::module_resolvers::StaticModuleResolver;
 | 
			
		||||
 | 
			
		||||
let mut module = Module::new();             // new module
 | 
			
		||||
 | 
			
		||||
module.set_var("answer", 41_i64);           // variable 'answer' under module
 | 
			
		||||
 | 
			
		||||
FuncRegistration::new("inc")
 | 
			
		||||
    .with_params_info(&["x: i64"])
 | 
			
		||||
    .set_into_module(&mut module, |x: i64| x + 1);
 | 
			
		||||
 | 
			
		||||
// 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'
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
engine.set_module_resolver(resolver);
 | 
			
		||||
 | 
			
		||||
// Use namespace-qualified variables
 | 
			
		||||
engine.eval::<i64>(
 | 
			
		||||
r#"
 | 
			
		||||
    import "question" as q;
 | 
			
		||||
    q::answer + 1
 | 
			
		||||
"#)? == 42;
 | 
			
		||||
 | 
			
		||||
// Call namespace-qualified functions
 | 
			
		||||
engine.eval::<i64>(
 | 
			
		||||
r#"
 | 
			
		||||
    import "question" as q;
 | 
			
		||||
    q::inc(q::answer)
 | 
			
		||||
"#)? == 42;
 | 
			
		||||
```
 | 
			
		||||
		Reference in New Issue
	
	Block a user