reorganize module
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
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.
|
Reference in New Issue
Block a user