Make module_resolver optional and remove NullModuleResolver.
This commit is contained in:
305
src/module.rs
305
src/module.rs
@@ -5,21 +5,35 @@ use crate::calc_fn_hash;
|
||||
use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib};
|
||||
use crate::parser::FnDef;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||
use crate::token::Position;
|
||||
use crate::token::Token;
|
||||
use crate::utils::StaticVec;
|
||||
|
||||
use crate::stdlib::{
|
||||
any::TypeId, collections::HashMap, fmt, iter::empty, mem, rc::Rc, string::String, sync::Arc,
|
||||
any::TypeId,
|
||||
collections::HashMap,
|
||||
fmt, mem,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::Rc,
|
||||
string::String,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// A trait that encapsulates a module resolution service.
|
||||
pub trait ModuleResolver {
|
||||
/// Resolve a module based on a path string.
|
||||
fn resolve(&self, engine: &Engine, path: &str) -> Result<Module, Box<EvalAltResult>>;
|
||||
fn resolve(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> Result<Module, Box<EvalAltResult>>;
|
||||
}
|
||||
|
||||
/// Return type of module-level Rust function.
|
||||
type FuncReturn<T> = Result<T, Box<EvalAltResult>>;
|
||||
|
||||
/// An imported module, which may contain variables, sub-modules,
|
||||
/// external Rust functions, and script-defined functions.
|
||||
///
|
||||
@@ -158,7 +172,6 @@ impl Module {
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
self.functions.insert(hash, Rc::new(func));
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
self.functions.insert(hash, Arc::new(func));
|
||||
|
||||
@@ -171,14 +184,16 @@ impl Module {
|
||||
pub fn set_fn_0<T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn() -> Result<T, Box<EvalAltResult>> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn() -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &mut FnCallArgs, _: Position| func().map(|v| v.into());
|
||||
self.set_fn(fn_name, &[], Box::new(f))
|
||||
let f = move |_: &mut FnCallArgs, pos| {
|
||||
func()
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
let arg_types = &[];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
||||
@@ -187,16 +202,16 @@ impl Module {
|
||||
pub fn set_fn_1<A: Variant + Clone, T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
func(mem::take(args[0]).cast::<A>()).map(|v| v.into())
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
func(mem::take(args[0]).cast::<A>())
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
self.set_fn(fn_name, &[TypeId::of::<A>()], Box::new(f))
|
||||
let arg_types = &[TypeId::of::<A>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
||||
@@ -205,16 +220,16 @@ impl Module {
|
||||
pub fn set_fn_1_mut<A: Variant + Clone, T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
func(args[0].downcast_mut::<A>().unwrap()).map(|v| v.into())
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
func(args[0].downcast_mut::<A>().unwrap())
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
self.set_fn(fn_name, &[TypeId::of::<A>()], Box::new(f))
|
||||
let arg_types = &[TypeId::of::<A>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
||||
@@ -223,23 +238,19 @@ impl Module {
|
||||
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
let a = mem::take(args[0]).cast::<A>();
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
|
||||
func(a, b).map(|v| v.into())
|
||||
func(a, b)
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
self.set_fn(
|
||||
fn_name,
|
||||
&[TypeId::of::<A>(), TypeId::of::<B>()],
|
||||
Box::new(f),
|
||||
)
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
||||
@@ -249,24 +260,19 @@ impl Module {
|
||||
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
let a = args[0].downcast_mut::<A>().unwrap();
|
||||
|
||||
func(a, b).map(|v| v.into())
|
||||
func(a, b)
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
self.set_fn(
|
||||
fn_name,
|
||||
&[TypeId::of::<A>(), TypeId::of::<B>()],
|
||||
Box::new(f),
|
||||
)
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
||||
@@ -280,24 +286,20 @@ impl Module {
|
||||
>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
let a = mem::take(args[0]).cast::<A>();
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
let c = mem::take(args[2]).cast::<C>();
|
||||
|
||||
func(a, b, c).map(|v| v.into())
|
||||
func(a, b, c)
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
self.set_fn(
|
||||
fn_name,
|
||||
&[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()],
|
||||
Box::new(f),
|
||||
)
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
||||
@@ -312,25 +314,20 @@ impl Module {
|
||||
>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
let c = mem::take(args[2]).cast::<C>();
|
||||
let a = args[0].downcast_mut::<A>().unwrap();
|
||||
|
||||
func(a, b, c).map(|v| v.into())
|
||||
func(a, b, c)
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
self.set_fn(
|
||||
fn_name,
|
||||
&[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()],
|
||||
Box::new(f),
|
||||
)
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Get a Rust function.
|
||||
@@ -388,22 +385,40 @@ impl Module {
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-export module resolvers.
|
||||
pub mod resolvers {
|
||||
use super::*;
|
||||
pub use super::file::FileModuleResolver;
|
||||
pub use super::stat::StaticModuleResolver;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
/// Script file-based module resolver.
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
mod file {
|
||||
use super::*;
|
||||
use crate::stdlib::path::PathBuf;
|
||||
|
||||
/// A module resolution service that loads module script files (assumed `.rhai` extension).
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct FileModuleResolver(PathBuf);
|
||||
/// A module resolution service that loads module script files from the file system.
|
||||
///
|
||||
/// 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`).
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub struct FileModuleResolver {
|
||||
path: PathBuf,
|
||||
extension: String,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
impl FileModuleResolver {
|
||||
/// Create a new `FileModuleResolver` with a specific base path.
|
||||
pub fn new_with_path(path: PathBuf) -> Self {
|
||||
Self(path)
|
||||
Self::new_with_path_and_extension(path, "rhai".to_string())
|
||||
}
|
||||
/// Create a new `FileModuleResolver` with a specific base path and file extension.
|
||||
///
|
||||
/// The default extension is `.rhai`.
|
||||
pub fn new_with_path_and_extension(path: PathBuf, extension: String) -> Self {
|
||||
Self { path, extension }
|
||||
}
|
||||
/// Create a new `FileModuleResolver` with the current directory as base path.
|
||||
pub fn new() -> Self {
|
||||
@@ -411,54 +426,63 @@ pub mod resolvers {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
impl Default for FileModuleResolver {
|
||||
fn default() -> Self {
|
||||
Self::new_with_path(".".into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
impl ModuleResolver for FileModuleResolver {
|
||||
fn resolve(&self, engine: &Engine, path: &str) -> Result<Module, Box<EvalAltResult>> {
|
||||
// Load the script file (attaching `.rhai`)
|
||||
let mut file_path = self.0.clone();
|
||||
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("rhai");
|
||||
file_path.set_extension(&self.extension); // Force extension
|
||||
|
||||
// Compile it
|
||||
let ast = engine.compile_file(file_path)?;
|
||||
let ast = engine
|
||||
.compile_file(file_path)
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))?;
|
||||
|
||||
// Use new scope
|
||||
let mut scope = Scope::new();
|
||||
|
||||
// Run the script
|
||||
engine.eval_ast_with_scope_raw(&mut scope, &ast)?;
|
||||
engine
|
||||
.eval_ast_with_scope_raw(&mut scope, &ast)
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))?;
|
||||
|
||||
// Create new module
|
||||
let mut module = Module::new();
|
||||
|
||||
// Variables left in the scope become module variables
|
||||
for entry in scope.into_iter() {
|
||||
match entry.typ {
|
||||
ScopeEntryType::Normal | ScopeEntryType::Constant => {
|
||||
module
|
||||
.variables
|
||||
.insert(entry.name.into_owned(), entry.value);
|
||||
scope.into_iter().for_each(
|
||||
|ScopeEntry {
|
||||
name, typ, value, ..
|
||||
}| {
|
||||
match typ {
|
||||
// Variables left in the scope become module variables
|
||||
ScopeEntryType::Normal | ScopeEntryType::Constant => {
|
||||
module.variables.insert(name.into_owned(), value);
|
||||
}
|
||||
// Modules left in the scope become sub-modules
|
||||
ScopeEntryType::Module => {
|
||||
module
|
||||
.modules
|
||||
.insert(name.into_owned(), value.cast::<Module>());
|
||||
}
|
||||
}
|
||||
ScopeEntryType::Module => {
|
||||
module
|
||||
.modules
|
||||
.insert(entry.name.into_owned(), entry.value.cast::<Module>());
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
module.fn_lib = FunctionsLib::new().merge(ast.fn_lib());
|
||||
module.fn_lib = module.fn_lib.merge(ast.fn_lib());
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Static module resolver.
|
||||
mod stat {
|
||||
use super::*;
|
||||
|
||||
/// A module resolution service that serves modules added into it.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -469,40 +493,33 @@ pub mod resolvers {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
/// Add a named module.
|
||||
pub fn add_module(&mut self, name: &str, module: Module) {
|
||||
self.0.insert(name.to_string(), module);
|
||||
}
|
||||
|
||||
impl Deref for StaticModuleResolver {
|
||||
type Target = HashMap<String, Module>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for StaticModuleResolver {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleResolver for StaticModuleResolver {
|
||||
fn resolve(&self, _: &Engine, path: &str) -> Result<Module, Box<EvalAltResult>> {
|
||||
self.0.get(path).cloned().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorModuleNotFound(
|
||||
path.to_string(),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A module resolution service that always returns a not-found error.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy, Default)]
|
||||
pub struct NullModuleResolver;
|
||||
|
||||
impl NullModuleResolver {
|
||||
/// Create a new `NullModuleResolver`.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleResolver for NullModuleResolver {
|
||||
fn resolve(&self, _: &Engine, path: &str) -> Result<Module, Box<EvalAltResult>> {
|
||||
Err(Box::new(EvalAltResult::ErrorModuleNotFound(
|
||||
path.to_string(),
|
||||
Position::none(),
|
||||
)))
|
||||
fn resolve(
|
||||
&self,
|
||||
_: &Engine,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> Result<Module, Box<EvalAltResult>> {
|
||||
self.0
|
||||
.get(path)
|
||||
.cloned()
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(path.to_string(), pos)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user