Refactor module.rs into files structure.
This commit is contained in:
parent
e637bfa51d
commit
93f53fa417
@ -93,8 +93,13 @@ pub use result::EvalAltResult;
|
|||||||
pub use scope::Scope;
|
pub use scope::Scope;
|
||||||
pub use syntax::{EvalContext, Expression};
|
pub use syntax::{EvalContext, Expression};
|
||||||
pub use token::Position;
|
pub use token::Position;
|
||||||
|
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
pub use utils::calc_fn_hash;
|
pub use utils::calc_fn_hash;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "internals"))]
|
||||||
|
pub(crate) use utils::calc_fn_hash;
|
||||||
|
|
||||||
pub use rhai_codegen::*;
|
pub use rhai_codegen::*;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -118,8 +123,6 @@ pub use parser::FLOAT;
|
|||||||
pub use module::ModuleResolver;
|
pub use module::ModuleResolver;
|
||||||
|
|
||||||
/// Module containing all built-in _module resolvers_ available to Rhai.
|
/// Module containing all built-in _module resolvers_ available to Rhai.
|
||||||
///
|
|
||||||
/// Not available under the `no_module` feature.
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub mod module_resolvers {
|
pub mod module_resolvers {
|
||||||
pub use crate::module::resolvers::*;
|
pub use crate::module::resolvers::*;
|
||||||
|
@ -38,16 +38,6 @@ use crate::stdlib::{
|
|||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
use crate::stdlib::cell::RefCell;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
use crate::stdlib::sync::RwLock;
|
|
||||||
|
|
||||||
/// Return type of module-level Rust function.
|
/// Return type of module-level Rust function.
|
||||||
pub type FuncReturn<T> = Result<T, Box<EvalAltResult>>;
|
pub type FuncReturn<T> = Result<T, Box<EvalAltResult>>;
|
||||||
|
|
||||||
@ -494,8 +484,12 @@ impl Module {
|
|||||||
self.set_fn(name, Public, arg_types, Func::from_method(Box::new(f)))
|
self.set_fn(name, Public, arg_types, Func::from_method(Box::new(f)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a raw function but with a signature that is a scripted function, but the implementation is in Rust.
|
/// Set a raw function but with a signature that is a scripted function (meaning that the types
|
||||||
|
/// are not determined), but the implementation is in Rust.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub(crate) fn set_raw_fn_as_scripted(
|
pub(crate) fn set_raw_fn_as_scripted(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
@ -1372,573 +1366,10 @@ impl ModuleRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait that encapsulates a module resolution service.
|
/// Re-export module resolver trait.
|
||||||
pub trait ModuleResolver: SendSync {
|
#[cfg(not(feature = "no_module"))]
|
||||||
/// Resolve a module based on a path string.
|
pub use resolvers::ModuleResolver;
|
||||||
fn resolve(&self, _: &Engine, path: &str, pos: Position) -> Result<Module, Box<EvalAltResult>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Re-export module resolvers.
|
/// Re-export module resolvers.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub mod resolvers {
|
pub mod resolvers;
|
||||||
pub use super::collection::ModuleResolversCollection;
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
pub use super::file::{FileModuleResolver, GlobalFileModuleResolver};
|
|
||||||
pub use super::stat::StaticModuleResolver;
|
|
||||||
}
|
|
||||||
#[cfg(feature = "no_module")]
|
|
||||||
pub mod resolvers {}
|
|
||||||
|
|
||||||
/// Script file-based module resolver.
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
mod file {
|
|
||||||
use super::*;
|
|
||||||
use crate::stdlib::path::PathBuf;
|
|
||||||
|
|
||||||
/// Module resolution service that loads module script files from the file system.
|
|
||||||
///
|
|
||||||
/// All functions in each module are treated as strictly independent and cannot refer to
|
|
||||||
/// other functions within the same module. Functions are searched in the _global_ namespace.
|
|
||||||
///
|
|
||||||
/// For simple utility libraries, this usually performs better than the full `FileModuleResolver`.
|
|
||||||
///
|
|
||||||
/// Script files are cached so they are are not reloaded and recompiled in subsequent requests.
|
|
||||||
///
|
|
||||||
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
|
|
||||||
/// allow specification of a base directory with module path used as a relative path offset
|
|
||||||
/// to the base directory. The script file is then forced to be in a specified extension
|
|
||||||
/// (default `.rhai`).
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::Engine;
|
|
||||||
/// use rhai::module_resolvers::GlobalFileModuleResolver;
|
|
||||||
///
|
|
||||||
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the 'scripts' subdirectory
|
|
||||||
/// // with file extension '.x'.
|
|
||||||
/// let resolver = GlobalFileModuleResolver::new_with_path_and_extension("./scripts", "x");
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
|
||||||
/// ```
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct GlobalFileModuleResolver {
|
|
||||||
path: PathBuf,
|
|
||||||
extension: String,
|
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
cache: RefCell<HashMap<PathBuf, AST>>,
|
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
cache: RwLock<HashMap<PathBuf, AST>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GlobalFileModuleResolver {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new_with_path(PathBuf::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlobalFileModuleResolver {
|
|
||||||
/// Create a new `GlobalFileModuleResolver` with a specific base path.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::Engine;
|
|
||||||
/// use rhai::module_resolvers::GlobalFileModuleResolver;
|
|
||||||
///
|
|
||||||
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the 'scripts' subdirectory
|
|
||||||
/// // with file extension '.rhai' (the default).
|
|
||||||
/// let resolver = GlobalFileModuleResolver::new_with_path("./scripts");
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
|
||||||
/// ```
|
|
||||||
pub fn new_with_path<P: Into<PathBuf>>(path: P) -> Self {
|
|
||||||
Self::new_with_path_and_extension(path, "rhai")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new `GlobalFileModuleResolver` with a specific base path and file extension.
|
|
||||||
///
|
|
||||||
/// The default extension is `.rhai`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::Engine;
|
|
||||||
/// use rhai::module_resolvers::GlobalFileModuleResolver;
|
|
||||||
///
|
|
||||||
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the 'scripts' subdirectory
|
|
||||||
/// // with file extension '.x'.
|
|
||||||
/// let resolver = GlobalFileModuleResolver::new_with_path_and_extension("./scripts", "x");
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
|
||||||
/// ```
|
|
||||||
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>(
|
|
||||||
path: P,
|
|
||||||
extension: E,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
path: path.into(),
|
|
||||||
extension: extension.into(),
|
|
||||||
cache: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new `GlobalFileModuleResolver` with the current directory as base path.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::Engine;
|
|
||||||
/// use rhai::module_resolvers::GlobalFileModuleResolver;
|
|
||||||
///
|
|
||||||
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the current directory
|
|
||||||
/// // with file extension '.rhai' (the default).
|
|
||||||
/// let resolver = GlobalFileModuleResolver::new();
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
|
||||||
/// ```
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a `Module` from a file path.
|
|
||||||
pub fn create_module<P: Into<PathBuf>>(
|
|
||||||
&self,
|
|
||||||
engine: &Engine,
|
|
||||||
path: &str,
|
|
||||||
) -> Result<Module, Box<EvalAltResult>> {
|
|
||||||
self.resolve(engine, path, Default::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleResolver for GlobalFileModuleResolver {
|
|
||||||
fn resolve(
|
|
||||||
&self,
|
|
||||||
engine: &Engine,
|
|
||||||
path: &str,
|
|
||||||
pos: Position,
|
|
||||||
) -> Result<Module, Box<EvalAltResult>> {
|
|
||||||
// Construct the script file path
|
|
||||||
let mut file_path = self.path.clone();
|
|
||||||
file_path.push(path);
|
|
||||||
file_path.set_extension(&self.extension); // Force extension
|
|
||||||
|
|
||||||
let scope = Default::default();
|
|
||||||
|
|
||||||
// See if it is cached
|
|
||||||
let (module, ast) = {
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
let c = self.cache.borrow();
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
let c = self.cache.read().unwrap();
|
|
||||||
|
|
||||||
if let Some(ast) = c.get(&file_path) {
|
|
||||||
(
|
|
||||||
Module::eval_ast_as_new(scope, ast, engine)
|
|
||||||
.map_err(|err| err.new_position(pos))?,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Load the file and compile it if not found
|
|
||||||
let ast = engine
|
|
||||||
.compile_file(file_path.clone())
|
|
||||||
.map_err(|err| err.new_position(pos))?;
|
|
||||||
|
|
||||||
(
|
|
||||||
Module::eval_ast_as_new(scope, &ast, engine)
|
|
||||||
.map_err(|err| err.new_position(pos))?,
|
|
||||||
Some(ast),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(ast) = ast {
|
|
||||||
// Put it into the cache
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
self.cache.borrow_mut().insert(file_path, ast);
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
self.cache.write().unwrap().insert(file_path, ast);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(module)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Module resolution service that loads module script files from the file system.
|
|
||||||
///
|
|
||||||
/// Script files are cached so they are are not reloaded and recompiled in subsequent requests.
|
|
||||||
///
|
|
||||||
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
|
|
||||||
/// allow specification of a base directory with module path used as a relative path offset
|
|
||||||
/// to the base directory. The script file is then forced to be in a specified extension
|
|
||||||
/// (default `.rhai`).
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::Engine;
|
|
||||||
/// use rhai::module_resolvers::FileModuleResolver;
|
|
||||||
///
|
|
||||||
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
|
|
||||||
/// // with file extension '.x'.
|
|
||||||
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
|
||||||
/// ```
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FileModuleResolver {
|
|
||||||
path: PathBuf,
|
|
||||||
extension: String,
|
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
cache: RefCell<HashMap<PathBuf, AST>>,
|
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
cache: RwLock<HashMap<PathBuf, AST>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FileModuleResolver {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new_with_path(PathBuf::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileModuleResolver {
|
|
||||||
/// Create a new `FileModuleResolver` with a specific base path.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::Engine;
|
|
||||||
/// use rhai::module_resolvers::FileModuleResolver;
|
|
||||||
///
|
|
||||||
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
|
|
||||||
/// // with file extension '.rhai' (the default).
|
|
||||||
/// let resolver = FileModuleResolver::new_with_path("./scripts");
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
|
||||||
/// ```
|
|
||||||
pub fn new_with_path<P: Into<PathBuf>>(path: P) -> Self {
|
|
||||||
Self::new_with_path_and_extension(path, "rhai")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new `FileModuleResolver` with a specific base path and file extension.
|
|
||||||
///
|
|
||||||
/// The default extension is `.rhai`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::Engine;
|
|
||||||
/// use rhai::module_resolvers::FileModuleResolver;
|
|
||||||
///
|
|
||||||
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
|
|
||||||
/// // with file extension '.x'.
|
|
||||||
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
|
||||||
/// ```
|
|
||||||
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>(
|
|
||||||
path: P,
|
|
||||||
extension: E,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
path: path.into(),
|
|
||||||
extension: extension.into(),
|
|
||||||
cache: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new `FileModuleResolver` with the current directory as base path.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::Engine;
|
|
||||||
/// use rhai::module_resolvers::FileModuleResolver;
|
|
||||||
///
|
|
||||||
/// // Create a new 'FileModuleResolver' loading scripts from the current directory
|
|
||||||
/// // with file extension '.rhai' (the default).
|
|
||||||
/// let resolver = FileModuleResolver::new();
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
|
||||||
/// ```
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a `Module` from a file path.
|
|
||||||
pub fn create_module<P: Into<PathBuf>>(
|
|
||||||
&self,
|
|
||||||
engine: &Engine,
|
|
||||||
path: &str,
|
|
||||||
) -> Result<Module, Box<EvalAltResult>> {
|
|
||||||
self.resolve(engine, path, Default::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleResolver for FileModuleResolver {
|
|
||||||
fn resolve(
|
|
||||||
&self,
|
|
||||||
engine: &Engine,
|
|
||||||
path: &str,
|
|
||||||
pos: Position,
|
|
||||||
) -> Result<Module, Box<EvalAltResult>> {
|
|
||||||
// Construct the script file path
|
|
||||||
let mut file_path = self.path.clone();
|
|
||||||
file_path.push(path);
|
|
||||||
file_path.set_extension(&self.extension); // Force extension
|
|
||||||
|
|
||||||
// See if it is cached
|
|
||||||
let exists = {
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
let c = self.cache.borrow();
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
let c = self.cache.read().unwrap();
|
|
||||||
|
|
||||||
c.contains_key(&file_path)
|
|
||||||
};
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
// Load the file and compile it if not found
|
|
||||||
let ast = engine
|
|
||||||
.compile_file(file_path.clone())
|
|
||||||
.map_err(|err| err.new_position(pos))?;
|
|
||||||
|
|
||||||
// Put it into the cache
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
self.cache.borrow_mut().insert(file_path.clone(), ast);
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
self.cache.write().unwrap().insert(file_path.clone(), ast);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
let c = self.cache.borrow();
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
let c = self.cache.read().unwrap();
|
|
||||||
|
|
||||||
let ast = c.get(&file_path).unwrap();
|
|
||||||
|
|
||||||
let mut _module = Module::eval_ast_as_new(Scope::new(), ast, engine)?;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
ast.iter_functions(|access, name, num_args| match access {
|
|
||||||
FnAccess::Private => (),
|
|
||||||
FnAccess::Public => {
|
|
||||||
let fn_name = name.to_string();
|
|
||||||
let ast_lib = ast.lib().clone();
|
|
||||||
|
|
||||||
_module.set_raw_fn_as_scripted(
|
|
||||||
name,
|
|
||||||
num_args,
|
|
||||||
move |engine: &Engine, _, args: &mut [&mut Dynamic]| {
|
|
||||||
engine.call_fn_dynamic_raw(
|
|
||||||
&mut Scope::new(),
|
|
||||||
&ast_lib,
|
|
||||||
&fn_name,
|
|
||||||
&mut None,
|
|
||||||
args,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(_module)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Static module resolver.
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
mod stat {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
/// Module resolution service that serves modules added into it.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::{Engine, Module};
|
|
||||||
/// use rhai::module_resolvers::StaticModuleResolver;
|
|
||||||
///
|
|
||||||
/// let mut resolver = StaticModuleResolver::new();
|
|
||||||
///
|
|
||||||
/// let module = Module::new();
|
|
||||||
/// resolver.insert("hello".to_string(), module);
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
|
||||||
/// ```
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct StaticModuleResolver(HashMap<String, Module>);
|
|
||||||
|
|
||||||
impl StaticModuleResolver {
|
|
||||||
/// Create a new `StaticModuleResolver`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::{Engine, Module};
|
|
||||||
/// use rhai::module_resolvers::StaticModuleResolver;
|
|
||||||
///
|
|
||||||
/// let mut resolver = StaticModuleResolver::new();
|
|
||||||
///
|
|
||||||
/// let module = Module::new();
|
|
||||||
/// resolver.insert("hello", module);
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
|
||||||
/// ```
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StaticModuleResolver {
|
|
||||||
/// Add a module keyed by its path.
|
|
||||||
pub fn insert<S: Into<String>>(&mut self, path: S, mut module: Module) {
|
|
||||||
module.index_all_sub_modules();
|
|
||||||
self.0.insert(path.into(), module);
|
|
||||||
}
|
|
||||||
/// Remove a module given its path.
|
|
||||||
pub fn remove(&mut self, path: &str) -> Option<Module> {
|
|
||||||
self.0.remove(path)
|
|
||||||
}
|
|
||||||
/// Does the path exist?
|
|
||||||
pub fn contains_path(&self, path: &str) -> bool {
|
|
||||||
self.0.contains_key(path)
|
|
||||||
}
|
|
||||||
/// Get an iterator of all the modules.
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
|
|
||||||
self.0.iter().map(|(k, v)| (k.as_str(), v))
|
|
||||||
}
|
|
||||||
/// Get a mutable iterator of all the modules.
|
|
||||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Module)> {
|
|
||||||
self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
|
|
||||||
}
|
|
||||||
/// Get an iterator of all the module paths.
|
|
||||||
pub fn paths(&self) -> impl Iterator<Item = &str> {
|
|
||||||
self.0.keys().map(String::as_str)
|
|
||||||
}
|
|
||||||
/// Get an iterator of all the modules.
|
|
||||||
pub fn values(&self) -> impl Iterator<Item = &Module> {
|
|
||||||
self.0.values()
|
|
||||||
}
|
|
||||||
/// Get a mutable iterator of all the modules.
|
|
||||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Module> {
|
|
||||||
self.0.values_mut()
|
|
||||||
}
|
|
||||||
/// Remove all modules.
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.0.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleResolver for StaticModuleResolver {
|
|
||||||
fn resolve(
|
|
||||||
&self,
|
|
||||||
_: &Engine,
|
|
||||||
path: &str,
|
|
||||||
pos: Position,
|
|
||||||
) -> Result<Module, Box<EvalAltResult>> {
|
|
||||||
self.0
|
|
||||||
.get(path)
|
|
||||||
.cloned()
|
|
||||||
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(path.into(), pos).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Module resolver collection.
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
mod collection {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
/// Module resolution service that holds a collection of module resolves,
|
|
||||||
/// to be searched in sequential order.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::{Engine, Module};
|
|
||||||
/// use rhai::module_resolvers::{StaticModuleResolver, ModuleResolversCollection};
|
|
||||||
///
|
|
||||||
/// let mut collection = ModuleResolversCollection::new();
|
|
||||||
///
|
|
||||||
/// let resolver = StaticModuleResolver::new();
|
|
||||||
/// collection.push(resolver);
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(collection));
|
|
||||||
/// ```
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ModuleResolversCollection(Vec<Box<dyn ModuleResolver>>);
|
|
||||||
|
|
||||||
impl ModuleResolversCollection {
|
|
||||||
/// Create a new `ModuleResolversCollection`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::{Engine, Module};
|
|
||||||
/// use rhai::module_resolvers::{StaticModuleResolver, ModuleResolversCollection};
|
|
||||||
///
|
|
||||||
/// let mut collection = ModuleResolversCollection::new();
|
|
||||||
///
|
|
||||||
/// let resolver = StaticModuleResolver::new();
|
|
||||||
/// collection.push(resolver);
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(Some(collection));
|
|
||||||
/// ```
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleResolversCollection {
|
|
||||||
/// Add a module keyed by its path.
|
|
||||||
pub fn push(&mut self, resolver: impl ModuleResolver + 'static) {
|
|
||||||
self.0.push(Box::new(resolver));
|
|
||||||
}
|
|
||||||
/// Get an iterator of all the module resolvers.
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &dyn ModuleResolver> {
|
|
||||||
self.0.iter().map(|v| v.as_ref())
|
|
||||||
}
|
|
||||||
/// Remove all module resolvers.
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.0.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleResolver for ModuleResolversCollection {
|
|
||||||
fn resolve(
|
|
||||||
&self,
|
|
||||||
engine: &Engine,
|
|
||||||
path: &str,
|
|
||||||
pos: Position,
|
|
||||||
) -> Result<Module, Box<EvalAltResult>> {
|
|
||||||
for resolver in self.0.iter() {
|
|
||||||
if let Ok(module) = resolver.resolve(engine, path, pos) {
|
|
||||||
return Ok(module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
80
src/module/resolvers/collection.rs
Normal file
80
src/module/resolvers/collection.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use crate::engine::Engine;
|
||||||
|
use crate::module::{Module, ModuleResolver};
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::token::Position;
|
||||||
|
|
||||||
|
use crate::stdlib::{boxed::Box, vec::Vec};
|
||||||
|
|
||||||
|
/// Module resolution service that holds a collection of module resolves,
|
||||||
|
/// to be searched in sequential order.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::{Engine, Module};
|
||||||
|
/// use rhai::module_resolvers::{StaticModuleResolver, ModuleResolversCollection};
|
||||||
|
///
|
||||||
|
/// let mut collection = ModuleResolversCollection::new();
|
||||||
|
///
|
||||||
|
/// let resolver = StaticModuleResolver::new();
|
||||||
|
/// collection.push(resolver);
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
/// engine.set_module_resolver(Some(collection));
|
||||||
|
/// ```
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ModuleResolversCollection(Vec<Box<dyn ModuleResolver>>);
|
||||||
|
|
||||||
|
impl ModuleResolversCollection {
|
||||||
|
/// Create a new `ModuleResolversCollection`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::{Engine, Module};
|
||||||
|
/// use rhai::module_resolvers::{StaticModuleResolver, ModuleResolversCollection};
|
||||||
|
///
|
||||||
|
/// let mut collection = ModuleResolversCollection::new();
|
||||||
|
///
|
||||||
|
/// let resolver = StaticModuleResolver::new();
|
||||||
|
/// collection.push(resolver);
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
/// engine.set_module_resolver(Some(collection));
|
||||||
|
/// ```
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleResolversCollection {
|
||||||
|
/// Add a module keyed by its path.
|
||||||
|
pub fn push(&mut self, resolver: impl ModuleResolver + 'static) {
|
||||||
|
self.0.push(Box::new(resolver));
|
||||||
|
}
|
||||||
|
/// Get an iterator of all the module resolvers.
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &dyn ModuleResolver> {
|
||||||
|
self.0.iter().map(|v| v.as_ref())
|
||||||
|
}
|
||||||
|
/// Remove all module resolvers.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.0.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleResolver for ModuleResolversCollection {
|
||||||
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
engine: &Engine,
|
||||||
|
path: &str,
|
||||||
|
pos: Position,
|
||||||
|
) -> Result<Module, Box<EvalAltResult>> {
|
||||||
|
for resolver in self.0.iter() {
|
||||||
|
if let Ok(module) = resolver.resolve(engine, path, pos) {
|
||||||
|
return Ok(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()
|
||||||
|
}
|
||||||
|
}
|
209
src/module/resolvers/file.rs
Normal file
209
src/module/resolvers/file.rs
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
use crate::any::Dynamic;
|
||||||
|
use crate::engine::Engine;
|
||||||
|
use crate::module::{Module, ModuleResolver};
|
||||||
|
use crate::parser::{FnAccess, AST};
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::scope::Scope;
|
||||||
|
use crate::token::Position;
|
||||||
|
|
||||||
|
use crate::stdlib::{
|
||||||
|
boxed::Box,
|
||||||
|
collections::HashMap,
|
||||||
|
path::PathBuf,
|
||||||
|
string::{String, ToString},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
use crate::stdlib::cell::RefCell;
|
||||||
|
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
use crate::stdlib::sync::RwLock;
|
||||||
|
|
||||||
|
/// Module resolution service that loads module script files from the file system.
|
||||||
|
///
|
||||||
|
/// Script files are cached so they are are not reloaded and recompiled in subsequent requests.
|
||||||
|
///
|
||||||
|
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
|
||||||
|
/// allow specification of a base directory with module path used as a relative path offset
|
||||||
|
/// to the base directory. The script file is then forced to be in a specified extension
|
||||||
|
/// (default `.rhai`).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Engine;
|
||||||
|
/// use rhai::module_resolvers::FileModuleResolver;
|
||||||
|
///
|
||||||
|
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
|
||||||
|
/// // with file extension '.x'.
|
||||||
|
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
///
|
||||||
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FileModuleResolver {
|
||||||
|
path: PathBuf,
|
||||||
|
extension: String,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
cache: RefCell<HashMap<PathBuf, AST>>,
|
||||||
|
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
cache: RwLock<HashMap<PathBuf, AST>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FileModuleResolver {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new_with_path(PathBuf::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileModuleResolver {
|
||||||
|
/// Create a new `FileModuleResolver` with a specific base path.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Engine;
|
||||||
|
/// use rhai::module_resolvers::FileModuleResolver;
|
||||||
|
///
|
||||||
|
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
|
||||||
|
/// // with file extension '.rhai' (the default).
|
||||||
|
/// let resolver = FileModuleResolver::new_with_path("./scripts");
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
|
/// ```
|
||||||
|
pub fn new_with_path<P: Into<PathBuf>>(path: P) -> Self {
|
||||||
|
Self::new_with_path_and_extension(path, "rhai")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `FileModuleResolver` with a specific base path and file extension.
|
||||||
|
///
|
||||||
|
/// The default extension is `.rhai`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Engine;
|
||||||
|
/// use rhai::module_resolvers::FileModuleResolver;
|
||||||
|
///
|
||||||
|
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
|
||||||
|
/// // with file extension '.x'.
|
||||||
|
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
|
/// ```
|
||||||
|
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>(
|
||||||
|
path: P,
|
||||||
|
extension: E,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
path: path.into(),
|
||||||
|
extension: extension.into(),
|
||||||
|
cache: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `FileModuleResolver` with the current directory as base path.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Engine;
|
||||||
|
/// use rhai::module_resolvers::FileModuleResolver;
|
||||||
|
///
|
||||||
|
/// // Create a new 'FileModuleResolver' loading scripts from the current directory
|
||||||
|
/// // with file extension '.rhai' (the default).
|
||||||
|
/// let resolver = FileModuleResolver::new();
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
|
/// ```
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a `Module` from a file path.
|
||||||
|
pub fn create_module<P: Into<PathBuf>>(
|
||||||
|
&self,
|
||||||
|
engine: &Engine,
|
||||||
|
path: &str,
|
||||||
|
) -> Result<Module, Box<EvalAltResult>> {
|
||||||
|
self.resolve(engine, path, Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleResolver for FileModuleResolver {
|
||||||
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
engine: &Engine,
|
||||||
|
path: &str,
|
||||||
|
pos: Position,
|
||||||
|
) -> Result<Module, Box<EvalAltResult>> {
|
||||||
|
// Construct the script file path
|
||||||
|
let mut file_path = self.path.clone();
|
||||||
|
file_path.push(path);
|
||||||
|
file_path.set_extension(&self.extension); // Force extension
|
||||||
|
|
||||||
|
// See if it is cached
|
||||||
|
let exists = {
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
let c = self.cache.borrow();
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
let c = self.cache.read().unwrap();
|
||||||
|
|
||||||
|
c.contains_key(&file_path)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
// Load the file and compile it if not found
|
||||||
|
let ast = engine
|
||||||
|
.compile_file(file_path.clone())
|
||||||
|
.map_err(|err| err.new_position(pos))?;
|
||||||
|
|
||||||
|
// Put it into the cache
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
self.cache.borrow_mut().insert(file_path.clone(), ast);
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
self.cache.write().unwrap().insert(file_path.clone(), ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
let c = self.cache.borrow();
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
let c = self.cache.read().unwrap();
|
||||||
|
|
||||||
|
let ast = c.get(&file_path).unwrap();
|
||||||
|
|
||||||
|
let mut _module = Module::eval_ast_as_new(Scope::new(), ast, engine)?;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
ast.iter_functions(|access, name, num_args| match access {
|
||||||
|
FnAccess::Private => (),
|
||||||
|
FnAccess::Public => {
|
||||||
|
let fn_name = name.to_string();
|
||||||
|
let ast_lib = ast.lib().clone();
|
||||||
|
|
||||||
|
_module.set_raw_fn_as_scripted(
|
||||||
|
name,
|
||||||
|
num_args,
|
||||||
|
move |engine: &Engine, _, args: &mut [&mut Dynamic]| {
|
||||||
|
engine.call_fn_dynamic_raw(
|
||||||
|
&mut Scope::new(),
|
||||||
|
&ast_lib,
|
||||||
|
&fn_name,
|
||||||
|
&mut None,
|
||||||
|
args,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(_module)
|
||||||
|
}
|
||||||
|
}
|
189
src/module/resolvers/global_file.rs
Normal file
189
src/module/resolvers/global_file.rs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
use crate::engine::Engine;
|
||||||
|
use crate::module::{Module, ModuleResolver};
|
||||||
|
use crate::parser::AST;
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::token::Position;
|
||||||
|
|
||||||
|
use crate::stdlib::{boxed::Box, collections::HashMap, path::PathBuf, string::String};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
use crate::stdlib::cell::RefCell;
|
||||||
|
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
use crate::stdlib::sync::RwLock;
|
||||||
|
|
||||||
|
/// Module resolution service that loads module script files from the file system.
|
||||||
|
///
|
||||||
|
/// All functions in each module are treated as strictly independent and cannot refer to
|
||||||
|
/// other functions within the same module. Functions are searched in the _global_ namespace.
|
||||||
|
///
|
||||||
|
/// For simple utility libraries, this usually performs better than the full `FileModuleResolver`.
|
||||||
|
///
|
||||||
|
/// Script files are cached so they are are not reloaded and recompiled in subsequent requests.
|
||||||
|
///
|
||||||
|
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
|
||||||
|
/// allow specification of a base directory with module path used as a relative path offset
|
||||||
|
/// to the base directory. The script file is then forced to be in a specified extension
|
||||||
|
/// (default `.rhai`).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Engine;
|
||||||
|
/// use rhai::module_resolvers::GlobalFileModuleResolver;
|
||||||
|
///
|
||||||
|
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the 'scripts' subdirectory
|
||||||
|
/// // with file extension '.x'.
|
||||||
|
/// let resolver = GlobalFileModuleResolver::new_with_path_and_extension("./scripts", "x");
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
///
|
||||||
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GlobalFileModuleResolver {
|
||||||
|
path: PathBuf,
|
||||||
|
extension: String,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
cache: RefCell<HashMap<PathBuf, AST>>,
|
||||||
|
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
cache: RwLock<HashMap<PathBuf, AST>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GlobalFileModuleResolver {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new_with_path(PathBuf::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlobalFileModuleResolver {
|
||||||
|
/// Create a new `GlobalFileModuleResolver` with a specific base path.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Engine;
|
||||||
|
/// use rhai::module_resolvers::GlobalFileModuleResolver;
|
||||||
|
///
|
||||||
|
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the 'scripts' subdirectory
|
||||||
|
/// // with file extension '.rhai' (the default).
|
||||||
|
/// let resolver = GlobalFileModuleResolver::new_with_path("./scripts");
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
|
/// ```
|
||||||
|
pub fn new_with_path<P: Into<PathBuf>>(path: P) -> Self {
|
||||||
|
Self::new_with_path_and_extension(path, "rhai")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `GlobalFileModuleResolver` with a specific base path and file extension.
|
||||||
|
///
|
||||||
|
/// The default extension is `.rhai`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Engine;
|
||||||
|
/// use rhai::module_resolvers::GlobalFileModuleResolver;
|
||||||
|
///
|
||||||
|
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the 'scripts' subdirectory
|
||||||
|
/// // with file extension '.x'.
|
||||||
|
/// let resolver = GlobalFileModuleResolver::new_with_path_and_extension("./scripts", "x");
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
|
/// ```
|
||||||
|
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>(
|
||||||
|
path: P,
|
||||||
|
extension: E,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
path: path.into(),
|
||||||
|
extension: extension.into(),
|
||||||
|
cache: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `GlobalFileModuleResolver` with the current directory as base path.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Engine;
|
||||||
|
/// use rhai::module_resolvers::GlobalFileModuleResolver;
|
||||||
|
///
|
||||||
|
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the current directory
|
||||||
|
/// // with file extension '.rhai' (the default).
|
||||||
|
/// let resolver = GlobalFileModuleResolver::new();
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
|
/// ```
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a `Module` from a file path.
|
||||||
|
pub fn create_module<P: Into<PathBuf>>(
|
||||||
|
&self,
|
||||||
|
engine: &Engine,
|
||||||
|
path: &str,
|
||||||
|
) -> Result<Module, Box<EvalAltResult>> {
|
||||||
|
self.resolve(engine, path, Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleResolver for GlobalFileModuleResolver {
|
||||||
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
engine: &Engine,
|
||||||
|
path: &str,
|
||||||
|
pos: Position,
|
||||||
|
) -> Result<Module, Box<EvalAltResult>> {
|
||||||
|
// Construct the script file path
|
||||||
|
let mut file_path = self.path.clone();
|
||||||
|
file_path.push(path);
|
||||||
|
file_path.set_extension(&self.extension); // Force extension
|
||||||
|
|
||||||
|
let scope = Default::default();
|
||||||
|
|
||||||
|
// See if it is cached
|
||||||
|
let (module, ast) = {
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
let c = self.cache.borrow();
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
let c = self.cache.read().unwrap();
|
||||||
|
|
||||||
|
if let Some(ast) = c.get(&file_path) {
|
||||||
|
(
|
||||||
|
Module::eval_ast_as_new(scope, ast, engine)
|
||||||
|
.map_err(|err| err.new_position(pos))?,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Load the file and compile it if not found
|
||||||
|
let ast = engine
|
||||||
|
.compile_file(file_path.clone())
|
||||||
|
.map_err(|err| err.new_position(pos))?;
|
||||||
|
|
||||||
|
(
|
||||||
|
Module::eval_ast_as_new(scope, &ast, engine)
|
||||||
|
.map_err(|err| err.new_position(pos))?,
|
||||||
|
Some(ast),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(ast) = ast {
|
||||||
|
// Put it into the cache
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
self.cache.borrow_mut().insert(file_path, ast);
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
self.cache.write().unwrap().insert(file_path, ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(module)
|
||||||
|
}
|
||||||
|
}
|
40
src/module/resolvers/mod.rs
Normal file
40
src/module/resolvers/mod.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use crate::engine::Engine;
|
||||||
|
use crate::fn_native::SendSync;
|
||||||
|
use crate::module::Module;
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::token::Position;
|
||||||
|
|
||||||
|
use crate::stdlib::boxed::Box;
|
||||||
|
|
||||||
|
mod collection;
|
||||||
|
pub use collection::ModuleResolversCollection;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
mod file;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub use file::FileModuleResolver;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
mod global_file;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub use global_file::GlobalFileModuleResolver;
|
||||||
|
|
||||||
|
mod stat;
|
||||||
|
pub use stat::StaticModuleResolver;
|
||||||
|
|
||||||
|
/// Trait that encapsulates a module resolution service.
|
||||||
|
pub trait ModuleResolver: SendSync {
|
||||||
|
/// Resolve a module based on a path string.
|
||||||
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
engine: &Engine,
|
||||||
|
path: &str,
|
||||||
|
pos: Position,
|
||||||
|
) -> Result<Module, Box<EvalAltResult>>;
|
||||||
|
}
|
97
src/module/resolvers/stat.rs
Normal file
97
src/module/resolvers/stat.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
use crate::engine::Engine;
|
||||||
|
use crate::module::{Module, ModuleResolver};
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::token::Position;
|
||||||
|
|
||||||
|
use crate::stdlib::{boxed::Box, collections::HashMap, string::String};
|
||||||
|
|
||||||
|
/// Module resolution service that serves modules added into it.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::{Engine, Module};
|
||||||
|
/// use rhai::module_resolvers::StaticModuleResolver;
|
||||||
|
///
|
||||||
|
/// let mut resolver = StaticModuleResolver::new();
|
||||||
|
///
|
||||||
|
/// let module = Module::new();
|
||||||
|
/// resolver.insert("hello".to_string(), module);
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
///
|
||||||
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct StaticModuleResolver(HashMap<String, Module>);
|
||||||
|
|
||||||
|
impl StaticModuleResolver {
|
||||||
|
/// Create a new `StaticModuleResolver`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::{Engine, Module};
|
||||||
|
/// use rhai::module_resolvers::StaticModuleResolver;
|
||||||
|
///
|
||||||
|
/// let mut resolver = StaticModuleResolver::new();
|
||||||
|
///
|
||||||
|
/// let module = Module::new();
|
||||||
|
/// resolver.insert("hello", module);
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
|
/// ```
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticModuleResolver {
|
||||||
|
/// Add a module keyed by its path.
|
||||||
|
pub fn insert<S: Into<String>>(&mut self, path: S, mut module: Module) {
|
||||||
|
module.index_all_sub_modules();
|
||||||
|
self.0.insert(path.into(), module);
|
||||||
|
}
|
||||||
|
/// Remove a module given its path.
|
||||||
|
pub fn remove(&mut self, path: &str) -> Option<Module> {
|
||||||
|
self.0.remove(path)
|
||||||
|
}
|
||||||
|
/// Does the path exist?
|
||||||
|
pub fn contains_path(&self, path: &str) -> bool {
|
||||||
|
self.0.contains_key(path)
|
||||||
|
}
|
||||||
|
/// Get an iterator of all the modules.
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||||
|
self.0.iter().map(|(k, v)| (k.as_str(), v))
|
||||||
|
}
|
||||||
|
/// Get a mutable iterator of all the modules.
|
||||||
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Module)> {
|
||||||
|
self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
|
||||||
|
}
|
||||||
|
/// Get an iterator of all the module paths.
|
||||||
|
pub fn paths(&self) -> impl Iterator<Item = &str> {
|
||||||
|
self.0.keys().map(String::as_str)
|
||||||
|
}
|
||||||
|
/// Get an iterator of all the modules.
|
||||||
|
pub fn values(&self) -> impl Iterator<Item = &Module> {
|
||||||
|
self.0.values()
|
||||||
|
}
|
||||||
|
/// Get a mutable iterator of all the modules.
|
||||||
|
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Module> {
|
||||||
|
self.0.values_mut()
|
||||||
|
}
|
||||||
|
/// Remove all modules.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.0.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleResolver for StaticModuleResolver {
|
||||||
|
fn resolve(&self, _: &Engine, path: &str, pos: Position) -> Result<Module, Box<EvalAltResult>> {
|
||||||
|
self.0
|
||||||
|
.get(path)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(path.into(), pos).into())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user