3.7 KiB
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).
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.
* 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
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.