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, Box> { // 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.