//! Module defining external-loaded modules for Rhai. use crate::ast::{FnAccess, Ident}; use crate::dynamic::Variant; use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync}; use crate::fn_register::by_value as cast_arg; use crate::stdlib::{ any::TypeId, boxed::Box, collections::HashMap, fmt, format, iter::empty, num::NonZeroU64, num::NonZeroUsize, ops::{Add, AddAssign, Deref, DerefMut}, string::{String, ToString}, vec::Vec, }; use crate::token::Token; use crate::utils::{combine_hashes, StraightHasherBuilder}; use crate::{ Dynamic, EvalAltResult, ImmutableString, NativeCallContext, Position, Shared, StaticVec, }; #[cfg(not(feature = "no_function"))] use crate::ast::ScriptFnDef; #[cfg(not(feature = "no_index"))] use crate::Array; #[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_object"))] use crate::Map; /// A type representing the namespace of a function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum FnNamespace { /// Global namespace. Global, /// Internal only. Internal, } impl Default for FnNamespace { fn default() -> Self { Self::Internal } } impl FnNamespace { /// Is this namespace [global][FnNamespace::Global]? #[inline(always)] pub fn is_global(self) -> bool { match self { Self::Global => true, Self::Internal => false, } } /// Is this namespace [internal][FnNamespace::Internal]? #[inline(always)] pub fn is_internal(self) -> bool { match self { Self::Global => false, Self::Internal => true, } } } /// Data structure containing a single registered function. #[derive(Debug, Clone)] pub struct FuncInfo { /// Function instance. pub func: CallableFunction, /// Function namespace. pub namespace: FnNamespace, /// Function access mode. pub access: FnAccess, /// Function name. pub name: String, /// Number of parameters. pub params: usize, /// Parameter types (if applicable). pub param_types: StaticVec, /// Parameter names (if available). pub param_names: StaticVec, } impl FuncInfo { /// Generate a signature of the function. pub fn gen_signature(&self) -> String { let mut sig = format!("{}(", self.name); if !self.param_names.is_empty() { let mut params: Vec<_> = self .param_names .iter() .map(ImmutableString::to_string) .collect(); let return_type = params.pop().unwrap_or_else(|| "()".to_string()); sig.push_str(¶ms.join(", ")); if return_type != "()" { sig.push_str(") -> "); sig.push_str(&return_type); } else if self.func.is_script() { sig.push_str(") -> Dynamic"); } else { sig.push_str(")"); } } else { for x in 0..self.params { sig.push_str("_"); if x < self.params - 1 { sig.push_str(", "); } } if self.func.is_script() { sig.push_str(") -> Dynamic"); } else { sig.push_str(") -> ?"); } } sig } } /// A module which may contain variables, sub-modules, external Rust functions, /// and/or script-defined functions. /// /// Not available under the `no_module` feature. #[derive(Clone)] pub struct Module { /// ID identifying the module. id: Option, /// Sub-modules. modules: HashMap>, /// Module variables. variables: HashMap, /// Flattened collection of all module variables, including those in sub-modules. all_variables: HashMap, /// External Rust functions. functions: HashMap, /// Flattened collection of all external Rust functions, native or scripted. /// including those in sub-modules. all_functions: HashMap, /// Iterator functions, keyed by the type producing the iterator. type_iterators: HashMap, /// Flattened collection of iterator functions, including those in sub-modules. all_type_iterators: HashMap, /// Is the module indexed? indexed: bool, } impl Default for Module { fn default() -> Self { Self { id: None, modules: Default::default(), variables: Default::default(), all_variables: Default::default(), functions: HashMap::with_capacity_and_hasher(64, StraightHasherBuilder), all_functions: HashMap::with_capacity_and_hasher(256, StraightHasherBuilder), type_iterators: Default::default(), all_type_iterators: Default::default(), indexed: false, } } } impl fmt::Debug for Module { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Module({}\n modules: {}\n vars: {}\n functions: {}\n)", if let Some(ref id) = self.id { format!("id: {:?}", id) } else { "".to_string() }, self.modules .keys() .map(|m| m.as_str()) .collect::>() .join(", "), self.variables .iter() .map(|(k, v)| format!("{}={:?}", k, v)) .collect::>() .join(", "), self.functions .values() .map(|FuncInfo { func, .. }| func.to_string()) .collect::>() .join(", "), ) } } impl AsRef for Module { #[inline(always)] fn as_ref(&self) -> &Module { self } } impl Module { /// Create a new module. /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// module.set_var("answer", 42_i64); /// assert_eq!(module.get_var_value::("answer").unwrap(), 42); /// ``` #[inline(always)] pub fn new() -> Self { Default::default() } /// Create a new module with a specified capacity for native Rust functions. /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// module.set_var("answer", 42_i64); /// assert_eq!(module.get_var_value::("answer").unwrap(), 42); /// ``` #[inline(always)] pub fn new_with_capacity(capacity: usize) -> Self { Self { functions: HashMap::with_capacity_and_hasher(capacity, StraightHasherBuilder), ..Default::default() } } /// Get the ID of the module, if any. /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// module.set_id(Some("hello")); /// assert_eq!(module.id(), Some("hello")); /// ``` pub fn id(&self) -> Option<&str> { self.id.as_ref().map(|s| s.as_str()) } /// Get the ID of the module, if any. pub(crate) fn id_raw(&self) -> &Option { &self.id } /// Set the ID of the module. /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// module.set_id(Some("hello")); /// assert_eq!(module.id(), Some("hello")); /// ``` pub fn set_id>(&mut self, id: Option) { self.id = id.map(|s| s.into()); } /// Is the module empty? /// /// # Example /// /// ``` /// use rhai::Module; /// /// let module = Module::new(); /// assert!(module.is_empty()); /// ``` #[inline(always)] pub fn is_empty(&self) -> bool { self.functions.is_empty() && self.all_functions.is_empty() && self.variables.is_empty() && self.all_variables.is_empty() && self.modules.is_empty() && self.type_iterators.is_empty() && self.all_type_iterators.is_empty() } /// Is the module indexed? /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// assert!(!module.is_indexed()); /// /// # #[cfg(not(feature = "no_module"))] /// # { /// module.build_index(); /// assert!(module.is_indexed()); /// # } /// ``` pub fn is_indexed(&self) -> bool { self.indexed } /// Generate signatures for all the functions in the module. pub fn gen_fn_signatures<'a>(&'a self) -> impl Iterator + 'a { self.functions .values() .filter(|FuncInfo { access, .. }| !access.is_private()) .map(FuncInfo::gen_signature) } /// Does a variable exist in the module? /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// module.set_var("answer", 42_i64); /// assert!(module.contains_var("answer")); /// ``` #[inline(always)] pub fn contains_var(&self, name: &str) -> bool { self.variables.contains_key(name) } /// Get the value of a module variable. /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// module.set_var("answer", 42_i64); /// assert_eq!(module.get_var_value::("answer").unwrap(), 42); /// ``` #[inline(always)] pub fn get_var_value(&self, name: &str) -> Option { self.get_var(name).and_then(Dynamic::try_cast::) } /// Get a module variable as a [`Dynamic`][crate::Dynamic]. /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// module.set_var("answer", 42_i64); /// assert_eq!(module.get_var("answer").unwrap().cast::(), 42); /// ``` #[inline(always)] pub fn get_var(&self, name: &str) -> Option { self.variables.get(name).cloned() } /// Set a variable into the module. /// /// If there is an existing variable of the same name, it is replaced. /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// module.set_var("answer", 42_i64); /// assert_eq!(module.get_var_value::("answer").unwrap(), 42); /// ``` #[inline(always)] pub fn set_var( &mut self, name: impl Into, value: impl Variant + Clone, ) -> &mut Self { self.variables.insert(name.into(), Dynamic::from(value)); self.indexed = false; self } /// Get a reference to a namespace-qualified variable. /// Name and Position in `EvalAltResult` are None and must be set afterwards. /// /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. #[inline(always)] pub(crate) fn get_qualified_var( &self, hash_var: NonZeroU64, ) -> Result<&Dynamic, Box> { self.all_variables.get(&hash_var).ok_or_else(|| { EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into() }) } /// 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. #[cfg(not(feature = "no_function"))] #[inline] pub(crate) fn set_script_fn(&mut self, fn_def: impl Into>) -> NonZeroU64 { let fn_def = fn_def.into(); // None + function name + number of arguments. let num_params = fn_def.params.len(); let hash_script = crate::calc_script_fn_hash(empty(), &fn_def.name, num_params).unwrap(); let mut param_names: StaticVec<_> = fn_def.params.iter().cloned().collect(); param_names.push("Dynamic".into()); self.functions.insert( hash_script, FuncInfo { name: fn_def.name.to_string(), namespace: FnNamespace::Internal, access: fn_def.access, params: num_params, param_types: Default::default(), param_names, func: fn_def.into(), }, ); self.indexed = false; hash_script } /// Get a script-defined function in the module based on name and number of parameters. #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn get_script_fn( &self, name: &str, num_params: usize, public_only: bool, ) -> Option<&ScriptFnDef> { self.functions .values() .find( |FuncInfo { name: fn_name, access, params, .. }| { (!public_only || *access == FnAccess::Public) && *params == num_params && fn_name == name }, ) .map(|FuncInfo { func, .. }| func.get_fn_def()) } /// Does a sub-module exist in the module? /// /// # Example /// /// ``` /// 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")); /// ``` #[inline(always)] pub fn contains_sub_module(&self, name: &str) -> bool { self.modules.contains_key(name) } /// Get a sub-module. /// /// # Example /// /// ``` /// 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()); /// ``` #[inline(always)] pub fn get_sub_module(&self, name: &str) -> Option<&Module> { self.modules.get(name).map(|m| m.as_ref()) } /// Set a sub-module into the module. /// /// If there is an existing sub-module of the same name, it is replaced. /// /// # Example /// /// ``` /// 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()); /// ``` #[inline(always)] pub fn set_sub_module( &mut self, name: impl Into, sub_module: impl Into>, ) -> &mut Self { self.modules.insert(name.into(), sub_module.into()); self.indexed = false; self } /// Does the particular Rust function exist in the module? /// /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. /// It is also returned by the `set_fn_XXX` calls. /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// let hash = module.set_fn_0("calc", || Ok(42_i64)); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn contains_fn(&self, hash_fn: NonZeroU64, public_only: bool) -> bool { if public_only { self.functions .get(&hash_fn) .map(|FuncInfo { access, .. }| access.is_public()) .unwrap_or(false) } else { self.functions.contains_key(&hash_fn) } } /// Update the metadata (parameter names/types and return type) of a registered function. /// /// The [`NonZeroU64`] hash is calculated either by the function [`crate::calc_native_fn_hash`] or /// the function [`crate::calc_script_fn_hash`]. /// /// Each parameter name/type pair should be a single string of the format: `var_name: type`. /// /// The last entry in the list should be the return type of the function. /// In other words, the number of entries should be one larger than the number of parameters. pub fn update_fn_metadata<'a>( &mut self, hash_fn: NonZeroU64, arg_names: impl AsRef<[&'a str]>, ) -> &mut Self { if let Some(f) = self.functions.get_mut(&hash_fn) { f.param_names = arg_names.as_ref().iter().map(|&n| n.into()).collect(); } self } /// Update the namespace of a registered function. /// /// The [`NonZeroU64`] hash is calculated either by the function [`crate::calc_native_fn_hash`] or /// the function [`crate::calc_script_fn_hash`]. pub fn update_fn_namespace( &mut self, hash_fn: NonZeroU64, namespace: FnNamespace, ) -> &mut Self { if let Some(f) = self.functions.get_mut(&hash_fn) { f.namespace = namespace; } self.indexed = false; self } /// 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. /// /// ## WARNING - Low Level API /// /// This function is very low level. pub fn set_fn( &mut self, name: impl Into, namespace: FnNamespace, access: FnAccess, arg_names: Option<&[&str]>, arg_types: &[TypeId], func: CallableFunction, ) -> NonZeroU64 { let name = name.into(); let hash_fn = crate::calc_native_fn_hash(empty(), &name, arg_types.iter().cloned()).unwrap(); let param_types = arg_types .into_iter() .cloned() .map(|id| { if id == TypeId::of::<&str>() || id == TypeId::of::() { TypeId::of::() } else { id } }) .collect::>(); self.functions.insert( hash_fn, FuncInfo { name, namespace, access, params: param_types.len(), param_types, param_names: if let Some(p) = arg_names { p.iter().map(|&v| v.into()).collect() } else { Default::default() }, func: func.into(), }, ); self.indexed = false; hash_fn } /// Set a Rust function taking a reference to the scripting [`Engine`][crate::Engine], /// the current set of functions, plus a list of mutable [`Dynamic`][crate::Dynamic] references /// into the module, returning a hash key. /// /// Use this to register a built-in function which must reference settings on the scripting /// [`Engine`][crate::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. /// /// ## 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`][crate::Dynamic], /// which is guaranteed to contain enough arguments of the correct types. /// /// The function is assumed to be a _method_, meaning that the first argument should not be consumed. /// All other arguments can be consumed. /// /// To access a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::()` /// /// To access a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::()`. /// Notice that this will _consume_ the argument, replacing it with `()`. /// /// To access the first mutable parameter, use `args.get_mut(0).unwrap()` /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// use rhai::{Module, FnNamespace, FnAccess}; /// /// let mut module = Module::new(); /// let hash = module.set_raw_fn("double_or_not", FnNamespace::Internal, FnAccess::Public, /// // Pass parameter types via a slice with TypeId's /// &[std::any::TypeId::of::(), std::any::TypeId::of::()], /// // Fixed closure signature /// |context, 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::(); /// // Since it is a primary type, it can also be cheaply copied /// let double = args[1].clone().cast::(); /// // Get a mutable reference to the first argument. /// let mut x = args[0].write_lock::().unwrap(); /// /// let orig = *x; /// /// if double { /// *x *= 2; // the first argument can be mutated /// } /// /// Ok(orig) // return Result> /// }); /// /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_raw_fn( &mut self, name: impl Into, namespace: FnNamespace, access: FnAccess, arg_types: &[TypeId], func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result> + SendSync + 'static, ) -> NonZeroU64 { let f = move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from); self.set_fn( name, namespace, access, None, arg_types, CallableFunction::from_method(Box::new(f)), ) } /// 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. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// let hash = module.set_fn_0("calc", || Ok(42_i64)); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_fn_0( &mut self, name: impl Into, func: impl Fn() -> Result> + SendSync + 'static, ) -> NonZeroU64 { let f = move |_: NativeCallContext, _: &mut FnCallArgs| func().map(Dynamic::from); let arg_types = []; self.set_fn( name, FnNamespace::Internal, FnAccess::Public, None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) } /// 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. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// use rhai::Module; /// /// let mut module = Module::new(); /// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1)); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_fn_1( &mut self, name: impl Into, func: impl Fn(A) -> Result> + SendSync + 'static, ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { func(cast_arg::(&mut args[0])).map(Dynamic::from) }; let arg_types = [TypeId::of::()]; self.set_fn( name, FnNamespace::Internal, FnAccess::Public, None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) } /// 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. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// use rhai::{Module, FnNamespace}; /// /// let mut module = Module::new(); /// let hash = module.set_fn_1_mut("calc", FnNamespace::Internal, /// |x: &mut i64| { *x += 1; Ok(*x) } /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_fn_1_mut( &mut self, name: impl Into, namespace: FnNamespace, func: impl Fn(&mut A) -> Result> + SendSync + 'static, ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { func(&mut args[0].write_lock::().unwrap()).map(Dynamic::from) }; let arg_types = [TypeId::of::()]; self.set_fn( name, namespace, FnAccess::Public, None, &arg_types, CallableFunction::from_method(Box::new(f)), ) } /// Set a Rust getter function taking one mutable parameter, returning a hash key. /// This function is automatically exposed to the global namespace. /// /// If there is a similar existing Rust getter function, it is replaced. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// 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"))] #[inline] pub fn set_getter_fn( &mut self, name: impl Into, func: impl Fn(&mut A) -> Result> + SendSync + 'static, ) -> NonZeroU64 { self.set_fn_1_mut( crate::engine::make_getter(&name.into()), FnNamespace::Global, func, ) } /// 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. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// 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) /// }); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_fn_2( &mut self, name: impl Into, func: impl Fn(A, B) -> Result> + SendSync + 'static, ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let a = cast_arg::(&mut args[0]); let b = cast_arg::(&mut args[1]); func(a, b).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::()]; self.set_fn( name, FnNamespace::Internal, FnAccess::Public, None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) } /// 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. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// use rhai::{Module, FnNamespace, ImmutableString}; /// /// let mut module = Module::new(); /// let hash = module.set_fn_2_mut("calc", FnNamespace::Internal, /// |x: &mut i64, y: ImmutableString| { /// *x += y.len() as i64; /// Ok(*x) /// } /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_fn_2_mut( &mut self, name: impl Into, namespace: FnNamespace, func: impl Fn(&mut A, B) -> Result> + SendSync + 'static, ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let b = cast_arg::(&mut args[1]); let a = &mut args[0].write_lock::().unwrap(); func(a, b).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::()]; self.set_fn( name, namespace, FnAccess::Public, None, &arg_types, CallableFunction::from_method(Box::new(f)), ) } /// Set a Rust setter function taking two parameters (the first one mutable) into the module, /// returning a hash key. /// This function is automatically exposed to the global namespace. /// /// If there is a similar existing setter Rust function, it is replaced. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// 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"))] #[inline] pub fn set_setter_fn( &mut self, name: impl Into, func: impl Fn(&mut A, B) -> Result<(), Box> + SendSync + 'static, ) -> NonZeroU64 { self.set_fn_2_mut( crate::engine::make_setter(&name.into()), FnNamespace::Global, func, ) } /// Set a Rust index getter taking two parameters (the first one mutable) into the module, /// returning a hash key. /// This function is automatically exposed to the global namespace. /// /// 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. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// use rhai::{Module, ImmutableString}; /// /// let mut module = Module::new(); /// 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"))] #[inline] pub fn set_indexer_get_fn( &mut self, func: impl Fn(&mut A, B) -> Result> + SendSync + 'static, ) -> NonZeroU64 { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } #[cfg(not(feature = "no_object"))] if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for object maps."); } if TypeId::of::() == TypeId::of::() || TypeId::of::() == TypeId::of::<&str>() || TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for strings."); } self.set_fn_2_mut(crate::engine::FN_IDX_GET, FnNamespace::Global, func) } /// 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. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// 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) /// }); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_fn_3< A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, T: Variant + Clone, >( &mut self, name: impl Into, func: impl Fn(A, B, C) -> Result> + SendSync + 'static, ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let a = cast_arg::(&mut args[0]); let b = cast_arg::(&mut args[1]); let c = cast_arg::(&mut args[2]); func(a, b, c).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( name, FnNamespace::Internal, FnAccess::Public, None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) } /// 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. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// use rhai::{Module, FnNamespace, ImmutableString}; /// /// let mut module = Module::new(); /// let hash = module.set_fn_3_mut("calc", FnNamespace::Internal, /// |x: &mut i64, y: ImmutableString, z: i64| { /// *x += y.len() as i64 + z; /// Ok(*x) /// } /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] pub fn set_fn_3_mut< A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, T: Variant + Clone, >( &mut self, name: impl Into, namespace: FnNamespace, func: impl Fn(&mut A, B, C) -> Result> + SendSync + 'static, ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let b = cast_arg::(&mut args[2]); let c = cast_arg::(&mut args[3]); let a = &mut args[0].write_lock::().unwrap(); func(a, b, c).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( name, namespace, FnAccess::Public, None, &arg_types, CallableFunction::from_method(Box::new(f)), ) } /// Set a Rust index setter taking three parameters (the first one mutable) into the module, /// returning a hash key. /// This function is automatically exposed to the global namespace. /// /// 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. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// 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)); /// ``` #[cfg(not(feature = "no_index"))] #[inline] pub fn set_indexer_set_fn( &mut self, func: impl Fn(&mut A, B, C) -> Result<(), Box> + SendSync + 'static, ) -> NonZeroU64 { if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } #[cfg(not(feature = "no_object"))] if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for object maps."); } if TypeId::of::() == TypeId::of::() || TypeId::of::() == TypeId::of::<&str>() || TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for strings."); } let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let b = cast_arg::(&mut args[1]); let c = cast_arg::(&mut args[2]); let a = &mut args[0].write_lock::().unwrap(); func(a, b, c).map(Dynamic::from) }; let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( crate::engine::FN_IDX_SET, FnNamespace::Internal, FnAccess::Public, None, &arg_types, CallableFunction::from_method(Box::new(f)), ) } /// Set a pair of Rust index getter and setter functions, returning both hash keys. /// This is a short-hand for [`set_indexer_get_fn`][Module::set_indexer_get_fn] and /// [`set_indexer_set_fn`][Module::set_indexer_set_fn]. /// /// 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. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// 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)); /// ``` #[cfg(not(feature = "no_index"))] #[inline] pub fn set_indexer_get_set_fn( &mut self, getter: impl Fn(&mut A, B) -> Result> + SendSync + 'static, setter: impl Fn(&mut A, B, T) -> Result<(), Box> + SendSync + 'static, ) -> (NonZeroU64, NonZeroU64) { ( self.set_indexer_get_fn(getter), self.set_indexer_set_fn(setter), ) } /// 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. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// use rhai::{Module, ImmutableString}; /// /// let mut module = Module::new(); /// let hash = module.set_fn_4("calc", |x: i64, y: ImmutableString, z: i64, _w: ()| { /// Ok(x + y.len() as i64 + z) /// }); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] 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, func: impl Fn(A, B, C, D) -> Result> + SendSync + 'static, ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let a = cast_arg::(&mut args[0]); let b = cast_arg::(&mut args[1]); let c = cast_arg::(&mut args[2]); let d = cast_arg::(&mut args[3]); func(a, b, c, d).map(Dynamic::from) }; let arg_types = [ TypeId::of::(), TypeId::of::(), TypeId::of::(), TypeId::of::(), ]; self.set_fn( name, FnNamespace::Internal, FnAccess::Public, None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) } /// Set a Rust function taking four parameters (the first one mutable) into the module, /// returning a hash key. /// /// If there is a similar existing Rust function, it is replaced. /// /// # Function Metadata /// /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// /// # Example /// /// ``` /// use rhai::{Module, FnNamespace, ImmutableString}; /// /// let mut module = Module::new(); /// let hash = module.set_fn_4_mut("calc", FnNamespace::Internal, /// |x: &mut i64, y: ImmutableString, z: i64, _w: ()| { /// *x += y.len() as i64 + z; /// Ok(*x) /// } /// ); /// assert!(module.contains_fn(hash, true)); /// ``` #[inline] 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, namespace: FnNamespace, func: impl Fn(&mut A, B, C, D) -> Result> + SendSync + 'static, ) -> NonZeroU64 { let f = move |_: NativeCallContext, args: &mut FnCallArgs| { let b = cast_arg::(&mut args[1]); let c = cast_arg::(&mut args[2]); let d = cast_arg::(&mut args[3]); let a = &mut args[0].write_lock::().unwrap(); func(a, b, c, d).map(Dynamic::from) }; let arg_types = [ TypeId::of::(), TypeId::of::(), TypeId::of::(), TypeId::of::(), ]; self.set_fn( name, namespace, FnAccess::Public, None, &arg_types, CallableFunction::from_method(Box::new(f)), ) } /// Get a Rust function. /// /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`]. /// It is also returned by the `set_fn_XXX` calls. #[inline(always)] pub(crate) fn get_fn( &self, hash_fn: NonZeroU64, public_only: bool, ) -> Option<&CallableFunction> { self.functions .get(&hash_fn) .and_then(|FuncInfo { access, func, .. }| match access { _ if !public_only => Some(func), FnAccess::Public => Some(func), FnAccess::Private => None, }) } /// Does the particular namespace-qualified function exist in the module? /// /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`] and must match /// the hash calculated by [`build_index`][Module::build_index]. #[inline(always)] pub fn contains_qualified_fn(&self, hash_fn: NonZeroU64) -> bool { self.all_functions.contains_key(&hash_fn) } /// Get a namespace-qualified function. /// Name and Position in `EvalAltResult` are None and must be set afterwards. /// /// The [`NonZeroU64`] hash is calculated by the function [`crate::calc_native_fn_hash`] and must match /// the hash calculated by [`build_index`][Module::build_index]. #[inline(always)] pub(crate) fn get_qualified_fn( &self, hash_qualified_fn: NonZeroU64, ) -> Option<&CallableFunction> { self.all_functions.get(&hash_qualified_fn) } /// Combine another module into this module. /// The other module is consumed to merge into this module. #[inline] 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.all_type_iterators.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. #[inline] pub fn combine_flatten(&mut self, other: Self) -> &mut Self { other.modules.into_iter().for_each(|(_, m)| { self.combine_flatten(shared_take_or_clone(m)); }); 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.all_type_iterators.clear(); self.indexed = false; self } /// Poly-fill this module with another module. /// Only items not existing in this module are added. #[inline] pub fn fill_with(&mut self, other: &Self) -> &mut Self { other.modules.iter().for_each(|(k, v)| { if !self.modules.contains_key(k) { self.modules.insert(k.clone(), v.clone()); } }); other.variables.iter().for_each(|(k, v)| { if !self.variables.contains_key(k) { self.variables.insert(k.clone(), v.clone()); } }); other.functions.iter().for_each(|(&k, v)| { self.functions.entry(k).or_insert_with(|| v.clone()); }); other.type_iterators.iter().for_each(|(&k, &v)| { self.type_iterators.entry(k).or_insert(v); }); self.all_functions.clear(); self.all_variables.clear(); self.all_type_iterators.clear(); self.indexed = false; self } /// Merge another module into this module. #[inline(always)] pub fn merge(&mut self, other: &Self) -> &mut Self { self.merge_filtered(other, &mut |_, _, _, _, _| true) } /// Merge another module into this module based on a filter predicate. pub(crate) fn merge_filtered( &mut self, other: &Self, mut _filter: &mut impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool, ) -> &mut Self { #[cfg(not(feature = "no_function"))] other.modules.iter().for_each(|(k, v)| { let mut m = Self::new(); m.merge_filtered(v, _filter); self.set_sub_module(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() .filter( |( _, FuncInfo { namespace, access, name, params, func, .. }, )| { _filter( *namespace, *access, func.is_script(), name.as_str(), *params, ) }, ) .map(|(&k, v)| (k, v.clone())), ); self.type_iterators.extend(other.type_iterators.iter()); self.all_functions.clear(); self.all_variables.clear(); self.all_type_iterators.clear(); self.indexed = false; self } /// Filter out the functions, retaining only some script-defined functions based on a filter predicate. #[cfg(not(feature = "no_function"))] #[inline] pub(crate) fn retain_script_functions( &mut self, mut filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool, ) -> &mut Self { self.functions.retain( |_, FuncInfo { namespace, access, name, params, func, .. }| { if func.is_script() { filter(*namespace, *access, name.as_str(), *params) } else { false } }, ); self.all_functions.clear(); self.all_variables.clear(); self.all_type_iterators.clear(); self.indexed = false; self } /// Get the number of variables, functions and type iterators in the module. #[inline(always)] pub fn count(&self) -> (usize, usize, usize) { ( self.variables.len(), self.functions.len(), self.type_iterators.len(), ) } /// Get an iterator to the sub-modules in the module. #[inline(always)] pub fn iter_sub_modules(&self) -> impl Iterator)> { self.modules.iter().map(|(k, m)| (k.as_str(), m.clone())) } /// Get an iterator to the variables in the module. #[inline(always)] pub fn iter_var(&self) -> impl Iterator { self.variables.iter().map(|(k, v)| (k.as_str(), v)) } /// Get an iterator to the functions in the module. #[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_function"))] #[inline(always)] pub(crate) fn iter_fn(&self) -> impl Iterator { self.functions.values() } /// Get an iterator over all script-defined functions in the module. /// /// Function metadata includes: /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]). /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]). /// 3) Function name (as string slice). /// 4) Number of parameters. /// 5) Shared reference to function definition [`ScriptFnDef`][crate::ast::ScriptFnDef]. #[cfg(not(feature = "no_function"))] #[inline(always)] pub(crate) fn iter_script_fn<'a>( &'a self, ) -> impl Iterator + 'a { self.functions.values().filter(|f| f.func.is_script()).map( |FuncInfo { namespace, access, name, params, func, .. }| { ( *namespace, *access, name.as_str(), *params, func.get_fn_def(), ) }, ) } /// Get an iterator over all script-defined functions in the module. /// /// Function metadata includes: /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]). /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]). /// 3) Function name (as string slice). /// 4) Number of parameters. #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "internals"))] #[inline(always)] pub fn iter_script_fn_info( &self, ) -> impl Iterator { self.functions.values().filter(|f| f.func.is_script()).map( |FuncInfo { name, namespace, access, params, .. }| (*namespace, *access, name.as_str(), *params), ) } /// Get an iterator over all script-defined functions in the module. /// /// Function metadata includes: /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]). /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]). /// 3) Function name (as string slice). /// 4) Number of parameters. /// 5) _(INTERNALS)_ Shared reference to function definition [`ScriptFnDef`][crate::ast::ScriptFnDef]. /// Exported under the `internals` feature only. #[cfg(not(feature = "no_function"))] #[cfg(feature = "internals")] #[inline(always)] pub fn iter_script_fn_info( &self, ) -> impl Iterator { self.iter_script_fn() } /// Create a new module by evaluating an [`AST`][crate::AST]. /// /// The entire [`AST`][crate::AST] is encapsulated into each function, allowing functions /// to cross-call each other. Functions in the global namespace, plus all functions /// defined in the module, are _merged_ into a _unified_ namespace before each call. /// Therefore, all functions will be found. /// /// # Example /// /// ``` /// # fn main() -> Result<(), Box> { /// 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::("answer").unwrap(), 42); /// # Ok(()) /// # } /// ``` #[cfg(not(feature = "no_module"))] pub fn eval_ast_as_new( mut scope: crate::Scope, ast: &crate::AST, engine: &crate::Engine, ) -> Result> { let mut mods = engine.global_sub_modules.clone(); let orig_mods_len = mods.len(); // 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(|(_, value, mut aliases)| { // Variables with an alias left in the scope become module variables if aliases.len() > 1 { aliases.into_iter().for_each(|alias| { module.variables.insert(alias, value.clone()); }); } else if aliases.len() == 1 { module.variables.insert(aliases.pop().unwrap(), value); } }); // Extra modules left in the scope become sub-modules let mut func_mods: crate::engine::Imports = Default::default(); mods.into_iter().skip(orig_mods_len).for_each(|(alias, m)| { func_mods.push(alias.clone(), m.clone()); module.set_sub_module(alias, m); }); // Non-private functions defined become module functions #[cfg(not(feature = "no_function"))] { ast.lib() .functions .values() .filter(|FuncInfo { access, func, .. }| !access.is_private() && func.is_script()) .for_each(|FuncInfo { func, .. }| { // Encapsulate AST environment let mut func = func.get_fn_def().clone(); func.lib = Some(ast.shared_lib()); func.mods = func_mods.clone(); module.set_script_fn(func); }); } module.set_id(ast.clone_source()); module.build_index(); Ok(module) } /// Scan through all the sub-modules in the module and build a hash index of all /// variables and functions as one flattened namespace. /// /// If the module is already indexed, this method has no effect. pub fn build_index(&mut self) -> &mut Self { // Collect a particular module. fn index_module<'a>( module: &'a Module, qualifiers: &mut Vec<&'a str>, variables: &mut HashMap, functions: &mut HashMap, type_iterators: &mut HashMap, ) { module.modules.iter().for_each(|(name, m)| { // Index all the sub-modules first. qualifiers.push(name); index_module(m, qualifiers, variables, functions, type_iterators); qualifiers.pop(); }); // Index all variables module.variables.iter().for_each(|(var_name, value)| { // Qualifiers + variable name let hash_var = crate::calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0).unwrap(); variables.insert(hash_var, value.clone()); }); // Index type iterators module.type_iterators.iter().for_each(|(&type_id, func)| { type_iterators.insert(type_id, func.clone()); }); // Index all Rust functions module .functions .iter() .filter(|(_, FuncInfo { access, .. })| access.is_public()) .for_each( |( &hash, FuncInfo { name, namespace, params, param_types, func, .. }, )| { // Flatten all functions with global namespace if namespace.is_global() { functions.insert(hash, func.clone()); } // Qualifiers + function name + number of arguments. let hash_qualified_script = crate::calc_script_fn_hash(qualifiers.iter().cloned(), name, *params) .unwrap(); if !func.is_script() { assert_eq!(*params, param_types.len()); // Namespace-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. // 2) Calculate a second hash with no qualifiers, empty function name, // and the actual list of argument [`TypeId`]'.s let hash_fn_args = crate::calc_native_fn_hash( empty(), "", param_types.iter().cloned(), ) .unwrap(); // 3) The two hashes are combined. let hash_qualified_fn = combine_hashes(hash_qualified_script, hash_fn_args); functions.insert(hash_qualified_fn, func.clone()); } else if cfg!(not(feature = "no_function")) { functions.insert(hash_qualified_script, func.clone()); } }, ); } if !self.indexed { let mut qualifiers = Vec::with_capacity(4); let mut variables = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder); let mut functions = HashMap::with_capacity_and_hasher(256, StraightHasherBuilder); let mut type_iterators = HashMap::with_capacity(16); qualifiers.push("root"); index_module( self, &mut qualifiers, &mut variables, &mut functions, &mut type_iterators, ); self.all_variables = variables; self.all_functions = functions; self.all_type_iterators = type_iterators; self.indexed = true; } self } /// Does a type iterator exist in the entire module tree? pub fn contains_qualified_iter(&self, id: TypeId) -> bool { self.all_type_iterators.contains_key(&id) } /// Does a type iterator exist in the module? pub fn contains_iter(&self, id: TypeId) -> bool { self.type_iterators.contains_key(&id) } /// Set a type iterator into the module. pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) -> &mut Self { self.type_iterators.insert(typ, func); self.indexed = false; self } /// Set a type iterator into the module. pub fn set_iterable(&mut self) -> &mut Self where T: Variant + Clone + IntoIterator, ::Item: Variant + Clone, { self.set_iter(TypeId::of::(), |obj: Dynamic| { Box::new(obj.cast::().into_iter().map(Dynamic::from)) }) } /// Set an iterator type into the module as a type iterator. pub fn set_iterator(&mut self) -> &mut Self where T: Variant + Clone + Iterator, ::Item: Variant + Clone, { self.set_iter(TypeId::of::(), |obj: Dynamic| { Box::new(obj.cast::().map(Dynamic::from)) }) } /// Get the specified type iterator. pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option { self.all_type_iterators.get(&id).cloned() } /// Get the specified type iterator. pub(crate) fn get_iter(&self, id: TypeId) -> Option { self.type_iterators.get(&id).cloned() } } /// _(INTERNALS)_ A chain of [module][Module] names to namespace-qualify a variable or function call. /// Exported under the `internals` feature only. /// /// A [`NonZeroU64`] offset to the current [`Scope`][crate::Scope] is cached for quick search purposes. /// /// A [`StaticVec`] is used because most namespace-qualified 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. #[derive(Clone, Eq, PartialEq, Default, Hash)] pub struct NamespaceRef(Option, StaticVec); impl fmt::Debug for NamespaceRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.1, f)?; if let Some(index) = self.0 { write!(f, " -> {}", index) } else { Ok(()) } } } impl Deref for NamespaceRef { type Target = StaticVec; fn deref(&self) -> &Self::Target { &self.1 } } impl DerefMut for NamespaceRef { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.1 } } impl fmt::Display for NamespaceRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for Ident { name, .. } in self.1.iter() { write!(f, "{}{}", name, Token::DoubleColon.syntax())?; } Ok(()) } } impl From> for NamespaceRef { fn from(modules: StaticVec) -> Self { Self(None, modules) } } impl> Add for &Module { type Output = Module; fn add(self, rhs: M) -> Self::Output { let mut module = self.clone(); module.merge(rhs.as_ref()); module } } impl> Add for Module { type Output = Self; fn add(mut self, rhs: M) -> Self::Output { self.merge(rhs.as_ref()); self } } impl> AddAssign for Module { fn add_assign(&mut self, rhs: M) { self.combine(rhs.into()); } } impl NamespaceRef { /// Get the [`Scope`][crate::Scope] index offset. pub(crate) fn index(&self) -> Option { self.0 } /// Set the [`Scope`][crate::Scope] index offset. #[cfg(not(feature = "no_module"))] pub(crate) fn set_index(&mut self, index: Option) { self.0 = index } } /// Re-export module resolver trait. #[cfg(not(feature = "no_module"))] pub use resolvers::ModuleResolver; /// Re-export module resolvers. #[cfg(not(feature = "no_module"))] pub mod resolvers;