quad_compat_rhai/module/
mod.rs

1//! Module defining external-loaded modules for Rhai.
2
3use crate::ast::{FnAccess, Ident};
4use crate::func::{
5    shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction,
6    SendSync,
7};
8use crate::parser::IdentifierBuilder;
9use crate::tokenizer::Token;
10use crate::types::dynamic::Variant;
11use crate::{
12    calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult,
13    Identifier, ImmutableString, NativeCallContext, Shared, StaticVec,
14};
15#[cfg(feature = "no_std")]
16use std::prelude::v1::*;
17use std::{
18    any::TypeId,
19    collections::{BTreeMap, BTreeSet},
20    fmt,
21    iter::{empty, once},
22    num::NonZeroUsize,
23    ops::{Add, AddAssign, Deref, DerefMut},
24};
25
26/// A type representing the namespace of a function.
27#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
28pub enum FnNamespace {
29    /// Expose to global namespace.
30    Global,
31    /// Module namespace only.
32    Internal,
33}
34
35/// Data structure containing a single registered function.
36#[derive(Debug, Clone)]
37pub struct FuncInfo {
38    /// Function instance.
39    pub func: Shared<CallableFunction>,
40    /// Function namespace.
41    pub namespace: FnNamespace,
42    /// Function access mode.
43    pub access: FnAccess,
44    /// Function name.
45    pub name: Identifier,
46    /// Number of parameters.
47    pub params: usize,
48    /// Parameter types (if applicable).
49    pub param_types: StaticVec<TypeId>,
50    /// Parameter names (if available).
51    #[cfg(feature = "metadata")]
52    pub param_names: StaticVec<Identifier>,
53}
54
55impl FuncInfo {
56    /// Generate a signature of the function.
57    /// Exported under the `metadata` feature only.
58    #[cfg(feature = "metadata")]
59    #[must_use]
60    pub fn gen_signature(&self) -> String {
61        let mut sig = format!("{}(", self.name);
62
63        if !self.param_names.is_empty() {
64            let mut params: StaticVec<Box<str>> =
65                self.param_names.iter().map(|s| s.as_str().into()).collect();
66            let return_type = params.pop().unwrap_or_else(|| "()".into());
67            sig.push_str(&params.join(", "));
68            if &*return_type != "()" {
69                sig.push_str(") -> ");
70                sig.push_str(&return_type);
71            } else {
72                sig.push(')');
73            }
74        } else {
75            for x in 0..self.params {
76                sig.push('_');
77                if x < self.params - 1 {
78                    sig.push_str(", ");
79                }
80            }
81
82            if self.func.is_script() {
83                sig.push(')');
84            } else {
85                sig.push_str(") -> ?");
86            }
87        }
88
89        sig
90    }
91}
92
93/// _(internals)_ Calculate a non-zero [`u64`] hash key from a namespace-qualified function name and parameter types.
94/// Exported under the `internals` feature only.
95///
96/// Module names are passed in via `&str` references from an iterator.
97/// Parameter types are passed in via [`TypeId`] values from an iterator.
98///
99/// # Note
100///
101/// The first module name is skipped.  Hashing starts from the _second_ module in the chain.
102#[inline]
103pub fn calc_native_fn_hash(
104    modules: impl Iterator<Item = impl AsRef<str>>,
105    fn_name: impl AsRef<str>,
106    params: &[TypeId],
107) -> u64 {
108    let hash_script = calc_qualified_fn_hash(modules, fn_name.as_ref(), params.len());
109    let hash_params = calc_fn_params_hash(params.iter().cloned());
110    combine_hashes(hash_script, hash_params)
111}
112
113/// A module which may contain variables, sub-modules, external Rust functions,
114/// and/or script-defined functions.
115#[derive(Clone)]
116pub struct Module {
117    /// ID identifying the module.
118    id: Option<Identifier>,
119    /// Is this module internal?
120    pub(crate) internal: bool,
121    /// Is this module part of a standard library?
122    pub(crate) standard: bool,
123    /// Sub-modules.
124    modules: BTreeMap<Identifier, Shared<Module>>,
125    /// [`Module`] variables.
126    variables: BTreeMap<Identifier, Dynamic>,
127    /// Flattened collection of all [`Module`] variables, including those in sub-modules.
128    all_variables: BTreeMap<u64, Dynamic>,
129    /// External Rust functions.
130    functions: BTreeMap<u64, Box<FuncInfo>>,
131    /// Flattened collection of all external Rust functions, native or scripted.
132    /// including those in sub-modules.
133    all_functions: BTreeMap<u64, Shared<CallableFunction>>,
134    /// Iterator functions, keyed by the type producing the iterator.
135    type_iterators: BTreeMap<TypeId, IteratorFn>,
136    /// Flattened collection of iterator functions, including those in sub-modules.
137    all_type_iterators: BTreeMap<TypeId, IteratorFn>,
138    /// Is the [`Module`] indexed?
139    indexed: bool,
140    /// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
141    contains_indexed_global_functions: bool,
142    /// Interned strings
143    identifiers: IdentifierBuilder,
144}
145
146impl Default for Module {
147    #[inline(always)]
148    fn default() -> Self {
149        Self::new()
150    }
151}
152
153impl fmt::Debug for Module {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        let mut d = f.debug_struct("Module");
156
157        self.id.as_ref().map(|id| d.field("id", id));
158
159        if !self.modules.is_empty() {
160            d.field(
161                "modules",
162                &self
163                    .modules
164                    .keys()
165                    .map(|m| m.as_str())
166                    .collect::<BTreeSet<_>>(),
167            );
168        }
169        if !self.variables.is_empty() {
170            d.field("vars", &self.variables);
171        }
172        if !self.functions.is_empty() {
173            d.field(
174                "functions",
175                &self
176                    .iter_fn()
177                    .map(|f| f.func.to_string())
178                    .collect::<BTreeSet<_>>(),
179            );
180        }
181        d.finish()
182    }
183}
184
185impl<M: AsRef<Module>> Add<M> for &Module {
186    type Output = Module;
187
188    #[inline]
189    fn add(self, rhs: M) -> Self::Output {
190        let mut module = self.clone();
191        module.merge(rhs.as_ref());
192        module
193    }
194}
195
196impl<M: AsRef<Module>> Add<M> for Module {
197    type Output = Self;
198
199    #[inline(always)]
200    fn add(mut self, rhs: M) -> Self::Output {
201        self.merge(rhs.as_ref());
202        self
203    }
204}
205
206impl<M: Into<Module>> AddAssign<M> for Module {
207    #[inline(always)]
208    fn add_assign(&mut self, rhs: M) {
209        self.combine(rhs.into());
210    }
211}
212
213impl Module {
214    /// Create a new [`Module`].
215    ///
216    /// # Example
217    ///
218    /// ```
219    /// # use quad_compat_rhai::Module;
220    /// let mut module = Module::new();
221    /// module.set_var("answer", 42_i64);
222    /// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
223    /// ```
224    #[inline]
225    #[must_use]
226    pub fn new() -> Self {
227        Self {
228            id: None,
229            internal: false,
230            standard: false,
231            modules: BTreeMap::new(),
232            variables: BTreeMap::new(),
233            all_variables: BTreeMap::new(),
234            functions: BTreeMap::new(),
235            all_functions: BTreeMap::new(),
236            type_iterators: BTreeMap::new(),
237            all_type_iterators: BTreeMap::new(),
238            indexed: true,
239            contains_indexed_global_functions: false,
240            identifiers: IdentifierBuilder::new(),
241        }
242    }
243
244    /// Get the ID of the [`Module`], if any.
245    ///
246    /// # Example
247    ///
248    /// ```
249    /// # use quad_compat_rhai::Module;
250    /// let mut module = Module::new();
251    /// module.set_id("hello");
252    /// assert_eq!(module.id(), Some("hello"));
253    /// ```
254    #[inline]
255    #[must_use]
256    pub fn id(&self) -> Option<&str> {
257        self.id_raw().map(|s| s.as_str())
258    }
259
260    /// Get the ID of the [`Module`] as an [`Identifier`], if any.
261    #[inline(always)]
262    #[must_use]
263    pub(crate) const fn id_raw(&self) -> Option<&Identifier> {
264        self.id.as_ref()
265    }
266
267    /// Set the ID of the [`Module`].
268    ///
269    /// # Example
270    ///
271    /// ```
272    /// # use quad_compat_rhai::Module;
273    /// let mut module = Module::new();
274    /// module.set_id("hello");
275    /// assert_eq!(module.id(), Some("hello"));
276    /// ```
277    #[inline(always)]
278    pub fn set_id(&mut self, id: impl Into<Identifier>) -> &mut Self {
279        self.id = Some(id.into());
280        self
281    }
282    /// Clear the ID of the [`Module`].
283    ///
284    /// # Example
285    ///
286    /// ```
287    /// # use quad_compat_rhai::Module;
288    /// let mut module = Module::new();
289    /// module.set_id("hello");
290    /// assert_eq!(module.id(), Some("hello"));
291    /// module.clear_id();
292    /// assert_eq!(module.id(), None);
293    /// ```
294    #[inline(always)]
295    pub fn clear_id(&mut self) -> &mut Self {
296        self.id = None;
297        self
298    }
299
300    /// Is the [`Module`] empty?
301    ///
302    /// # Example
303    ///
304    /// ```
305    /// # use quad_compat_rhai::Module;
306    /// let module = Module::new();
307    /// assert!(module.is_empty());
308    /// ```
309    #[inline]
310    #[must_use]
311    pub fn is_empty(&self) -> bool {
312        self.indexed
313            && !self.contains_indexed_global_functions
314            && self.functions.is_empty()
315            && self.all_functions.is_empty()
316            && self.variables.is_empty()
317            && self.all_variables.is_empty()
318            && self.modules.is_empty()
319            && self.type_iterators.is_empty()
320            && self.all_type_iterators.is_empty()
321    }
322
323    /// Is the [`Module`] indexed?
324    ///
325    /// A module must be indexed before it can be used in an `import` statement.
326    ///
327    /// # Example
328    ///
329    /// ```
330    /// # use quad_compat_rhai::Module;
331    /// let mut module = Module::new();
332    /// assert!(module.is_indexed());
333    ///
334    /// module.set_native_fn("foo", |x: &mut i64, y: i64| { *x = y; Ok(()) });
335    /// assert!(!module.is_indexed());
336    ///
337    /// # #[cfg(not(feature = "no_module"))]
338    /// # {
339    /// module.build_index();
340    /// assert!(module.is_indexed());
341    /// # }
342    /// ```
343    #[inline(always)]
344    #[must_use]
345    pub const fn is_indexed(&self) -> bool {
346        self.indexed
347    }
348
349    /// Generate signatures for all the non-private functions in the [`Module`].
350    /// Exported under the `metadata` feature only.
351    #[cfg(feature = "metadata")]
352    #[inline]
353    pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
354        self.iter_fn()
355            .filter(|&f| match f.access {
356                FnAccess::Public => true,
357                FnAccess::Private => false,
358            })
359            .map(|f| f.gen_signature())
360    }
361
362    /// Does a variable exist in the [`Module`]?
363    ///
364    /// # Example
365    ///
366    /// ```
367    /// # use quad_compat_rhai::Module;
368    /// let mut module = Module::new();
369    /// module.set_var("answer", 42_i64);
370    /// assert!(module.contains_var("answer"));
371    /// ```
372    #[inline(always)]
373    #[must_use]
374    pub fn contains_var(&self, name: &str) -> bool {
375        self.variables.contains_key(name)
376    }
377
378    /// Get the value of a [`Module`] variable.
379    ///
380    /// # Example
381    ///
382    /// ```
383    /// # use quad_compat_rhai::Module;
384    /// let mut module = Module::new();
385    /// module.set_var("answer", 42_i64);
386    /// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
387    /// ```
388    #[inline]
389    #[must_use]
390    pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
391        self.get_var(name).and_then(Dynamic::try_cast::<T>)
392    }
393
394    /// Get a [`Module`] variable as a [`Dynamic`].
395    ///
396    /// # Example
397    ///
398    /// ```
399    /// # use quad_compat_rhai::Module;
400    /// let mut module = Module::new();
401    /// module.set_var("answer", 42_i64);
402    /// assert_eq!(module.get_var("answer").expect("answer should exist").cast::<i64>(), 42);
403    /// ```
404    #[inline(always)]
405    #[must_use]
406    pub fn get_var(&self, name: &str) -> Option<Dynamic> {
407        self.variables.get(name).cloned()
408    }
409
410    /// Set a variable into the [`Module`].
411    ///
412    /// If there is an existing variable of the same name, it is replaced.
413    ///
414    /// # Example
415    ///
416    /// ```
417    /// # use quad_compat_rhai::Module;
418    /// let mut module = Module::new();
419    /// module.set_var("answer", 42_i64);
420    /// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
421    /// ```
422    #[inline]
423    pub fn set_var(
424        &mut self,
425        name: impl Into<Identifier>,
426        value: impl Variant + Clone,
427    ) -> &mut Self {
428        let ident = name.into();
429        let value = Dynamic::from(value);
430
431        if self.indexed {
432            let hash_var = crate::calc_qualified_var_hash(once(""), &ident);
433            self.all_variables.insert(hash_var, value.clone());
434        }
435        self.variables.insert(ident, value);
436        self
437    }
438
439    /// Get a reference to a namespace-qualified variable.
440    /// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards.
441    #[cfg(not(feature = "no_module"))]
442    #[inline]
443    pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box<EvalAltResult>> {
444        self.all_variables.get(&hash_var).ok_or_else(|| {
445            EvalAltResult::ErrorVariableNotFound(String::new(), crate::Position::NONE).into()
446        })
447    }
448
449    /// Set a script-defined function into the [`Module`].
450    ///
451    /// If there is an existing function of the same name and number of arguments, it is replaced.
452    #[cfg(not(feature = "no_function"))]
453    #[inline]
454    pub fn set_script_fn(&mut self, fn_def: impl Into<Shared<crate::ast::ScriptFnDef>>) -> u64 {
455        let fn_def = fn_def.into();
456
457        // None + function name + number of arguments.
458        let num_params = fn_def.params.len();
459        let hash_script = crate::calc_fn_hash(&fn_def.name, num_params);
460        let mut param_names = fn_def.params.clone();
461        param_names.push("Dynamic".into());
462        self.functions.insert(
463            hash_script,
464            FuncInfo {
465                name: fn_def.name.clone(),
466                namespace: FnNamespace::Internal,
467                access: fn_def.access,
468                params: num_params,
469                param_types: StaticVec::new_const(),
470                #[cfg(feature = "metadata")]
471                param_names,
472                func: Into::<CallableFunction>::into(fn_def).into(),
473            }
474            .into(),
475        );
476        self.indexed = false;
477        self.contains_indexed_global_functions = false;
478        hash_script
479    }
480
481    /// Get a shared reference to the script-defined function in the [`Module`] based on name
482    /// and number of parameters.
483    #[cfg(not(feature = "no_function"))]
484    #[inline]
485    #[must_use]
486    pub fn get_script_fn(
487        &self,
488        name: impl AsRef<str>,
489        num_params: usize,
490    ) -> Option<&Shared<crate::ast::ScriptFnDef>> {
491        if self.functions.is_empty() {
492            None
493        } else {
494            let name = name.as_ref();
495
496            self.iter_fn()
497                .find(|f| f.params == num_params && f.name == name)
498                .and_then(|f| f.func.get_script_fn_def())
499        }
500    }
501
502    /// Get a mutable reference to the underlying [`BTreeMap`] of sub-modules.
503    ///
504    /// # WARNING
505    ///
506    /// By taking a mutable reference, it is assumed that some sub-modules will be modified.
507    /// Thus the [`Module`] is automatically set to be non-indexed.
508    #[cfg(not(feature = "no_module"))]
509    #[inline]
510    #[must_use]
511    pub(crate) fn sub_modules_mut(&mut self) -> &mut BTreeMap<Identifier, Shared<Module>> {
512        // We must assume that the user has changed the sub-modules
513        // (otherwise why take a mutable reference?)
514        self.all_functions.clear();
515        self.all_variables.clear();
516        self.all_type_iterators.clear();
517        self.indexed = false;
518        self.contains_indexed_global_functions = false;
519
520        &mut self.modules
521    }
522
523    /// Does a sub-module exist in the [`Module`]?
524    ///
525    /// # Example
526    ///
527    /// ```
528    /// # use quad_compat_rhai::Module;
529    /// let mut module = Module::new();
530    /// let sub_module = Module::new();
531    /// module.set_sub_module("question", sub_module);
532    /// assert!(module.contains_sub_module("question"));
533    /// ```
534    #[inline(always)]
535    #[must_use]
536    pub fn contains_sub_module(&self, name: &str) -> bool {
537        self.modules.contains_key(name)
538    }
539
540    /// Get a sub-module in the [`Module`].
541    ///
542    /// # Example
543    ///
544    /// ```
545    /// # use quad_compat_rhai::Module;
546    /// let mut module = Module::new();
547    /// let sub_module = Module::new();
548    /// module.set_sub_module("question", sub_module);
549    /// assert!(module.get_sub_module("question").is_some());
550    /// ```
551    #[inline]
552    #[must_use]
553    pub fn get_sub_module(&self, name: &str) -> Option<&Module> {
554        self.modules.get(name).map(|m| m.as_ref())
555    }
556
557    /// Set a sub-module into the [`Module`].
558    ///
559    /// If there is an existing sub-module of the same name, it is replaced.
560    ///
561    /// # Example
562    ///
563    /// ```
564    /// # use quad_compat_rhai::Module;
565    /// let mut module = Module::new();
566    /// let sub_module = Module::new();
567    /// module.set_sub_module("question", sub_module);
568    /// assert!(module.get_sub_module("question").is_some());
569    /// ```
570    #[inline]
571    pub fn set_sub_module(
572        &mut self,
573        name: impl Into<Identifier>,
574        sub_module: impl Into<Shared<Module>>,
575    ) -> &mut Self {
576        self.modules.insert(name.into(), sub_module.into());
577        self.indexed = false;
578        self.contains_indexed_global_functions = false;
579        self
580    }
581
582    /// Does the particular Rust function exist in the [`Module`]?
583    ///
584    /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
585    ///
586    /// # Example
587    ///
588    /// ```
589    /// # use quad_compat_rhai::Module;
590    /// let mut module = Module::new();
591    /// let hash = module.set_native_fn("calc", || Ok(42_i64));
592    /// assert!(module.contains_fn(hash));
593    /// ```
594    #[inline(always)]
595    #[must_use]
596    pub fn contains_fn(&self, hash_fn: u64) -> bool {
597        self.functions.contains_key(&hash_fn)
598    }
599
600    /// Update the metadata (parameter names/types and return type) of a registered function.
601    /// Exported under the `metadata` feature only.
602    ///
603    /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
604    ///
605    /// ## Parameter Names and Types
606    ///
607    /// Each parameter name/type pair should be a single string of the format: `var_name: type`.
608    ///
609    /// ## Return Type
610    ///
611    /// The _last entry_ in the list should be the _return type_ of the function.
612    /// In other words, the number of entries should be one larger than the number of parameters.
613    #[cfg(feature = "metadata")]
614    #[inline]
615    pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[impl AsRef<str>]) -> &mut Self {
616        let param_names = arg_names
617            .iter()
618            .map(|name| self.identifiers.get(name.as_ref()))
619            .collect();
620
621        if let Some(f) = self.functions.get_mut(&hash_fn) {
622            f.param_names = param_names;
623        }
624
625        self
626    }
627
628    /// Update the namespace of a registered function.
629    ///
630    /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
631    #[inline]
632    pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
633        if let Some(f) = self.functions.get_mut(&hash_fn) {
634            f.namespace = namespace;
635            self.indexed = false;
636            self.contains_indexed_global_functions = false;
637        }
638        self
639    }
640
641    /// Remap type ID.
642    #[inline]
643    #[must_use]
644    fn map_type(map: bool, type_id: TypeId) -> TypeId {
645        if !map {
646            return type_id;
647        }
648        if type_id == TypeId::of::<&str>() {
649            // Map &str to ImmutableString
650            return TypeId::of::<ImmutableString>();
651        }
652        if type_id == TypeId::of::<String>() {
653            // Map String to ImmutableString
654            return TypeId::of::<ImmutableString>();
655        }
656
657        type_id
658    }
659
660    /// Set a Rust function into the [`Module`], returning a non-zero hash key.
661    ///
662    /// If there is an existing Rust function of the same hash, it is replaced.
663    ///
664    /// # WARNING - Low Level API
665    ///
666    /// This function is very low level.
667    #[inline]
668    pub fn set_fn(
669        &mut self,
670        name: impl AsRef<str> + Into<Identifier>,
671        namespace: FnNamespace,
672        access: FnAccess,
673        arg_names: Option<&[&str]>,
674        arg_types: &[TypeId],
675        func: CallableFunction,
676    ) -> u64 {
677        let _arg_names = arg_names;
678        let is_method = func.is_method();
679
680        let mut param_types: StaticVec<_> = arg_types
681            .iter()
682            .cloned()
683            .enumerate()
684            .map(|(i, type_id)| Self::map_type(!is_method || i > 0, type_id))
685            .collect();
686        param_types.shrink_to_fit();
687
688        #[cfg(feature = "metadata")]
689        let mut param_names: StaticVec<_> = _arg_names
690            .iter()
691            .flat_map(|&p| p.iter())
692            .map(|&arg| self.identifiers.get(arg))
693            .collect();
694        #[cfg(feature = "metadata")]
695        param_names.shrink_to_fit();
696
697        let hash_fn = calc_native_fn_hash(empty::<&str>(), name.as_ref(), &param_types);
698
699        self.functions.insert(
700            hash_fn,
701            FuncInfo {
702                name: self.identifiers.get(name),
703                namespace,
704                access,
705                params: param_types.len(),
706                param_types,
707                #[cfg(feature = "metadata")]
708                param_names,
709                func: func.into(),
710            }
711            .into(),
712        );
713
714        self.indexed = false;
715        self.contains_indexed_global_functions = false;
716
717        hash_fn
718    }
719
720    /// Set a Rust function taking a reference to the scripting [`Engine`][crate::Engine],
721    /// the current set of functions, plus a list of mutable [`Dynamic`] references
722    /// into the [`Module`], returning a non-zero hash key.
723    ///
724    /// Use this to register a built-in function which must reference settings on the scripting
725    /// [`Engine`][crate::Engine] (e.g. to prevent growing an array beyond the allowed maximum size),
726    /// or to call a script-defined function in the current evaluation context.
727    ///
728    /// If there is a similar existing Rust function, it is replaced.
729    ///
730    /// # WARNING - Low Level API
731    ///
732    /// This function is very low level.
733    ///
734    /// ## Arguments
735    ///
736    /// A list of [`TypeId`]'s is taken as the argument types.
737    ///
738    /// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][Dynamic],
739    /// which is guaranteed to contain enough arguments of the correct types.
740    ///
741    /// The function is assumed to be a _method_, meaning that the first argument should not be consumed.
742    /// All other arguments can be consumed.
743    ///
744    /// To access a primary argument value (i.e. cloning is cheap), use: `args[n].as_xxx().unwrap()`
745    ///
746    /// To access an argument value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
747    /// Notice that this will _consume_ the argument, replacing it with `()`.
748    ///
749    /// To access the first mutable argument, use `args.get_mut(0).unwrap()`
750    ///
751    /// # Function Metadata
752    ///
753    /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
754    ///
755    /// # Example
756    ///
757    /// ```
758    /// use quad_compat_rhai::{Module, FnNamespace, FnAccess};
759    ///
760    /// let mut module = Module::new();
761    /// let hash = module.set_raw_fn("double_or_not", FnNamespace::Internal, FnAccess::Public,
762    ///                 // Pass parameter types via a slice with TypeId's
763    ///                 &[std::any::TypeId::of::<i64>(), std::any::TypeId::of::<bool>()],
764    ///                 // Fixed closure signature
765    ///                 |context, args| {
766    ///                     // 'args' is guaranteed to be the right length and of the correct types
767    ///
768    ///                     // Get the second parameter by 'consuming' it
769    ///                     let double = std::mem::take(args[1]).cast::<bool>();
770    ///                     // Since it is a primary type, it can also be cheaply copied
771    ///                     let double = args[1].clone_cast::<bool>();
772    ///                     // Get a mutable reference to the first argument.
773    ///                     let mut x = args[0].write_lock::<i64>().unwrap();
774    ///
775    ///                     let orig = *x;
776    ///
777    ///                     if double {
778    ///                         *x *= 2;            // the first argument can be mutated
779    ///                     }
780    ///
781    ///                     Ok(orig)                // return Result<T, Box<EvalAltResult>>
782    ///                 });
783    ///
784    /// assert!(module.contains_fn(hash));
785    /// ```
786    #[inline(always)]
787    pub fn set_raw_fn<N, T, F>(
788        &mut self,
789        name: N,
790        namespace: FnNamespace,
791        access: FnAccess,
792        arg_types: &[TypeId],
793        func: F,
794    ) -> u64
795    where
796        N: AsRef<str> + Into<Identifier>,
797        T: Variant + Clone,
798        F: Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
799            + SendSync
800            + 'static,
801    {
802        let f =
803            move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
804
805        self.set_fn(
806            name,
807            namespace,
808            access,
809            None,
810            arg_types,
811            CallableFunction::from_method(Box::new(f)),
812        )
813    }
814
815    /// Set a Rust function into the [`Module`], returning a non-zero hash key.
816    ///
817    /// If there is a similar existing Rust function, it is replaced.
818    ///
819    /// # Function Namespace
820    ///
821    /// The default function namespace is [`FnNamespace::Internal`].
822    /// Use [`update_fn_namespace`][Module::update_fn_namespace] to change it.
823    ///
824    /// # Function Metadata
825    ///
826    /// No metadata for the function is registered.
827    /// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata.
828    ///
829    /// # Example
830    ///
831    /// ```
832    /// # use quad_compat_rhai::Module;
833    /// let mut module = Module::new();
834    /// let hash = module.set_native_fn("calc", || Ok(42_i64));
835    /// assert!(module.contains_fn(hash));
836    /// ```
837    #[inline(always)]
838    pub fn set_native_fn<ARGS, N, T, F>(&mut self, name: N, func: F) -> u64
839    where
840        N: AsRef<str> + Into<Identifier>,
841        T: Variant + Clone,
842        F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
843    {
844        self.set_fn(
845            name,
846            FnNamespace::Internal,
847            FnAccess::Public,
848            None,
849            &F::param_types(),
850            func.into_callable_function(),
851        )
852    }
853
854    /// Set a Rust getter function taking one mutable parameter, returning a non-zero hash key.
855    /// This function is automatically exposed to the global namespace.
856    ///
857    /// If there is a similar existing Rust getter function, it is replaced.
858    ///
859    /// # Function Metadata
860    ///
861    /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
862    ///
863    /// # Example
864    ///
865    /// ```
866    /// # use quad_compat_rhai::Module;
867    /// let mut module = Module::new();
868    /// let hash = module.set_getter_fn("value", |x: &mut i64| { Ok(*x) });
869    /// assert!(module.contains_fn(hash));
870    /// ```
871    #[cfg(not(feature = "no_object"))]
872    #[inline(always)]
873    pub fn set_getter_fn<ARGS, A, T, F>(&mut self, name: impl AsRef<str>, func: F) -> u64
874    where
875        A: Variant + Clone,
876        T: Variant + Clone,
877        F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
878        F: Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
879    {
880        self.set_fn(
881            &crate::engine::make_getter(name),
882            FnNamespace::Global,
883            FnAccess::Public,
884            None,
885            &F::param_types(),
886            func.into_callable_function(),
887        )
888    }
889
890    /// Set a Rust setter function taking two parameters (the first one mutable) into the [`Module`],
891    /// returning a non-zero hash key.
892    /// This function is automatically exposed to the global namespace.
893    ///
894    /// If there is a similar existing setter Rust function, it is replaced.
895    ///
896    /// # Function Metadata
897    ///
898    /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
899    ///
900    /// # Example
901    ///
902    /// ```
903    /// use quad_compat_rhai::{Module, ImmutableString};
904    ///
905    /// let mut module = Module::new();
906    /// let hash = module.set_setter_fn("value", |x: &mut i64, y: ImmutableString| {
907    ///     *x = y.len() as i64;
908    ///     Ok(())
909    /// });
910    /// assert!(module.contains_fn(hash));
911    /// ```
912    #[cfg(not(feature = "no_object"))]
913    #[inline(always)]
914    pub fn set_setter_fn<ARGS, A, B, F>(&mut self, name: impl AsRef<str>, func: F) -> u64
915    where
916        A: Variant + Clone,
917        B: Variant + Clone,
918        F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>,
919        F: Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
920    {
921        self.set_fn(
922            &crate::engine::make_setter(name),
923            FnNamespace::Global,
924            FnAccess::Public,
925            None,
926            &F::param_types(),
927            func.into_callable_function(),
928        )
929    }
930
931    /// Set a Rust index getter taking two parameters (the first one mutable) into the [`Module`],
932    /// returning a non-zero hash key.
933    /// This function is automatically exposed to the global namespace.
934    ///
935    /// If there is a similar existing setter Rust function, it is replaced.
936    ///
937    /// # Panics
938    ///
939    /// Panics if the type is [`Array`][crate::Array] or [`Map`][crate::Map].
940    /// Indexers for arrays, object maps and strings cannot be registered.
941    ///
942    /// # Function Metadata
943    ///
944    /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
945    ///
946    /// # Example
947    ///
948    /// ```
949    /// use quad_compat_rhai::{Module, ImmutableString};
950    ///
951    /// let mut module = Module::new();
952    /// let hash = module.set_indexer_get_fn(|x: &mut i64, y: ImmutableString| {
953    ///     Ok(*x + y.len() as i64)
954    /// });
955    /// assert!(module.contains_fn(hash));
956    /// ```
957    #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
958    #[inline]
959    pub fn set_indexer_get_fn<ARGS, A, B, T, F>(&mut self, func: F) -> u64
960    where
961        A: Variant + Clone,
962        B: Variant + Clone,
963        T: Variant + Clone,
964        F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
965        F: Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
966    {
967        #[cfg(not(feature = "no_index"))]
968        if TypeId::of::<A>() == TypeId::of::<crate::Array>() {
969            panic!("Cannot register indexer for arrays.");
970        }
971        #[cfg(not(feature = "no_object"))]
972        if TypeId::of::<A>() == TypeId::of::<crate::Map>() {
973            panic!("Cannot register indexer for object maps.");
974        }
975        if TypeId::of::<A>() == TypeId::of::<String>()
976            || TypeId::of::<A>() == TypeId::of::<&str>()
977            || TypeId::of::<A>() == TypeId::of::<ImmutableString>()
978        {
979            panic!("Cannot register indexer for strings.");
980        }
981
982        self.set_fn(
983            crate::engine::FN_IDX_GET,
984            FnNamespace::Global,
985            FnAccess::Public,
986            None,
987            &F::param_types(),
988            func.into_callable_function(),
989        )
990    }
991
992    /// Set a Rust index setter taking three parameters (the first one mutable) into the [`Module`],
993    /// returning a non-zero hash key.
994    /// This function is automatically exposed to the global namespace.
995    ///
996    /// If there is a similar existing Rust function, it is replaced.
997    ///
998    /// # Panics
999    ///
1000    /// Panics if the type is [`Array`][crate::Array] or [`Map`][crate::Map].
1001    /// Indexers for arrays, object maps and strings cannot be registered.
1002    ///
1003    /// # Function Metadata
1004    ///
1005    /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
1006    ///
1007    /// # Example
1008    ///
1009    /// ```
1010    /// use quad_compat_rhai::{Module, ImmutableString};
1011    ///
1012    /// let mut module = Module::new();
1013    /// let hash = module.set_indexer_set_fn(|x: &mut i64, y: ImmutableString, value: i64| {
1014    ///     *x = y.len() as i64 + value; Ok(())
1015    /// });
1016    /// assert!(module.contains_fn(hash));
1017    /// ```
1018    #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
1019    #[inline]
1020    pub fn set_indexer_set_fn<ARGS, A, B, C, F>(&mut self, func: F) -> u64
1021    where
1022        A: Variant + Clone,
1023        B: Variant + Clone,
1024        C: Variant + Clone,
1025        F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>,
1026        F: Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
1027    {
1028        #[cfg(not(feature = "no_index"))]
1029        if TypeId::of::<A>() == TypeId::of::<crate::Array>() {
1030            panic!("Cannot register indexer for arrays.");
1031        }
1032        #[cfg(not(feature = "no_object"))]
1033        if TypeId::of::<A>() == TypeId::of::<crate::Map>() {
1034            panic!("Cannot register indexer for object maps.");
1035        }
1036        if TypeId::of::<A>() == TypeId::of::<String>()
1037            || TypeId::of::<A>() == TypeId::of::<&str>()
1038            || TypeId::of::<A>() == TypeId::of::<ImmutableString>()
1039        {
1040            panic!("Cannot register indexer for strings.");
1041        }
1042
1043        self.set_fn(
1044            crate::engine::FN_IDX_SET,
1045            FnNamespace::Global,
1046            FnAccess::Public,
1047            None,
1048            &F::param_types(),
1049            func.into_callable_function(),
1050        )
1051    }
1052
1053    /// Set a pair of Rust index getter and setter functions, returning both non-zero hash keys.
1054    /// This is a short-hand for [`set_indexer_get_fn`][Module::set_indexer_get_fn] and
1055    /// [`set_indexer_set_fn`][Module::set_indexer_set_fn].
1056    ///
1057    /// If there are similar existing Rust functions, they are replaced.
1058    ///
1059    /// # Panics
1060    ///
1061    /// Panics if the type is [`Array`][crate::Array] or [`Map`][crate::Map].
1062    /// Indexers for arrays, object maps and strings cannot be registered.
1063    ///
1064    /// # Function Metadata
1065    ///
1066    /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata.
1067    ///
1068    /// # Example
1069    ///
1070    /// ```
1071    /// use quad_compat_rhai::{Module, ImmutableString};
1072    ///
1073    /// let mut module = Module::new();
1074    /// let (hash_get, hash_set) = module.set_indexer_get_set_fn(
1075    ///     |x: &mut i64, y: ImmutableString| {
1076    ///         Ok(*x + y.len() as i64)
1077    ///     },
1078    ///     |x: &mut i64, y: ImmutableString, value: i64| {
1079    ///         *x = y.len() as i64 + value; Ok(())
1080    ///     }
1081    /// );
1082    /// assert!(module.contains_fn(hash_get));
1083    /// assert!(module.contains_fn(hash_set));
1084    /// ```
1085    #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
1086    #[inline(always)]
1087    pub fn set_indexer_get_set_fn<A, B, T>(
1088        &mut self,
1089        get_fn: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
1090        set_fn: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
1091    ) -> (u64, u64)
1092    where
1093        A: Variant + Clone,
1094        B: Variant + Clone,
1095        T: Variant + Clone,
1096    {
1097        (
1098            self.set_indexer_get_fn(get_fn),
1099            self.set_indexer_set_fn(set_fn),
1100        )
1101    }
1102
1103    /// Get a Rust function.
1104    ///
1105    /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
1106    #[inline]
1107    #[must_use]
1108    pub(crate) fn get_fn(&self, hash_fn: u64) -> Option<&CallableFunction> {
1109        self.functions.get(&hash_fn).map(|f| f.func.as_ref())
1110    }
1111
1112    /// Does the particular namespace-qualified function exist in the [`Module`]?
1113    ///
1114    /// The [`u64`] hash is calculated by [`build_index`][Module::build_index].
1115    #[inline(always)]
1116    #[must_use]
1117    pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
1118        self.all_functions.contains_key(&hash_fn)
1119    }
1120
1121    /// Get a namespace-qualified function.
1122    ///
1123    /// The [`u64`] hash is calculated by [`build_index`][Module::build_index].
1124    #[inline]
1125    #[must_use]
1126    pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
1127        self.all_functions
1128            .get(&hash_qualified_fn)
1129            .map(|f| f.as_ref())
1130    }
1131
1132    /// Combine another [`Module`] into this [`Module`].
1133    /// The other [`Module`] is _consumed_ to merge into this [`Module`].
1134    #[inline]
1135    pub fn combine(&mut self, other: Self) -> &mut Self {
1136        self.modules.extend(other.modules.into_iter());
1137        self.variables.extend(other.variables.into_iter());
1138        self.functions.extend(other.functions.into_iter());
1139        self.type_iterators.extend(other.type_iterators.into_iter());
1140        self.all_functions.clear();
1141        self.all_variables.clear();
1142        self.all_type_iterators.clear();
1143        self.indexed = false;
1144        self.contains_indexed_global_functions = false;
1145        self.identifiers += other.identifiers;
1146        self
1147    }
1148
1149    /// Combine another [`Module`] into this [`Module`].
1150    /// The other [`Module`] is _consumed_ to merge into this [`Module`].
1151    /// Sub-modules are flattened onto the root [`Module`], with higher level overriding lower level.
1152    #[inline]
1153    pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
1154        other.modules.into_iter().for_each(|(_, m)| {
1155            self.combine_flatten(shared_take_or_clone(m));
1156        });
1157        self.variables.extend(other.variables.into_iter());
1158        self.functions.extend(other.functions.into_iter());
1159        self.type_iterators.extend(other.type_iterators.into_iter());
1160        self.all_functions.clear();
1161        self.all_variables.clear();
1162        self.all_type_iterators.clear();
1163        self.indexed = false;
1164        self.contains_indexed_global_functions = false;
1165        self.identifiers += other.identifiers;
1166        self
1167    }
1168
1169    /// Polyfill this [`Module`] with another [`Module`].
1170    /// Only items not existing in this [`Module`] are added.
1171    #[inline]
1172    pub fn fill_with(&mut self, other: &Self) -> &mut Self {
1173        other.modules.iter().for_each(|(k, v)| {
1174            if !self.modules.contains_key(k) {
1175                self.modules.insert(k.clone(), v.clone());
1176            }
1177        });
1178        other.variables.iter().for_each(|(k, v)| {
1179            if !self.variables.contains_key(k) {
1180                self.variables.insert(k.clone(), v.clone());
1181            }
1182        });
1183        other.functions.iter().for_each(|(&k, v)| {
1184            self.functions.entry(k).or_insert_with(|| v.clone());
1185        });
1186        other.type_iterators.iter().for_each(|(&k, &v)| {
1187            self.type_iterators.entry(k).or_insert(v);
1188        });
1189        self.all_functions.clear();
1190        self.all_variables.clear();
1191        self.all_type_iterators.clear();
1192        self.indexed = false;
1193        self.contains_indexed_global_functions = false;
1194        self.identifiers.merge(&other.identifiers);
1195        self
1196    }
1197
1198    /// Merge another [`Module`] into this [`Module`].
1199    #[inline(always)]
1200    pub fn merge(&mut self, other: &Self) -> &mut Self {
1201        self.merge_filtered(other, &|_, _, _, _, _| true)
1202    }
1203
1204    /// Merge another [`Module`] into this [`Module`] based on a filter predicate.
1205    pub(crate) fn merge_filtered(
1206        &mut self,
1207        other: &Self,
1208        _filter: &impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
1209    ) -> &mut Self {
1210        #[cfg(not(feature = "no_function"))]
1211        other.modules.iter().for_each(|(k, v)| {
1212            let mut m = Self::new();
1213            m.merge_filtered(v, _filter);
1214            self.set_sub_module(k.clone(), m);
1215        });
1216        #[cfg(feature = "no_function")]
1217        self.modules
1218            .extend(other.modules.iter().map(|(k, v)| (k.clone(), v.clone())));
1219
1220        self.variables
1221            .extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone())));
1222        self.functions.extend(
1223            other
1224                .functions
1225                .iter()
1226                .filter(|&(_, f)| {
1227                    _filter(
1228                        f.namespace,
1229                        f.access,
1230                        f.func.is_script(),
1231                        f.name.as_str(),
1232                        f.params,
1233                    )
1234                })
1235                .map(|(&k, v)| (k, v.clone())),
1236        );
1237
1238        self.type_iterators.extend(other.type_iterators.iter());
1239        self.all_functions.clear();
1240        self.all_variables.clear();
1241        self.all_type_iterators.clear();
1242        self.indexed = false;
1243        self.contains_indexed_global_functions = false;
1244        self.identifiers.merge(&other.identifiers);
1245        self
1246    }
1247
1248    /// Filter out the functions, retaining only some script-defined functions based on a filter predicate.
1249    #[cfg(not(feature = "no_function"))]
1250    #[inline]
1251    pub(crate) fn retain_script_functions(
1252        &mut self,
1253        filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
1254    ) -> &mut Self {
1255        self.functions = std::mem::take(&mut self.functions)
1256            .into_iter()
1257            .filter(|(_, f)| {
1258                if f.func.is_script() {
1259                    filter(f.namespace, f.access, f.name.as_str(), f.params)
1260                } else {
1261                    false
1262                }
1263            })
1264            .collect();
1265
1266        self.all_functions.clear();
1267        self.all_variables.clear();
1268        self.all_type_iterators.clear();
1269        self.indexed = false;
1270        self.contains_indexed_global_functions = false;
1271        self
1272    }
1273
1274    /// Get the number of variables, functions and type iterators in the [`Module`].
1275    #[inline(always)]
1276    #[must_use]
1277    pub fn count(&self) -> (usize, usize, usize) {
1278        (
1279            self.variables.len(),
1280            self.functions.len(),
1281            self.type_iterators.len(),
1282        )
1283    }
1284
1285    /// Get an iterator to the sub-modules in the [`Module`].
1286    #[inline]
1287    pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, Shared<Module>)> {
1288        self.modules.iter().map(|(k, m)| (k.as_str(), m.clone()))
1289    }
1290
1291    /// Get an iterator to the variables in the [`Module`].
1292    #[inline]
1293    pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
1294        self.variables.iter().map(|(k, v)| (k.as_str(), v))
1295    }
1296
1297    /// Get an iterator to the functions in the [`Module`].
1298    #[inline]
1299    #[allow(dead_code)]
1300    pub(crate) fn iter_fn(&self) -> impl Iterator<Item = &FuncInfo> {
1301        self.functions.values().map(Box::as_ref)
1302    }
1303
1304    /// Get an iterator over all script-defined functions in the [`Module`].
1305    ///
1306    /// Function metadata includes:
1307    /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]).
1308    /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]).
1309    /// 3) Function name (as string slice).
1310    /// 4) Number of parameters.
1311    /// 5) Shared reference to function definition [`ScriptFnDef`][crate::ast::ScriptFnDef].
1312    #[cfg(not(feature = "no_function"))]
1313    #[inline]
1314    pub(crate) fn iter_script_fn(
1315        &self,
1316    ) -> impl Iterator<
1317        Item = (
1318            FnNamespace,
1319            FnAccess,
1320            &str,
1321            usize,
1322            &Shared<crate::ast::ScriptFnDef>,
1323        ),
1324    > + '_ {
1325        self.iter_fn().filter(|&f| f.func.is_script()).map(|f| {
1326            (
1327                f.namespace,
1328                f.access,
1329                f.name.as_str(),
1330                f.params,
1331                f.func.get_script_fn_def().expect("scripted function"),
1332            )
1333        })
1334    }
1335
1336    /// Get an iterator over all script-defined functions in the [`Module`].
1337    ///
1338    /// Function metadata includes:
1339    /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]).
1340    /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]).
1341    /// 3) Function name (as string slice).
1342    /// 4) Number of parameters.
1343    #[cfg(not(feature = "no_function"))]
1344    #[cfg(not(feature = "internals"))]
1345    #[inline]
1346    pub fn iter_script_fn_info(
1347        &self,
1348    ) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize)> {
1349        self.iter_fn()
1350            .filter(|&f| f.func.is_script())
1351            .map(|f| (f.namespace, f.access, f.name.as_str(), f.params))
1352    }
1353
1354    /// _(internals)_ Get an iterator over all script-defined functions in the [`Module`].
1355    /// Exported under the `internals` feature only.
1356    ///
1357    /// Function metadata includes:
1358    /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]).
1359    /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]).
1360    /// 3) Function name (as string slice).
1361    /// 4) Number of parameters.
1362    /// 5) _(internals)_ Shared reference to function definition [`ScriptFnDef`][crate::ast::ScriptFnDef].
1363    #[cfg(not(feature = "no_function"))]
1364    #[cfg(feature = "internals")]
1365    #[inline(always)]
1366    #[must_use]
1367    pub fn iter_script_fn_info(
1368        &self,
1369    ) -> impl Iterator<
1370        Item = (
1371            FnNamespace,
1372            FnAccess,
1373            &str,
1374            usize,
1375            &Shared<crate::ast::ScriptFnDef>,
1376        ),
1377    > {
1378        self.iter_script_fn()
1379    }
1380
1381    /// Create a new [`Module`] by evaluating an [`AST`][crate::AST].
1382    ///
1383    /// The entire [`AST`][crate::AST] is encapsulated into each function, allowing functions
1384    /// to cross-call each other.  Functions in the global namespace, plus all functions
1385    /// defined in the [`Module`], are _merged_ into a _unified_ namespace before each call.
1386    /// Therefore, all functions will be found.
1387    ///
1388    /// # Example
1389    ///
1390    /// ```
1391    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
1392    /// use quad_compat_rhai::{Engine, Module, Scope};
1393    ///
1394    /// let engine = Engine::new();
1395    /// let ast = engine.compile("let answer = 42; export answer;")?;
1396    /// let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
1397    /// assert!(module.contains_var("answer"));
1398    /// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
1399    /// # Ok(())
1400    /// # }
1401    /// ```
1402    #[cfg(not(feature = "no_module"))]
1403    pub fn eval_ast_as_new(
1404        scope: crate::Scope,
1405        ast: &crate::AST,
1406        engine: &crate::Engine,
1407    ) -> Result<Self, Box<EvalAltResult>> {
1408        let mut scope = scope;
1409        let mut mods = crate::engine::Imports::new();
1410        let orig_mods_len = mods.len();
1411
1412        // Run the script
1413        engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast, 0)?;
1414
1415        // Create new module
1416        let mut module =
1417            scope
1418                .into_iter()
1419                .fold(Module::new(), |mut module, (_, value, mut aliases)| {
1420                    // Variables with an alias left in the scope become module variables
1421                    match aliases.len() {
1422                        0 => (),
1423                        1 => {
1424                            let alias = aliases.pop().expect("not empty");
1425                            module.set_var(alias, value);
1426                        }
1427                        _ => {
1428                            let last_alias = aliases.pop().expect("not empty");
1429                            aliases.into_iter().for_each(|alias| {
1430                                module.set_var(alias, value.clone());
1431                            });
1432                            // Avoid cloning the last value
1433                            module.set_var(last_alias, value);
1434                        }
1435                    }
1436                    module
1437                });
1438
1439        // Extra modules left in the scope become sub-modules
1440        let mut func_mods = crate::engine::Imports::new();
1441
1442        mods.into_iter().skip(orig_mods_len).for_each(|(alias, m)| {
1443            func_mods.push(alias.clone(), m.clone());
1444            module.set_sub_module(alias, m);
1445        });
1446
1447        // Non-private functions defined become module functions
1448        #[cfg(not(feature = "no_function"))]
1449        if ast.has_functions() {
1450            ast.shared_lib()
1451                .iter_fn()
1452                .filter(|&f| match f.access {
1453                    FnAccess::Public => true,
1454                    FnAccess::Private => false,
1455                })
1456                .filter(|&f| f.func.is_script())
1457                .for_each(|f| {
1458                    // Encapsulate AST environment
1459                    let mut func = f
1460                        .func
1461                        .get_script_fn_def()
1462                        .expect("scripted function")
1463                        .as_ref()
1464                        .clone();
1465                    func.lib = Some(ast.shared_lib().clone());
1466                    func.mods = func_mods.clone();
1467                    module.set_script_fn(func);
1468                });
1469        }
1470
1471        if let Some(s) = ast.source_raw() {
1472            module.set_id(s.clone());
1473        } else {
1474            module.clear_id();
1475        }
1476
1477        module.build_index();
1478
1479        Ok(module)
1480    }
1481
1482    /// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
1483    ///
1484    /// # Panics
1485    ///
1486    /// Panics if the [`Module`] is not yet indexed via [`build_index`][Module::build_index].
1487    #[inline(always)]
1488    #[must_use]
1489    pub fn contains_indexed_global_functions(&self) -> bool {
1490        self.contains_indexed_global_functions
1491    }
1492
1493    /// Scan through all the sub-modules in the [`Module`] and build a hash index of all
1494    /// variables and functions as one flattened namespace.
1495    ///
1496    /// If the [`Module`] is already indexed, this method has no effect.
1497    pub fn build_index(&mut self) -> &mut Self {
1498        // Collect a particular module.
1499        fn index_module<'a>(
1500            module: &'a Module,
1501            path: &mut Vec<&'a str>,
1502            variables: &mut BTreeMap<u64, Dynamic>,
1503            functions: &mut BTreeMap<u64, Shared<CallableFunction>>,
1504            type_iterators: &mut BTreeMap<TypeId, IteratorFn>,
1505        ) -> bool {
1506            let mut contains_indexed_global_functions = false;
1507
1508            module.modules.iter().for_each(|(name, m)| {
1509                // Index all the sub-modules first.
1510                path.push(name);
1511                if index_module(m, path, variables, functions, type_iterators) {
1512                    contains_indexed_global_functions = true;
1513                }
1514                path.pop();
1515            });
1516
1517            // Index all variables
1518            module.variables.iter().for_each(|(var_name, value)| {
1519                let hash_var = crate::calc_qualified_var_hash(path.iter().copied(), var_name);
1520                variables.insert(hash_var, value.clone());
1521            });
1522
1523            // Index type iterators
1524            module.type_iterators.iter().for_each(|(&type_id, func)| {
1525                type_iterators.insert(type_id, *func);
1526                contains_indexed_global_functions = true;
1527            });
1528
1529            // Index all Rust functions
1530            module.functions.iter().for_each(|(&hash, f)| {
1531                match f.namespace {
1532                    FnNamespace::Global => {
1533                        // Flatten all functions with global namespace
1534                        functions.insert(hash, f.func.clone());
1535                        contains_indexed_global_functions = true;
1536                    }
1537                    FnNamespace::Internal => (),
1538                }
1539                match f.access {
1540                    FnAccess::Public => (),
1541                    FnAccess::Private => return, // Do not index private functions
1542                }
1543
1544                if !f.func.is_script() {
1545                    let hash_qualified_fn =
1546                        calc_native_fn_hash(path.iter().cloned(), f.name.as_str(), &f.param_types);
1547                    functions.insert(hash_qualified_fn, f.func.clone());
1548                } else if cfg!(not(feature = "no_function")) {
1549                    let hash_qualified_script = crate::calc_qualified_fn_hash(
1550                        path.iter().cloned(),
1551                        f.name.as_str(),
1552                        f.params,
1553                    );
1554                    functions.insert(hash_qualified_script, f.func.clone());
1555                }
1556            });
1557
1558            contains_indexed_global_functions
1559        }
1560
1561        if !self.indexed {
1562            let mut path = Vec::with_capacity(4);
1563            let mut variables = BTreeMap::new();
1564            let mut functions = BTreeMap::new();
1565            let mut type_iterators = BTreeMap::new();
1566
1567            path.push("");
1568
1569            self.contains_indexed_global_functions = index_module(
1570                self,
1571                &mut path,
1572                &mut variables,
1573                &mut functions,
1574                &mut type_iterators,
1575            );
1576
1577            self.all_variables = variables;
1578            self.all_functions = functions;
1579            self.all_type_iterators = type_iterators;
1580            self.indexed = true;
1581        }
1582
1583        self
1584    }
1585
1586    /// Does a type iterator exist in the entire module tree?
1587    #[inline(always)]
1588    #[must_use]
1589    pub fn contains_qualified_iter(&self, id: TypeId) -> bool {
1590        self.all_type_iterators.contains_key(&id)
1591    }
1592
1593    /// Does a type iterator exist in the module?
1594    #[inline(always)]
1595    #[must_use]
1596    pub fn contains_iter(&self, id: TypeId) -> bool {
1597        self.type_iterators.contains_key(&id)
1598    }
1599
1600    /// Set a type iterator into the [`Module`].
1601    #[inline]
1602    pub fn set_iter(&mut self, type_id: TypeId, func: IteratorFn) -> &mut Self {
1603        if self.indexed {
1604            self.all_type_iterators.insert(type_id, func);
1605            self.contains_indexed_global_functions = true;
1606        }
1607        self.type_iterators.insert(type_id, func);
1608        self
1609    }
1610
1611    /// Set a type iterator into the [`Module`].
1612    #[inline]
1613    pub fn set_iterable<T>(&mut self) -> &mut Self
1614    where
1615        T: Variant + Clone + IntoIterator,
1616        <T as IntoIterator>::Item: Variant + Clone,
1617    {
1618        self.set_iter(TypeId::of::<T>(), |obj: Dynamic| {
1619            Box::new(obj.cast::<T>().into_iter().map(Dynamic::from))
1620        })
1621    }
1622
1623    /// Set an iterator type into the [`Module`] as a type iterator.
1624    #[inline]
1625    pub fn set_iterator<T>(&mut self) -> &mut Self
1626    where
1627        T: Variant + Clone + Iterator,
1628        <T as Iterator>::Item: Variant + Clone,
1629    {
1630        self.set_iter(TypeId::of::<T>(), |obj: Dynamic| {
1631            Box::new(obj.cast::<T>().map(Dynamic::from))
1632        })
1633    }
1634
1635    /// Get the specified type iterator.
1636    #[inline]
1637    #[must_use]
1638    pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option<IteratorFn> {
1639        self.all_type_iterators.get(&id).cloned()
1640    }
1641
1642    /// Get the specified type iterator.
1643    #[inline]
1644    #[must_use]
1645    pub(crate) fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
1646        self.type_iterators.get(&id).cloned()
1647    }
1648}
1649
1650/// _(internals)_ A chain of [module][Module] names to namespace-qualify a variable or function call.
1651/// Exported under the `internals` feature only.
1652///
1653/// A [`u64`] offset to the current [`Scope`][crate::Scope] is cached for quick search purposes.
1654///
1655/// A [`StaticVec`] is used because most namespace-qualified access contains only one level,
1656/// and it is wasteful to always allocate a [`Vec`] with one element.
1657#[derive(Clone, Eq, PartialEq, Default, Hash)]
1658pub struct NamespaceRef {
1659    index: Option<NonZeroUsize>,
1660    path: StaticVec<Ident>,
1661}
1662
1663impl fmt::Debug for NamespaceRef {
1664    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1665        if let Some(index) = self.index {
1666            write!(f, "{} -> ", index)?;
1667        }
1668
1669        f.write_str(
1670            &self
1671                .path
1672                .iter()
1673                .map(|Ident { name, .. }| name.as_str())
1674                .collect::<StaticVec<_>>()
1675                .join(Token::DoubleColon.literal_syntax()),
1676        )
1677    }
1678}
1679
1680impl fmt::Display for NamespaceRef {
1681    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1682        f.write_str(
1683            &self
1684                .path
1685                .iter()
1686                .map(|Ident { name, .. }| name.as_str())
1687                .collect::<StaticVec<_>>()
1688                .join(Token::DoubleColon.literal_syntax()),
1689        )
1690    }
1691}
1692
1693impl Deref for NamespaceRef {
1694    type Target = StaticVec<Ident>;
1695
1696    #[inline(always)]
1697    fn deref(&self) -> &Self::Target {
1698        &self.path
1699    }
1700}
1701
1702impl DerefMut for NamespaceRef {
1703    #[inline(always)]
1704    fn deref_mut(&mut self) -> &mut Self::Target {
1705        &mut self.path
1706    }
1707}
1708
1709impl From<Vec<Ident>> for NamespaceRef {
1710    #[inline(always)]
1711    fn from(mut path: Vec<Ident>) -> Self {
1712        path.shrink_to_fit();
1713        Self {
1714            index: None,
1715            path: path.into(),
1716        }
1717    }
1718}
1719
1720impl From<StaticVec<Ident>> for NamespaceRef {
1721    #[inline(always)]
1722    fn from(mut path: StaticVec<Ident>) -> Self {
1723        path.shrink_to_fit();
1724        Self { index: None, path }
1725    }
1726}
1727
1728impl NamespaceRef {
1729    /// Create a new [`NamespaceRef`].
1730    #[inline(always)]
1731    #[must_use]
1732    pub const fn new() -> Self {
1733        Self {
1734            index: None,
1735            path: StaticVec::new_const(),
1736        }
1737    }
1738    /// Get the [`Scope`][crate::Scope] index offset.
1739    #[inline(always)]
1740    #[must_use]
1741    pub(crate) const fn index(&self) -> Option<NonZeroUsize> {
1742        self.index
1743    }
1744    /// Set the [`Scope`][crate::Scope] index offset.
1745    #[cfg(not(feature = "no_module"))]
1746    #[inline(always)]
1747    pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) {
1748        self.index = index
1749    }
1750}
1751
1752#[cfg(not(feature = "no_module"))]
1753pub use resolvers::ModuleResolver;
1754
1755/// Module containing all built-in [module resolvers][ModuleResolver].
1756#[cfg(not(feature = "no_module"))]
1757pub mod resolvers;