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(¶ms.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(), ¶m_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;