rhai/src/module.rs

1945 lines
64 KiB
Rust
Raw Normal View History

//! Module defining external-loaded modules for Rhai.
2020-05-05 09:00:10 +02:00
use crate::any::{Dynamic, Variant};
2020-05-05 11:51:40 +02:00
use crate::calc_fn_hash;
2020-07-26 09:53:22 +02:00
use crate::engine::Engine;
use crate::fn_native::{CallableFunction as Func, FnCallArgs, IteratorFn, SendSync};
use crate::fn_register::by_value as cast_arg;
2020-09-24 17:32:54 +02:00
use crate::parser::{FnAccess, FnAccess::Public};
2020-05-05 09:00:10 +02:00
use crate::result::EvalAltResult;
use crate::token::{Position, Token};
use crate::utils::{ImmutableString, StaticVec, StraightHasherBuilder};
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_function"))]
2020-09-24 17:32:54 +02:00
use crate::{fn_native::Shared, parser::ScriptFnDef};
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_module"))]
use crate::{
engine::Imports,
parser::AST,
scope::{Entry as ScopeEntry, Scope},
};
#[cfg(not(feature = "no_index"))]
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_object"))]
use crate::engine::{make_getter, make_setter, Map};
2020-07-26 09:53:22 +02:00
2020-05-05 17:57:25 +02:00
use crate::stdlib::{
any::TypeId,
boxed::Box,
collections::HashMap,
2020-06-26 17:24:47 +02:00
fmt, format,
iter::empty,
num::NonZeroUsize,
ops::{Deref, DerefMut},
string::{String, ToString},
vec::Vec,
2020-05-05 17:57:25 +02:00
};
2020-07-13 13:38:50 +02:00
#[cfg(not(feature = "no_std"))]
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "sync"))]
use crate::stdlib::cell::RefCell;
#[cfg(not(feature = "no_std"))]
#[cfg(not(feature = "no_module"))]
2020-07-20 17:23:12 +02:00
#[cfg(feature = "sync")]
2020-07-13 13:38:50 +02:00
use crate::stdlib::sync::RwLock;
/// Return type of module-level Rust function.
2020-05-13 13:21:42 +02:00
pub type FuncReturn<T> = Result<T, Box<EvalAltResult>>;
2020-05-05 09:00:10 +02:00
/// An imported module, which may contain variables, sub-modules,
/// external Rust functions, and script-defined functions.
///
/// Not available under the `no_module` feature.
#[derive(Default)]
2020-05-05 09:00:10 +02:00
pub struct Module {
/// Sub-modules.
modules: HashMap<String, Module>,
/// Module variables.
2020-05-05 09:00:10 +02:00
variables: HashMap<String, Dynamic>,
2020-05-05 17:57:25 +02:00
/// Flattened collection of all module variables, including those in sub-modules.
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>,
2020-05-05 09:00:10 +02:00
/// External Rust functions.
2020-09-24 16:50:28 +02:00
functions: HashMap<
u64,
(String, FnAccess, usize, Option<StaticVec<TypeId>>, Func),
StraightHasherBuilder,
>,
2020-05-05 17:57:25 +02:00
2020-05-13 13:21:42 +02:00
/// Iterator functions, keyed by the type producing the iterator.
2020-05-20 13:27:23 +02:00
type_iterators: HashMap<TypeId, IteratorFn>,
/// Flattened collection of all external Rust functions, native or scripted,
/// including those in sub-modules.
2020-07-07 16:59:23 +02:00
all_functions: HashMap<u64, Func, StraightHasherBuilder>,
/// Is the module indexed?
indexed: bool,
2020-05-05 09:00:10 +02:00
}
impl fmt::Debug for Module {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Module(\n modules: {}\n vars: {}\n functions: {}\n)",
self.modules
.keys()
.map(|k| k.as_str())
.collect::<Vec<_>>()
.join(", "),
self.variables
.iter()
.map(|(k, v)| format!("{}={:?}", k, v))
.collect::<Vec<_>>()
.join(", "),
self.functions
.values()
2020-09-24 16:50:28 +02:00
.map(|(_, _, _, _, f)| f.to_string())
.collect::<Vec<_>>()
.join(", "),
2020-05-05 09:00:10 +02:00
)
}
}
impl Clone for Module {
fn clone(&self) -> Self {
// Only clone the index at the top level
Self {
all_variables: self.all_variables.clone(),
all_functions: self.all_functions.clone(),
indexed: self.indexed,
..self.do_clone(false)
}
}
}
2020-07-06 07:01:57 +02:00
impl AsRef<Module> for Module {
fn as_ref(&self) -> &Module {
self
}
}
impl Module {
/// Create a new module.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ```
pub fn new() -> Self {
2020-05-05 09:00:10 +02:00
Default::default()
}
/// Create a new module with a specified capacity for native Rust functions.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ```
pub fn new_with_capacity(capacity: usize) -> Self {
Self {
functions: HashMap::with_capacity_and_hasher(capacity, StraightHasherBuilder),
..Default::default()
}
}
/// Clone the module, optionally skipping the index.
fn do_clone(&self, clone_index: bool) -> Self {
Self {
modules: if clone_index {
self.modules.clone()
} else {
self.modules
.iter()
.map(|(k, m)| (k.clone(), m.do_clone(clone_index)))
.collect()
},
variables: self.variables.clone(),
functions: self.functions.clone(),
type_iterators: self.type_iterators.clone(),
..Default::default()
}
}
2020-05-05 09:00:10 +02:00
/// Does a variable exist in the module?
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert!(module.contains_var("answer"));
/// ```
2020-05-05 11:51:40 +02:00
pub fn contains_var(&self, name: &str) -> bool {
2020-05-05 09:00:10 +02:00
self.variables.contains_key(name)
}
2020-05-05 09:00:10 +02:00
/// Get the value of a module variable.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ```
2020-05-05 11:51:40 +02:00
pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
self.get_var(name).and_then(Dynamic::try_cast::<T>)
2020-05-05 09:00:10 +02:00
}
/// Get a module variable as a `Dynamic`.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var("answer").unwrap().cast::<i64>(), 42);
/// ```
2020-05-05 11:51:40 +02:00
pub fn get_var(&self, name: &str) -> Option<Dynamic> {
2020-05-05 09:00:10 +02:00
self.variables.get(name).cloned()
}
/// Set a variable into the module.
///
/// If there is an existing variable of the same name, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ```
2020-07-12 05:46:53 +02:00
pub fn set_var(&mut self, name: impl Into<String>, value: impl Variant + Clone) -> &mut Self {
2020-05-13 13:21:42 +02:00
self.variables.insert(name.into(), Dynamic::from(value));
self.indexed = false;
2020-07-12 05:46:53 +02:00
self
2020-05-05 09:00:10 +02:00
}
/// Get a mutable reference to a modules-qualified variable.
2020-06-28 09:49:24 +02:00
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
2020-05-05 11:51:40 +02:00
pub(crate) fn get_qualified_var_mut(
2020-05-05 09:00:10 +02:00
&mut self,
2020-05-11 17:48:50 +02:00
hash_var: u64,
2020-05-05 09:00:10 +02:00
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
2020-09-11 16:32:59 +02:00
if hash_var == 0 {
Err(EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into())
} else {
self.all_variables.get_mut(&hash_var).ok_or_else(|| {
EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into()
})
}
2020-05-05 09:00:10 +02:00
}
/// Set a script-defined function into the module.
///
/// If there is an existing function of the same name and number of arguments, it is replaced.
2020-09-24 17:32:54 +02:00
#[cfg(not(feature = "no_function"))]
2020-09-24 16:50:28 +02:00
pub(crate) fn set_script_fn(&mut self, fn_def: ScriptFnDef) -> u64 {
// None + function name + number of arguments.
2020-09-24 16:50:28 +02:00
let num_params = fn_def.params.len();
let hash_script = calc_fn_hash(empty(), &fn_def.name, num_params, empty());
self.functions.insert(
2020-06-11 12:13:33 +02:00
hash_script,
(
fn_def.name.to_string(),
fn_def.access,
2020-09-24 16:50:28 +02:00
num_params,
None,
fn_def.into(),
),
);
self.indexed = false;
2020-09-24 16:50:28 +02:00
hash_script
}
2020-05-05 09:00:10 +02:00
/// Does a sub-module exist in the module?
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.contains_sub_module("question"));
/// ```
2020-05-05 09:00:10 +02:00
pub fn contains_sub_module(&self, name: &str) -> bool {
self.modules.contains_key(name)
}
/// Get a sub-module.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.get_sub_module("question").is_some());
/// ```
2020-05-05 09:00:10 +02:00
pub fn get_sub_module(&self, name: &str) -> Option<&Module> {
self.modules.get(name)
}
/// Get a mutable reference to a sub-module.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.get_sub_module_mut("question").is_some());
/// ```
2020-05-05 09:00:10 +02:00
pub fn get_sub_module_mut(&mut self, name: &str) -> Option<&mut Module> {
self.modules.get_mut(name)
}
/// Set a sub-module into the module.
///
/// If there is an existing sub-module of the same name, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.get_sub_module("question").is_some());
/// ```
2020-07-12 05:46:53 +02:00
pub fn set_sub_module(&mut self, name: impl Into<String>, sub_module: Module) -> &mut Self {
2020-05-05 09:00:10 +02:00
self.modules.insert(name.into(), sub_module.into());
self.indexed = false;
2020-07-12 05:46:53 +02:00
self
2020-05-05 09:00:10 +02:00
}
2020-05-05 11:51:40 +02:00
/// Does the particular Rust function exist in the module?
///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
/// It is also returned by the `set_fn_XXX` calls.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
2020-05-09 18:19:13 +02:00
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn contains_fn(&self, hash_fn: u64, public_only: bool) -> bool {
2020-09-11 16:32:59 +02:00
if hash_fn == 0 {
false
} else if public_only {
self.functions
.get(&hash_fn)
2020-09-24 16:50:28 +02:00
.map(|(_, access, _, _, _)| match access {
FnAccess::Public => true,
FnAccess::Private => false,
})
.unwrap_or(false)
} else {
self.functions.contains_key(&hash_fn)
}
2020-05-05 11:51:40 +02:00
}
/// Set a Rust function into the module, returning a hash key.
///
/// If there is an existing Rust function of the same hash, it is replaced.
2020-07-11 09:09:17 +02:00
///
/// ## WARNING - Low Level API
///
/// This function is very low level.
pub fn set_fn(
2020-05-22 15:50:24 +02:00
&mut self,
name: impl Into<String>,
access: FnAccess,
2020-07-07 16:59:23 +02:00
arg_types: &[TypeId],
func: Func,
2020-05-22 15:50:24 +02:00
) -> u64 {
let name = name.into();
2020-09-11 16:32:59 +02:00
let args_len = if arg_types.is_empty() {
// Distinguish between a script function and a function with no parameters
usize::MAX
} else {
arg_types.len()
};
let params = arg_types
.into_iter()
.cloned()
.map(|id| {
if id == TypeId::of::<&str>() {
TypeId::of::<ImmutableString>()
} else if id == TypeId::of::<String>() {
TypeId::of::<ImmutableString>()
} else {
id
}
})
.collect();
2020-05-05 17:57:25 +02:00
let hash_fn = calc_fn_hash(empty(), &name, args_len, arg_types.iter().cloned());
2020-05-17 16:19:49 +02:00
self.functions
2020-09-24 16:50:28 +02:00
.insert(hash_fn, (name, access, args_len, Some(params), func.into()));
2020-05-05 17:57:25 +02:00
self.indexed = false;
2020-05-11 17:48:50 +02:00
hash_fn
2020-05-05 11:51:40 +02:00
}
2020-07-07 16:59:23 +02:00
/// Set a Rust function taking a reference to the scripting `Engine`, the current set of functions,
/// plus a list of mutable `Dynamic` references into the module, returning a hash key.
///
/// Use this to register a built-in function which must reference settings on the scripting
2020-07-07 16:59:23 +02:00
/// `Engine` (e.g. to prevent growing an array beyond the allowed maximum size), or to call a
/// script-defined function in the current evaluation context.
///
/// If there is a similar existing Rust function, it is replaced.
2020-07-07 16:59:23 +02:00
///
/// ## WARNING - Low Level API
///
/// This function is very low level.
///
/// A list of `TypeId`'s is taken as the argument types.
///
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`,
/// which is guaranteed to contain enough arguments of the correct types.
///
/// To access a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
///
/// To access a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
/// Notice that this will _consume_ the argument, replacing it with `()`.
///
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_raw_fn("double_or_not",
/// // Pass parameter types via a slice with TypeId's
2020-07-28 04:25:57 +02:00
/// &[std::any::TypeId::of::<i64>(), std::any::TypeId::of::<bool>()],
2020-07-07 16:59:23 +02:00
/// // Fixed closure signature
/// |engine, lib, args| {
/// // 'args' is guaranteed to be the right length and of the correct types
///
/// // Get the second parameter by 'consuming' it
/// let double = std::mem::take(args[1]).cast::<bool>();
/// // Since it is a primary type, it can also be cheaply copied
/// let double = args[1].clone().cast::<bool>();
/// // Get a mutable reference to the first argument.
/// let mut x = args[0].write_lock::<i64>().unwrap();
2020-07-07 16:59:23 +02:00
///
/// let orig = *x;
///
/// if double {
/// *x *= 2; // the first argument can be mutated
/// }
///
/// Ok(orig) // return Result<T, Box<EvalAltResult>>
/// });
///
/// assert!(module.contains_fn(hash, true));
2020-07-07 16:59:23 +02:00
/// ```
pub fn set_raw_fn<T: Variant + Clone>(
&mut self,
name: impl Into<String>,
2020-07-07 16:59:23 +02:00
arg_types: &[TypeId],
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
) -> u64 {
let f = move |engine: &Engine, lib: &Module, args: &mut FnCallArgs| {
func(engine, lib, args).map(Dynamic::from)
};
2020-07-07 16:59:23 +02:00
self.set_fn(name, Public, arg_types, Func::from_method(Box::new(f)))
}
2020-09-24 16:50:28 +02:00
/// Set a raw function but with a signature that is a scripted function, but the implementation is in Rust.
2020-09-24 17:32:54 +02:00
#[cfg(not(feature = "no_function"))]
2020-09-24 16:50:28 +02:00
pub(crate) fn set_raw_fn_as_scripted(
&mut self,
name: impl Into<String>,
num_args: usize,
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<Dynamic> + SendSync + 'static,
) -> u64 {
// None + function name + number of arguments.
let name = name.into();
let hash_script = calc_fn_hash(empty(), &name, num_args, empty());
let f = move |engine: &Engine, lib: &Module, args: &mut FnCallArgs| func(engine, lib, args);
self.functions.insert(
hash_script,
(
name,
FnAccess::Public,
num_args,
None,
Func::from_pure(Box::new(f)),
),
);
self.indexed = false;
hash_script
}
2020-05-05 11:51:40 +02:00
/// Set a Rust function taking no parameters into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
2020-05-09 18:19:13 +02:00
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
/// assert!(module.contains_fn(hash, true));
/// ```
2020-05-22 15:50:24 +02:00
pub fn set_fn_0<T: Variant + Clone>(
2020-05-05 11:51:40 +02:00
&mut self,
2020-05-22 15:50:24 +02:00
name: impl Into<String>,
func: impl Fn() -> FuncReturn<T> + SendSync + 'static,
2020-05-05 11:51:40 +02:00
) -> u64 {
let f = move |_: &Engine, _: &Module, _: &mut FnCallArgs| func().map(Dynamic::from);
2020-07-07 16:59:23 +02:00
let arg_types = [];
self.set_fn(name, Public, &arg_types, Func::from_pure(Box::new(f)))
2020-05-05 11:51:40 +02:00
}
/// Set a Rust function taking one parameter into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
2020-05-09 18:19:13 +02:00
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
/// assert!(module.contains_fn(hash, true));
/// ```
2020-05-22 15:50:24 +02:00
pub fn set_fn_1<A: Variant + Clone, T: Variant + Clone>(
2020-05-05 11:51:40 +02:00
&mut self,
2020-05-22 15:50:24 +02:00
name: impl Into<String>,
func: impl Fn(A) -> FuncReturn<T> + SendSync + 'static,
2020-05-05 11:51:40 +02:00
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
func(cast_arg::<A>(&mut args[0])).map(Dynamic::from)
};
2020-07-07 16:59:23 +02:00
let arg_types = [TypeId::of::<A>()];
self.set_fn(name, Public, &arg_types, Func::from_pure(Box::new(f)))
2020-05-05 11:51:40 +02:00
}
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
2020-05-09 18:19:13 +02:00
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
/// assert!(module.contains_fn(hash, true));
/// ```
2020-05-22 15:50:24 +02:00
pub fn set_fn_1_mut<A: Variant + Clone, T: Variant + Clone>(
2020-05-05 11:51:40 +02:00
&mut self,
2020-05-22 15:50:24 +02:00
name: impl Into<String>,
func: impl Fn(&mut A) -> FuncReturn<T> + SendSync + 'static,
2020-05-05 11:51:40 +02:00
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
func(&mut args[0].write_lock::<A>().unwrap()).map(Dynamic::from)
2020-05-05 11:51:40 +02:00
};
2020-07-07 16:59:23 +02:00
let arg_types = [TypeId::of::<A>()];
self.set_fn(name, Public, &arg_types, Func::from_method(Box::new(f)))
2020-05-05 11:51:40 +02:00
}
/// Set a Rust getter function taking one mutable parameter, returning a hash key.
///
/// If there is a similar existing Rust getter function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_getter_fn("value", |x: &mut i64| { Ok(*x) });
/// assert!(module.contains_fn(hash, true));
/// ```
#[cfg(not(feature = "no_object"))]
pub fn set_getter_fn<A: Variant + Clone, T: Variant + Clone>(
&mut self,
name: impl Into<String>,
func: impl Fn(&mut A) -> FuncReturn<T> + SendSync + 'static,
) -> u64 {
self.set_fn_1_mut(make_getter(&name.into()), func)
}
2020-05-05 11:51:40 +02:00
/// Set a Rust function taking two parameters into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
///
/// let mut module = Module::new();
/// let hash = module.set_fn_2("calc", |x: i64, y: ImmutableString| {
/// Ok(x + y.len() as i64)
2020-05-09 18:19:13 +02:00
/// });
/// assert!(module.contains_fn(hash, true));
/// ```
2020-05-22 15:50:24 +02:00
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
2020-05-05 11:51:40 +02:00
&mut self,
2020-05-22 15:50:24 +02:00
name: impl Into<String>,
func: impl Fn(A, B) -> FuncReturn<T> + SendSync + 'static,
2020-05-05 11:51:40 +02:00
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let a = cast_arg::<A>(&mut args[0]);
let b = cast_arg::<B>(&mut args[1]);
2020-05-05 11:51:40 +02:00
func(a, b).map(Dynamic::from)
2020-05-05 11:51:40 +02:00
};
2020-07-07 16:59:23 +02:00
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()];
self.set_fn(name, Public, &arg_types, Func::from_pure(Box::new(f)))
2020-05-05 11:51:40 +02:00
}
/// Set a Rust function taking two parameters (the first one mutable) into the module,
/// returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
///
/// let mut module = Module::new();
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: ImmutableString| {
/// *x += y.len() as i64; Ok(*x)
2020-05-09 18:19:13 +02:00
/// });
/// assert!(module.contains_fn(hash, true));
/// ```
2020-05-22 15:50:24 +02:00
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
2020-05-05 11:51:40 +02:00
&mut self,
2020-05-22 15:50:24 +02:00
name: impl Into<String>,
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
2020-05-05 11:51:40 +02:00
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let b = cast_arg::<B>(&mut args[1]);
2020-08-11 07:46:09 +02:00
let a = &mut args[0].write_lock::<A>().unwrap();
2020-05-05 11:51:40 +02:00
2020-08-11 07:46:09 +02:00
func(a, b).map(Dynamic::from)
2020-05-05 11:51:40 +02:00
};
2020-07-07 16:59:23 +02:00
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()];
self.set_fn(name, Public, &arg_types, Func::from_method(Box::new(f)))
2020-05-05 11:51:40 +02:00
}
/// Set a Rust setter function taking two parameters (the first one mutable) into the module,
/// returning a hash key.
///
/// If there is a similar existing setter Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
///
/// let mut module = Module::new();
/// let hash = module.set_setter_fn("value", |x: &mut i64, y: ImmutableString| {
/// *x = y.len() as i64;
/// Ok(())
/// });
/// assert!(module.contains_fn(hash, true));
/// ```
#[cfg(not(feature = "no_object"))]
pub fn set_setter_fn<A: Variant + Clone, B: Variant + Clone>(
&mut self,
name: impl Into<String>,
func: impl Fn(&mut A, B) -> FuncReturn<()> + SendSync + 'static,
) -> u64 {
self.set_fn_2_mut(make_setter(&name.into()), func)
}
2020-06-06 07:06:00 +02:00
/// Set a Rust index getter taking two parameters (the first one mutable) into the module,
/// returning a hash key.
///
/// If there is a similar existing setter Rust function, it is replaced.
///
/// # Panics
///
/// Panics if the type is `Array` or `Map`.
/// Indexers for arrays, object maps and strings cannot be registered.
///
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
///
/// let mut module = Module::new();
2020-06-06 07:06:00 +02:00
/// let hash = module.set_indexer_get_fn(|x: &mut i64, y: ImmutableString| {
/// Ok(*x + y.len() as i64)
/// });
/// assert!(module.contains_fn(hash, true));
/// ```
#[cfg(not(feature = "no_index"))]
2020-06-06 07:06:00 +02:00
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
) -> u64 {
if TypeId::of::<A>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
}
#[cfg(not(feature = "no_object"))]
if TypeId::of::<A>() == TypeId::of::<Map>() {
panic!("Cannot register indexer for object maps.");
}
if TypeId::of::<A>() == TypeId::of::<String>()
|| TypeId::of::<A>() == TypeId::of::<&str>()
|| TypeId::of::<A>() == TypeId::of::<ImmutableString>()
{
panic!("Cannot register indexer for strings.");
}
2020-06-25 05:07:46 +02:00
self.set_fn_2_mut(FN_IDX_GET, func)
}
2020-05-05 11:51:40 +02:00
/// Set a Rust function taking three parameters into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
///
/// let mut module = Module::new();
/// let hash = module.set_fn_3("calc", |x: i64, y: ImmutableString, z: i64| {
/// Ok(x + y.len() as i64 + z)
2020-05-09 18:19:13 +02:00
/// });
/// assert!(module.contains_fn(hash, true));
/// ```
2020-05-05 11:51:40 +02:00
pub fn set_fn_3<
A: Variant + Clone,
B: Variant + Clone,
C: Variant + Clone,
2020-05-13 13:21:42 +02:00
T: Variant + Clone,
2020-05-05 11:51:40 +02:00
>(
&mut self,
2020-05-22 15:50:24 +02:00
name: impl Into<String>,
func: impl Fn(A, B, C) -> FuncReturn<T> + SendSync + 'static,
2020-05-05 11:51:40 +02:00
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let a = cast_arg::<A>(&mut args[0]);
let b = cast_arg::<B>(&mut args[1]);
let c = cast_arg::<C>(&mut args[2]);
2020-05-05 11:51:40 +02:00
func(a, b, c).map(Dynamic::from)
2020-05-05 11:51:40 +02:00
};
2020-07-07 16:59:23 +02:00
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
self.set_fn(name, Public, &arg_types, Func::from_pure(Box::new(f)))
2020-05-05 11:51:40 +02:00
}
/// Set a Rust function taking three parameters (the first one mutable) into the module,
/// returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
///
/// let mut module = Module::new();
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: ImmutableString, z: i64| {
/// *x += y.len() as i64 + z; Ok(*x)
2020-05-09 18:19:13 +02:00
/// });
/// assert!(module.contains_fn(hash, true));
/// ```
2020-05-05 11:51:40 +02:00
pub fn set_fn_3_mut<
A: Variant + Clone,
B: Variant + Clone,
C: Variant + Clone,
2020-05-13 13:21:42 +02:00
T: Variant + Clone,
2020-05-05 11:51:40 +02:00
>(
&mut self,
2020-05-22 15:50:24 +02:00
name: impl Into<String>,
func: impl Fn(&mut A, B, C) -> FuncReturn<T> + SendSync + 'static,
2020-05-05 11:51:40 +02:00
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let b = cast_arg::<B>(&mut args[2]);
let c = cast_arg::<C>(&mut args[3]);
2020-08-11 07:46:09 +02:00
let a = &mut args[0].write_lock::<A>().unwrap();
2020-05-05 11:51:40 +02:00
2020-08-11 07:46:09 +02:00
func(a, b, c).map(Dynamic::from)
2020-05-05 11:51:40 +02:00
};
2020-07-07 16:59:23 +02:00
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
self.set_fn(name, Public, &arg_types, Func::from_method(Box::new(f)))
2020-05-05 11:51:40 +02:00
}
2020-06-06 07:06:00 +02:00
/// Set a Rust index setter taking three parameters (the first one mutable) into the module,
/// returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Panics
///
/// Panics if the type is `Array` or `Map`.
/// Indexers for arrays, object maps and strings cannot be registered.
///
2020-06-06 07:06:00 +02:00
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
///
/// let mut module = Module::new();
/// let hash = module.set_indexer_set_fn(|x: &mut i64, y: ImmutableString, value: i64| {
/// *x = y.len() as i64 + value;
/// Ok(())
/// });
/// assert!(module.contains_fn(hash, true));
2020-06-06 07:06:00 +02:00
/// ```
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_index"))]
2020-07-24 17:16:54 +02:00
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
2020-06-06 07:06:00 +02:00
&mut self,
2020-07-24 17:16:54 +02:00
func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static,
2020-06-06 07:06:00 +02:00
) -> u64 {
if TypeId::of::<A>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
}
#[cfg(not(feature = "no_object"))]
if TypeId::of::<A>() == TypeId::of::<Map>() {
panic!("Cannot register indexer for object maps.");
}
if TypeId::of::<A>() == TypeId::of::<String>()
|| TypeId::of::<A>() == TypeId::of::<&str>()
|| TypeId::of::<A>() == TypeId::of::<ImmutableString>()
{
panic!("Cannot register indexer for strings.");
}
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let b = cast_arg::<B>(&mut args[1]);
let c = cast_arg::<C>(&mut args[2]);
2020-08-11 07:46:09 +02:00
let a = &mut args[0].write_lock::<A>().unwrap();
2020-06-06 07:06:00 +02:00
2020-08-11 07:46:09 +02:00
func(a, b, c).map(Dynamic::from)
2020-06-06 07:06:00 +02:00
};
2020-07-24 17:16:54 +02:00
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
2020-06-06 07:06:00 +02:00
self.set_fn(
2020-06-25 05:07:46 +02:00
FN_IDX_SET,
2020-06-06 07:06:00 +02:00
Public,
2020-07-07 16:59:23 +02:00
&arg_types,
Func::from_method(Box::new(f)),
2020-06-06 07:06:00 +02:00
)
}
2020-07-24 17:16:54 +02:00
/// Set a pair of Rust index getter and setter functions, returning both hash keys.
2020-09-20 08:52:38 +02:00
/// This is a short-hand for `set_indexer_get_fn` and `set_indexer_set_fn`.
2020-07-24 17:16:54 +02:00
///
/// If there are similar existing Rust functions, they are replaced.
///
/// # Panics
///
/// Panics if the type is `Array` or `Map`.
/// Indexers for arrays, object maps and strings cannot be registered.
///
2020-07-24 17:16:54 +02:00
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
///
/// let mut module = Module::new();
/// let (hash_get, hash_set) = module.set_indexer_get_set_fn(
/// |x: &mut i64, y: ImmutableString| {
/// Ok(*x + y.len() as i64)
/// },
/// |x: &mut i64, y: ImmutableString, value: i64| {
/// *x = y.len() as i64 + value;
/// Ok(())
/// }
/// );
/// assert!(module.contains_fn(hash_get, true));
/// assert!(module.contains_fn(hash_set, true));
2020-07-24 17:16:54 +02:00
/// ```
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_index"))]
2020-07-24 17:16:54 +02:00
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
getter: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
setter: impl Fn(&mut A, B, T) -> FuncReturn<()> + SendSync + 'static,
) -> (u64, u64) {
(
self.set_indexer_get_fn(getter),
self.set_indexer_set_fn(setter),
)
}
2020-05-28 08:07:34 +02:00
/// Set a Rust function taking four parameters into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
2020-05-28 08:07:34 +02:00
///
/// let mut module = Module::new();
/// let hash = module.set_fn_4("calc", |x: i64, y: ImmutableString, z: i64, _w: ()| {
2020-05-28 08:07:34 +02:00
/// Ok(x + y.len() as i64 + z)
/// });
/// assert!(module.contains_fn(hash, true));
2020-05-28 08:07:34 +02:00
/// ```
pub fn set_fn_4<
A: Variant + Clone,
B: Variant + Clone,
C: Variant + Clone,
D: Variant + Clone,
T: Variant + Clone,
>(
&mut self,
name: impl Into<String>,
func: impl Fn(A, B, C, D) -> FuncReturn<T> + SendSync + 'static,
2020-05-28 08:07:34 +02:00
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let a = cast_arg::<A>(&mut args[0]);
let b = cast_arg::<B>(&mut args[1]);
let c = cast_arg::<C>(&mut args[2]);
let d = cast_arg::<D>(&mut args[3]);
2020-05-28 08:07:34 +02:00
func(a, b, c, d).map(Dynamic::from)
};
2020-07-07 16:59:23 +02:00
let arg_types = [
2020-05-28 08:07:34 +02:00
TypeId::of::<A>(),
TypeId::of::<B>(),
TypeId::of::<C>(),
TypeId::of::<D>(),
];
2020-07-07 16:59:23 +02:00
self.set_fn(name, Public, &arg_types, Func::from_pure(Box::new(f)))
2020-05-28 08:07:34 +02:00
}
/// Set a Rust function taking four parameters (the first one mutable) into the module,
2020-05-28 08:07:34 +02:00
/// returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
2020-05-28 08:07:34 +02:00
///
/// let mut module = Module::new();
/// let hash = module.set_fn_4_mut("calc", |x: &mut i64, y: ImmutableString, z: i64, _w: ()| {
2020-05-28 08:07:34 +02:00
/// *x += y.len() as i64 + z; Ok(*x)
/// });
/// assert!(module.contains_fn(hash, true));
2020-05-28 08:07:34 +02:00
/// ```
pub fn set_fn_4_mut<
A: Variant + Clone,
B: Variant + Clone,
C: Variant + Clone,
D: Variant + Clone,
T: Variant + Clone,
>(
&mut self,
name: impl Into<String>,
func: impl Fn(&mut A, B, C, D) -> FuncReturn<T> + SendSync + 'static,
2020-05-28 08:07:34 +02:00
) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let b = cast_arg::<B>(&mut args[1]);
let c = cast_arg::<C>(&mut args[2]);
let d = cast_arg::<D>(&mut args[3]);
2020-08-11 07:46:09 +02:00
let a = &mut args[0].write_lock::<A>().unwrap();
2020-05-28 08:07:34 +02:00
2020-08-11 07:46:09 +02:00
func(a, b, c, d).map(Dynamic::from)
2020-05-28 08:07:34 +02:00
};
2020-07-07 16:59:23 +02:00
let arg_types = [
2020-05-28 08:07:34 +02:00
TypeId::of::<A>(),
TypeId::of::<B>(),
TypeId::of::<C>(),
2020-09-15 05:03:46 +02:00
TypeId::of::<D>(),
2020-05-28 08:07:34 +02:00
];
2020-07-07 16:59:23 +02:00
self.set_fn(name, Public, &arg_types, Func::from_method(Box::new(f)))
2020-05-28 08:07:34 +02:00
}
2020-05-05 11:51:40 +02:00
/// Get a Rust function.
///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
/// It is also returned by the `set_fn_XXX` calls.
pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&Func> {
2020-09-11 16:32:59 +02:00
if hash_fn == 0 {
None
} else {
self.functions
.get(&hash_fn)
2020-09-24 16:50:28 +02:00
.and_then(|(_, access, _, _, f)| match access {
2020-09-11 16:32:59 +02:00
_ if !public_only => Some(f),
FnAccess::Public => Some(f),
FnAccess::Private => None,
})
}
2020-05-05 11:51:40 +02:00
}
/// Get a modules-qualified function.
2020-06-28 09:49:24 +02:00
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
2020-05-05 11:51:40 +02:00
///
2020-05-28 08:07:34 +02:00
/// The `u64` hash is calculated by the function `crate::calc_fn_hash` and must match
/// the hash calculated by `index_all_sub_modules`.
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&Func> {
self.all_functions.get(&hash_qualified_fn)
2020-05-05 11:51:40 +02:00
}
2020-05-05 17:57:25 +02:00
2020-08-14 18:04:10 +02:00
/// Combine another module into this module.
/// The other module is consumed to merge into this module.
pub fn combine(&mut self, other: Self) -> &mut Self {
self.modules.extend(other.modules.into_iter());
self.variables.extend(other.variables.into_iter());
self.functions.extend(other.functions.into_iter());
self.type_iterators.extend(other.type_iterators.into_iter());
self.all_functions.clear();
self.all_variables.clear();
self.indexed = false;
self
}
/// Combine another module into this module.
/// The other module is consumed to merge into this module.
/// Sub-modules are flattened onto the root module, with higher level overriding lower level.
pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
other.modules.into_iter().for_each(|(_, m)| {
self.combine_flatten(m);
});
2020-08-14 18:04:10 +02:00
self.variables.extend(other.variables.into_iter());
self.functions.extend(other.functions.into_iter());
self.type_iterators.extend(other.type_iterators.into_iter());
self.all_functions.clear();
self.all_variables.clear();
self.indexed = false;
self
}
/// Merge another module into this module.
2020-07-12 05:46:53 +02:00
pub fn merge(&mut self, other: &Self) -> &mut Self {
2020-09-24 16:50:28 +02:00
self.merge_filtered(other, &mut |_, _, _| true)
}
2020-07-26 09:53:22 +02:00
/// Merge another module into this module, with only selected script-defined functions based on a filter predicate.
pub(crate) fn merge_filtered(
&mut self,
other: &Self,
2020-09-24 16:50:28 +02:00
mut _filter: &mut impl FnMut(FnAccess, &str, usize) -> bool,
2020-07-12 05:46:53 +02:00
) -> &mut Self {
#[cfg(not(feature = "no_function"))]
for (k, v) in &other.modules {
let mut m = Self::new();
m.merge_filtered(v, _filter);
self.modules.insert(k.clone(), m);
}
#[cfg(feature = "no_function")]
self.modules
.extend(other.modules.iter().map(|(k, v)| (k.clone(), v.clone())));
self.variables
.extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone())));
self.functions.extend(
other
.functions
.iter()
2020-09-24 16:50:28 +02:00
.filter(|(_, (_, _, _, _, v))| match v {
2020-07-04 10:21:15 +02:00
#[cfg(not(feature = "no_function"))]
2020-07-26 09:53:22 +02:00
Func::Script(ref f) => _filter(f.access, f.name.as_str(), f.params.len()),
2020-07-02 15:46:08 +02:00
_ => true,
})
.map(|(&k, v)| (k, v.clone())),
);
self.type_iterators
.extend(other.type_iterators.iter().map(|(&k, v)| (k, v.clone())));
self.all_functions.clear();
self.all_variables.clear();
self.indexed = false;
2020-07-12 05:46:53 +02:00
self
}
/// Filter out the functions, retaining only some based on a filter predicate.
2020-07-04 10:21:15 +02:00
#[cfg(not(feature = "no_function"))]
2020-07-12 05:46:53 +02:00
pub(crate) fn retain_functions(
&mut self,
2020-09-24 16:50:28 +02:00
mut filter: impl FnMut(FnAccess, &str, usize) -> bool,
2020-07-12 05:46:53 +02:00
) -> &mut Self {
2020-09-24 16:50:28 +02:00
self.functions.retain(|_, (_, _, _, _, v)| match v {
2020-07-07 16:59:23 +02:00
Func::Script(ref f) => filter(f.access, f.name.as_str(), f.params.len()),
2020-07-02 15:46:08 +02:00
_ => true,
});
self.all_functions.clear();
self.all_variables.clear();
self.indexed = false;
2020-07-12 05:46:53 +02:00
self
}
/// Get the number of variables in the module.
pub fn num_var(&self) -> usize {
self.variables.len()
}
/// Get the number of functions in the module.
pub fn num_fn(&self) -> usize {
self.variables.len()
}
/// Get the number of type iterators in the module.
pub fn num_iter(&self) -> usize {
self.variables.len()
}
/// Get an iterator to the variables in the module.
pub fn iter_var(&self) -> impl Iterator<Item = (&String, &Dynamic)> {
self.variables.iter()
}
/// Get an iterator to the functions in the module.
pub(crate) fn iter_fn(
&self,
2020-09-24 16:50:28 +02:00
) -> impl Iterator<Item = &(String, FnAccess, usize, Option<StaticVec<TypeId>>, Func)> {
self.functions.values()
}
2020-06-26 16:33:27 +02:00
/// Get an iterator over all script-defined functions in the module.
2020-07-04 10:21:15 +02:00
#[cfg(not(feature = "no_function"))]
2020-06-26 16:33:27 +02:00
pub fn iter_script_fn<'a>(&'a self) -> impl Iterator<Item = Shared<ScriptFnDef>> + 'a {
self.functions
.values()
2020-09-24 16:50:28 +02:00
.map(|(_, _, _, _, f)| f)
2020-06-26 16:33:27 +02:00
.filter(|f| f.is_script())
.map(|f| f.get_shared_fn_def())
}
#[cfg(not(feature = "no_function"))]
2020-09-24 16:50:28 +02:00
pub fn iter_script_fn_info(&self, mut action: impl FnMut(FnAccess, &str, usize)) {
self.functions
.iter()
.for_each(|(_, (_, _, _, _, v))| match v {
Func::Script(ref f) => action(f.access, f.name.as_str(), f.params.len()),
_ => (),
});
}
/// Create a new `Module` by evaluating an `AST`.
///
/// # Examples
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// use rhai::{Engine, Module, Scope};
///
/// let engine = Engine::new();
/// let ast = engine.compile("let answer = 42; export answer;")?;
/// let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
/// assert!(module.contains_var("answer"));
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// # Ok(())
/// # }
/// ```
2020-05-13 13:21:42 +02:00
#[cfg(not(feature = "no_module"))]
2020-05-08 10:49:24 +02:00
pub fn eval_ast_as_new(mut scope: Scope, ast: &AST, engine: &Engine) -> FuncReturn<Self> {
let mut mods = Imports::new();
// Run the script
engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?;
// Create new module
let mut module = Module::new();
scope
.into_iter()
.for_each(|ScopeEntry { value, alias, .. }| {
// Variables with an alias left in the scope become module variables
if alias.is_some() {
module.variables.insert(*alias.unwrap(), value);
}
});
// Modules left in the scope become sub-modules
mods.into_iter().for_each(|(alias, m)| {
module.modules.insert(alias.to_string(), m);
});
module.merge(ast.lib());
Ok(module)
}
/// Scan through all the sub-modules in the module build an index of all
/// variables and external Rust functions via hashing.
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_module"))]
2020-05-09 04:00:59 +02:00
pub(crate) fn index_all_sub_modules(&mut self) {
// Collect a particular module.
2020-05-09 04:00:59 +02:00
fn index_module<'a>(
2020-05-17 16:19:49 +02:00
module: &'a Module,
2020-05-09 04:00:59 +02:00
qualifiers: &mut Vec<&'a str>,
variables: &mut Vec<(u64, Dynamic)>,
2020-07-07 16:59:23 +02:00
functions: &mut Vec<(u64, Func)>,
) {
2020-05-17 16:19:49 +02:00
for (name, m) in &module.modules {
2020-05-09 04:00:59 +02:00
// Index all the sub-modules first.
qualifiers.push(name);
index_module(m, qualifiers, variables, functions);
2020-05-09 04:00:59 +02:00
qualifiers.pop();
}
2020-05-09 04:00:59 +02:00
// Index all variables
2020-05-17 16:19:49 +02:00
for (var_name, value) in &module.variables {
2020-05-09 04:00:59 +02:00
// Qualifiers + variable name
let hash_var = calc_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0, empty());
2020-05-11 17:48:50 +02:00
variables.push((hash_var, value.clone()));
}
2020-05-09 04:00:59 +02:00
// Index all Rust functions
2020-09-24 17:32:54 +02:00
for (&_hash, (name, access, _num_args, params, func)) in module.functions.iter() {
match access {
// Private functions are not exported
2020-07-26 09:53:22 +02:00
FnAccess::Private => continue,
FnAccess::Public => (),
}
2020-07-04 10:21:15 +02:00
#[cfg(not(feature = "no_function"))]
2020-09-24 16:50:28 +02:00
if params.is_none() {
let hash_qualified_script = if qualifiers.is_empty() {
2020-09-24 17:32:54 +02:00
_hash
2020-09-24 16:50:28 +02:00
} else {
// Qualifiers + function name + number of arguments.
2020-09-24 17:32:54 +02:00
calc_fn_hash(qualifiers.iter().map(|&v| v), &name, *_num_args, empty())
2020-09-24 16:50:28 +02:00
};
functions.push((hash_qualified_script, func.clone()));
2020-07-04 10:21:15 +02:00
continue;
}
2020-07-04 10:21:15 +02:00
2020-09-24 16:50:28 +02:00
if let Some(params) = params {
// Qualified Rust functions are indexed in two steps:
// 1) Calculate a hash in a similar manner to script-defined functions,
// i.e. qualifiers + function name + number of arguments.
let hash_qualified_script =
calc_fn_hash(qualifiers.iter().map(|&v| v), name, params.len(), empty());
// 2) Calculate a second hash with no qualifiers, empty function name,
// zero number of arguments, and the actual list of argument `TypeId`'.s
let hash_fn_args = calc_fn_hash(empty(), "", 0, params.iter().cloned());
// 3) The final hash is the XOR of the two hashes.
let hash_qualified_fn = hash_qualified_script ^ hash_fn_args;
functions.push((hash_qualified_fn, func.clone()));
}
}
}
if self.indexed {
return;
}
2020-07-26 09:53:22 +02:00
let mut qualifiers: Vec<_> = Default::default();
let mut variables: Vec<_> = Default::default();
let mut functions: Vec<_> = Default::default();
qualifiers.push("root");
2020-07-26 09:53:22 +02:00
index_module(self, &mut qualifiers, &mut variables, &mut functions);
self.all_variables = variables.into_iter().collect();
self.all_functions = functions.into_iter().collect();
self.indexed = true;
}
2020-05-13 13:21:42 +02:00
/// Does a type iterator exist in the module?
2020-05-13 14:22:05 +02:00
pub fn contains_iter(&self, id: TypeId) -> bool {
2020-05-13 13:21:42 +02:00
self.type_iterators.contains_key(&id)
}
/// Set a type iterator into the module.
2020-07-12 05:46:53 +02:00
pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) -> &mut Self {
2020-05-20 13:27:23 +02:00
self.type_iterators.insert(typ, func);
self.indexed = false;
2020-07-12 05:46:53 +02:00
self
2020-05-13 13:21:42 +02:00
}
/// Get the specified type iterator.
2020-06-20 04:49:15 +02:00
pub(crate) fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
2020-05-20 13:27:23 +02:00
self.type_iterators.get(&id).cloned()
2020-05-13 13:21:42 +02:00
}
}
/// [INTERNALS] A chain of module names to qualify a variable or function call.
/// Exported under the `internals` feature only.
///
/// A `u64` hash key is cached for quick search purposes.
2020-05-13 13:21:42 +02:00
///
/// A `StaticVec` is used because most module-level access contains only one level,
/// and it is wasteful to always allocate a `Vec` with one element.
///
/// ## WARNING
///
/// This type is volatile and may change.
2020-07-19 11:14:55 +02:00
#[derive(Clone, Eq, PartialEq, Default, Hash)]
2020-05-13 13:21:42 +02:00
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);
impl fmt::Debug for ModuleRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)?;
if let Some(index) = self.1 {
write!(f, " -> {}", index)
} else {
Ok(())
}
}
}
impl Deref for ModuleRef {
type Target = StaticVec<(String, Position)>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for ModuleRef {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl fmt::Display for ModuleRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (m, _) in self.0.iter() {
write!(f, "{}{}", m, Token::DoubleColon.syntax())?;
}
Ok(())
}
}
impl From<StaticVec<(String, Position)>> for ModuleRef {
fn from(modules: StaticVec<(String, Position)>) -> Self {
Self(modules, None)
}
}
impl ModuleRef {
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
self.1
}
pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) {
self.1 = index
}
}
2020-05-15 15:40:54 +02:00
/// Trait that encapsulates a module resolution service.
pub trait ModuleResolver: SendSync {
2020-05-13 13:21:42 +02:00
/// Resolve a module based on a path string.
fn resolve(&self, _: &Engine, path: &str, pos: Position) -> Result<Module, Box<EvalAltResult>>;
2020-05-05 17:57:25 +02:00
}
/// Re-export module resolvers.
2020-05-13 13:21:42 +02:00
#[cfg(not(feature = "no_module"))]
2020-05-05 17:57:25 +02:00
pub mod resolvers {
2020-07-07 17:44:23 +02:00
pub use super::collection::ModuleResolversCollection;
#[cfg(not(feature = "no_std"))]
2020-06-17 03:54:17 +02:00
#[cfg(not(target_arch = "wasm32"))]
2020-09-24 16:50:28 +02:00
pub use super::file::{FileModuleResolver, GlobalFileModuleResolver};
pub use super::stat::StaticModuleResolver;
}
#[cfg(feature = "no_module")]
pub mod resolvers {}
2020-05-05 17:57:25 +02:00
/// Script file-based module resolver.
2020-05-13 13:21:42 +02:00
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_std"))]
2020-06-17 03:54:17 +02:00
#[cfg(not(target_arch = "wasm32"))]
mod file {
use super::*;
2020-05-05 17:57:25 +02:00
use crate::stdlib::path::PathBuf;
2020-05-15 15:40:54 +02:00
/// Module resolution service that loads module script files from the file system.
///
2020-09-25 03:39:18 +02:00
/// All functions in each module are treated as strictly independent and cannot refer to
2020-09-24 16:50:28 +02:00
/// 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;
2020-09-24 16:50:28 +02:00
/// use rhai::module_resolvers::GlobalFileModuleResolver;
///
2020-09-24 16:50:28 +02:00
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the 'scripts' subdirectory
/// // with file extension '.x'.
2020-09-24 16:50:28 +02:00
/// let resolver = GlobalFileModuleResolver::new_with_path_and_extension("./scripts", "x");
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
#[derive(Debug)]
2020-09-24 16:50:28 +02:00
pub struct GlobalFileModuleResolver {
path: PathBuf,
extension: String,
#[cfg(not(feature = "sync"))]
cache: RefCell<HashMap<PathBuf, AST>>,
#[cfg(feature = "sync")]
cache: RwLock<HashMap<PathBuf, AST>>,
}
2020-05-05 17:57:25 +02:00
2020-09-24 16:50:28 +02:00
impl Default for GlobalFileModuleResolver {
fn default() -> Self {
Self::new_with_path(PathBuf::default())
}
}
2020-09-24 16:50:28 +02:00
impl GlobalFileModuleResolver {
/// Create a new `GlobalFileModuleResolver` with a specific base path.
///
/// # Examples
///
/// ```
/// use rhai::Engine;
2020-09-24 16:50:28 +02:00
/// use rhai::module_resolvers::GlobalFileModuleResolver;
///
2020-09-24 16:50:28 +02:00
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the 'scripts' subdirectory
/// // with file extension '.rhai' (the default).
2020-09-24 16:50:28 +02:00
/// 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")
}
2020-09-24 16:50:28 +02:00
/// Create a new `GlobalFileModuleResolver` with a specific base path and file extension.
///
/// The default extension is `.rhai`.
///
/// # Examples
///
/// ```
/// use rhai::Engine;
2020-09-24 16:50:28 +02:00
/// use rhai::module_resolvers::GlobalFileModuleResolver;
///
2020-09-24 16:50:28 +02:00
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the 'scripts' subdirectory
/// // with file extension '.x'.
2020-09-24 16:50:28 +02:00
/// 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(),
}
2020-05-05 17:57:25 +02:00
}
2020-09-24 16:50:28 +02:00
/// Create a new `GlobalFileModuleResolver` with the current directory as base path.
///
/// # Examples
///
/// ```
/// use rhai::Engine;
2020-09-24 16:50:28 +02:00
/// use rhai::module_resolvers::GlobalFileModuleResolver;
///
2020-09-24 16:50:28 +02:00
/// // Create a new 'GlobalFileModuleResolver' loading scripts from the current directory
/// // with file extension '.rhai' (the default).
2020-09-24 16:50:28 +02:00
/// let resolver = GlobalFileModuleResolver::new();
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
2020-05-05 17:57:25 +02:00
pub fn new() -> Self {
Default::default()
}
2020-05-06 16:26:52 +02:00
/// 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())
2020-05-06 16:26:52 +02:00
}
2020-05-05 17:57:25 +02:00
}
2020-09-24 16:50:28 +02:00
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();
2020-05-05 17:57:25 +02:00
file_path.push(path);
file_path.set_extension(&self.extension); // Force extension
2020-05-05 17:57:25 +02:00
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);
}
2020-05-05 17:57:25 +02:00
Ok(module)
2020-05-05 17:57:25 +02:00
}
}
2020-09-24 16:50:28 +02:00
/// 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();
2020-09-24 17:32:54 +02:00
let mut _module = Module::eval_ast_as_new(Scope::new(), ast, engine)?;
2020-09-24 16:50:28 +02:00
2020-09-24 17:32:54 +02:00
#[cfg(not(feature = "no_function"))]
2020-09-24 16:50:28 +02:00
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();
2020-09-24 17:32:54 +02:00
_module.set_raw_fn_as_scripted(
2020-09-24 16:50:28 +02:00
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,
)
},
);
}
});
2020-09-24 17:32:54 +02:00
Ok(_module)
2020-09-24 16:50:28 +02:00
}
}
}
/// Static module resolver.
2020-05-13 13:21:42 +02:00
#[cfg(not(feature = "no_module"))]
mod stat {
use super::*;
2020-05-05 17:57:25 +02:00
2020-05-15 15:40:54 +02:00
/// 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));
/// ```
2020-05-05 17:57:25 +02:00
#[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));
/// ```
2020-05-05 17:57:25 +02:00
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);
2020-05-05 17:57:25 +02:00
}
/// 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();
2020-05-05 17:57:25 +02:00
}
}
impl ModuleResolver for StaticModuleResolver {
fn resolve(
&self,
_: &Engine,
path: &str,
pos: Position,
) -> Result<Module, Box<EvalAltResult>> {
self.0
.get(path)
.cloned()
2020-08-06 04:17:32 +02:00
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(path.into(), pos).into())
2020-05-05 17:57:25 +02:00
}
}
}
2020-07-07 17:44:23 +02:00
/// 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);
}
}
2020-08-06 04:17:32 +02:00
EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()
2020-07-07 17:44:23 +02:00
}
}
}