spo_rhai/module/mod.rs
1//! Module defining external-loaded modules for Rhai.
2
3#[cfg(feature = "metadata")]
4use crate::api::formatting::format_param_type_for_display;
5use crate::ast::FnAccess;
6use crate::func::{
7 shared_take_or_clone, FnIterator, RhaiFunc, RhaiNativeFunc, SendSync, StraightHashMap,
8};
9use crate::types::{dynamic::Variant, BloomFilterU64, CustomTypeInfo, CustomTypesCollection};
10use crate::{
11 calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, Identifier, ImmutableString,
12 RhaiResultOf, Shared, SharedModule, SmartString,
13};
14use bitflags::bitflags;
15#[cfg(feature = "no_std")]
16use hashbrown::hash_map::Entry;
17#[cfg(not(feature = "no_std"))]
18use std::collections::hash_map::Entry;
19#[cfg(feature = "no_std")]
20use std::prelude::v1::*;
21use std::{
22 any::{type_name, TypeId},
23 collections::BTreeMap,
24 fmt,
25 ops::{Add, AddAssign},
26};
27
28#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
29use crate::func::register::Mut;
30
31/// Initial capacity of the hashmap for functions.
32const FN_MAP_SIZE: usize = 16;
33
34/// A type representing the namespace of a function.
35#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
36#[cfg_attr(
37 feature = "metadata",
38 derive(serde::Serialize, serde::Deserialize),
39 serde(rename_all = "camelCase")
40)]
41#[non_exhaustive]
42pub enum FnNamespace {
43 /// Module namespace only.
44 ///
45 /// Ignored under `no_module`.
46 Internal,
47 /// Expose to global namespace.
48 Global,
49}
50
51impl FnNamespace {
52 /// Is this a module namespace?
53 #[inline(always)]
54 #[must_use]
55 pub const fn is_module_namespace(self) -> bool {
56 match self {
57 Self::Internal => true,
58 Self::Global => false,
59 }
60 }
61 /// Is this a global namespace?
62 #[inline(always)]
63 #[must_use]
64 pub const fn is_global_namespace(self) -> bool {
65 match self {
66 Self::Internal => false,
67 Self::Global => true,
68 }
69 }
70}
71
72/// A type containing the metadata of a single registered function.
73#[derive(Debug, Clone, Eq, PartialEq, Hash)]
74#[non_exhaustive]
75pub struct FuncMetadata {
76 /// Hash value.
77 pub hash: u64,
78 /// Function namespace.
79 pub namespace: FnNamespace,
80 /// Function access mode.
81 pub access: FnAccess,
82 /// Function name.
83 pub name: Identifier,
84 /// Number of parameters.
85 pub num_params: usize,
86 /// Parameter types (if applicable).
87 pub param_types: FnArgsVec<TypeId>,
88 /// Parameter names and types (if available).
89 #[cfg(feature = "metadata")]
90 pub params_info: FnArgsVec<Identifier>,
91 /// Return type name.
92 #[cfg(feature = "metadata")]
93 pub return_type: Identifier,
94 /// Comments.
95 #[cfg(feature = "metadata")]
96 pub comments: crate::StaticVec<SmartString>,
97}
98
99impl FuncMetadata {
100 /// _(metadata)_ Generate a signature of the function.
101 /// Exported under the `metadata` feature only.
102 #[cfg(feature = "metadata")]
103 #[must_use]
104 pub fn gen_signature<'a>(
105 &'a self,
106 type_mapper: impl Fn(&'a str) -> std::borrow::Cow<'a, str>,
107 ) -> String {
108 let mut signature = format!("{}(", self.name);
109
110 let return_type = format_param_type_for_display(&self.return_type, true);
111
112 if self.params_info.is_empty() {
113 for x in 0..self.num_params {
114 signature.push('_');
115 if x < self.num_params - 1 {
116 signature.push_str(", ");
117 }
118 }
119 } else {
120 let params = self
121 .params_info
122 .iter()
123 .map(|param| {
124 let mut segment = param.splitn(2, ':');
125 let name = match segment.next().unwrap().trim() {
126 "" => "_".into(),
127 s => s,
128 };
129 let result: std::borrow::Cow<_> = segment.next().map_or_else(
130 || name.into(),
131 |typ| {
132 format!(
133 "{name}: {}",
134 format_param_type_for_display(&type_mapper(typ), false)
135 )
136 .into()
137 },
138 );
139 result
140 })
141 .collect::<crate::FnArgsVec<_>>();
142 signature.push_str(¶ms.join(", "));
143 }
144 signature.push(')');
145
146 if !return_type.is_empty() {
147 signature.push_str(" -> ");
148 signature.push_str(&return_type);
149 }
150
151 signature
152 }
153}
154
155/// _(internals)_ Calculate a [`u64`] hash key from a namespace-qualified function name and parameter types.
156/// Exported under the `internals` feature only.
157///
158/// Module names are passed in via `&str` references from an iterator.
159/// Parameter types are passed in via [`TypeId`] values from an iterator.
160///
161/// # Note
162///
163/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
164#[inline]
165pub fn calc_native_fn_hash<'a>(
166 modules: impl IntoIterator<Item = &'a str, IntoIter = impl ExactSizeIterator<Item = &'a str>>,
167 fn_name: &str,
168 params: &[TypeId],
169) -> u64 {
170 calc_fn_hash_full(
171 calc_fn_hash(modules, fn_name, params.len()),
172 params.iter().copied(),
173 )
174}
175
176/// Type for fine-tuned module function registration.
177#[derive(Debug, Clone, Eq, PartialEq, Hash)]
178pub struct FuncRegistration {
179 /// Function metadata.
180 metadata: FuncMetadata,
181 /// Is the function pure?
182 purity: Option<bool>,
183 /// Is the function volatile?
184 volatility: Option<bool>,
185}
186
187impl FuncRegistration {
188 /// Create a new [`FuncRegistration`].
189 ///
190 /// # Defaults
191 ///
192 /// * **Accessibility**: The function namespace is [`FnNamespace::Internal`].
193 ///
194 /// * **Purity**: The function is assumed to be _pure_ unless it is a property setter or an index setter.
195 ///
196 /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s).
197 ///
198 /// * **Metadata**: No metadata for the function is registered.
199 ///
200 /// ```
201 /// # use rhai::{Module, FuncRegistration, FnNamespace};
202 /// let mut module = Module::new();
203 ///
204 /// fn inc(x: i64) -> i64 { x + 1 }
205 ///
206 /// let f = FuncRegistration::new("inc")
207 /// .with_namespace(FnNamespace::Global)
208 /// .set_into_module(&mut module, inc);
209 ///
210 /// let hash = f.hash;
211 ///
212 /// assert!(module.contains_fn(hash));
213 /// ```
214 pub fn new(name: impl Into<Identifier>) -> Self {
215 Self {
216 metadata: FuncMetadata {
217 hash: 0,
218 name: name.into(),
219 namespace: FnNamespace::Internal,
220 access: FnAccess::Public,
221 num_params: 0,
222 param_types: <_>::default(),
223 #[cfg(feature = "metadata")]
224 params_info: <_>::default(),
225 #[cfg(feature = "metadata")]
226 return_type: "".into(),
227 #[cfg(feature = "metadata")]
228 comments: <_>::default(),
229 },
230 purity: None,
231 volatility: None,
232 }
233 }
234 /// Create a new [`FuncRegistration`] for a property getter.
235 ///
236 /// Not available under `no_object`.
237 ///
238 /// # Defaults
239 ///
240 /// * **Accessibility**: The function namespace is [`FnNamespace::Global`].
241 ///
242 /// * **Purity**: The function is assumed to be _pure_.
243 ///
244 /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s).
245 ///
246 /// * **Metadata**: No metadata for the function is registered.
247 #[cfg(not(feature = "no_object"))]
248 #[inline(always)]
249 pub fn new_getter(prop: impl AsRef<str>) -> Self {
250 Self::new(crate::engine::make_getter(prop.as_ref())).with_namespace(FnNamespace::Global)
251 }
252 /// Create a new [`FuncRegistration`] for a property setter.
253 ///
254 /// Not available under `no_object`.
255 ///
256 /// # Defaults
257 ///
258 /// * **Accessibility**: The function namespace is [`FnNamespace::Global`].
259 ///
260 /// * **Purity**: The function is assumed to be _no-pure_.
261 ///
262 /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s).
263 ///
264 /// * **Metadata**: No metadata for the function is registered.
265 #[cfg(not(feature = "no_object"))]
266 #[inline(always)]
267 pub fn new_setter(prop: impl AsRef<str>) -> Self {
268 Self::new(crate::engine::make_setter(prop.as_ref()))
269 .with_namespace(FnNamespace::Global)
270 .with_purity(false)
271 }
272 /// Create a new [`FuncRegistration`] for an index getter.
273 ///
274 /// Not available under both `no_index` and `no_object`.
275 ///
276 /// # Defaults
277 ///
278 /// * **Accessibility**: The function namespace is [`FnNamespace::Global`].
279 ///
280 /// * **Purity**: The function is assumed to be _pure_.
281 ///
282 /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s).
283 ///
284 /// * **Metadata**: No metadata for the function is registered.
285 #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
286 #[inline(always)]
287 pub fn new_index_getter() -> Self {
288 Self::new(crate::engine::FN_IDX_GET).with_namespace(FnNamespace::Global)
289 }
290 /// Create a new [`FuncRegistration`] for an index setter.
291 ///
292 /// Not available under both `no_index` and `no_object`.
293 ///
294 /// # Defaults
295 ///
296 /// * **Accessibility**: The function namespace is [`FnNamespace::Global`].
297 ///
298 /// * **Purity**: The function is assumed to be _no-pure_.
299 ///
300 /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s).
301 ///
302 /// * **Metadata**: No metadata for the function is registered.
303 #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
304 #[inline(always)]
305 pub fn new_index_setter() -> Self {
306 Self::new(crate::engine::FN_IDX_SET)
307 .with_namespace(FnNamespace::Global)
308 .with_purity(false)
309 }
310 /// Set the [namespace][`FnNamespace`] of the function.
311 pub fn with_namespace(mut self, namespace: FnNamespace) -> Self {
312 self.metadata.namespace = namespace;
313 self
314 }
315 /// Set whether the function is _pure_.
316 /// A pure function has no side effects.
317 pub fn with_purity(mut self, pure: bool) -> Self {
318 self.purity = Some(pure);
319 self
320 }
321 /// Set whether the function is _volatile_.
322 /// A volatile function does not guarantee the same result for the same input(s).
323 pub fn with_volatility(mut self, volatile: bool) -> Self {
324 self.volatility = Some(volatile);
325 self
326 }
327 /// _(metadata)_ Set the function's parameter names and/or types.
328 /// Exported under the `metadata` feature only.
329 ///
330 /// The input is a list of strings, each of which is either a parameter name or a parameter name/type pair.
331 ///
332 /// The _last_ string should be the _type_ of the return value.
333 ///
334 /// # Parameter Examples
335 ///
336 /// `"foo: &str"` <- parameter name = `foo`, type = `&str`
337 /// `"bar"` <- parameter name = `bar`, type unknown
338 /// `"_: i64"` <- parameter name unknown, type = `i64`
339 /// `"MyType"` <- parameter name unknown, type = `MyType`
340 #[cfg(feature = "metadata")]
341 pub fn with_params_info<S: AsRef<str>>(mut self, params: impl IntoIterator<Item = S>) -> Self {
342 self.metadata.params_info = params.into_iter().map(|s| s.as_ref().into()).collect();
343 self
344 }
345 /// _(metadata)_ Set the function's doc-comments.
346 /// Exported under the `metadata` feature only.
347 ///
348 /// The input is a list of strings, each of which is either a block of single-line doc-comments
349 /// or a single block doc-comment.
350 ///
351 /// ## Comments
352 ///
353 /// Single-line doc-comments typically start with `///` and should be merged, with line-breaks,
354 /// into a single string without a final termination line-break.
355 ///
356 /// Block doc-comments typically start with `/**` and end with `*/` and should be kept in a
357 /// separate string.
358 ///
359 /// Leading white-spaces should be stripped, and each string should always start with the
360 /// corresponding doc-comment leader: `///` or `/**`.
361 ///
362 /// Each line in non-block doc-comments should start with `///`.
363 #[cfg(feature = "metadata")]
364 pub fn with_comments<S: AsRef<str>>(mut self, comments: impl IntoIterator<Item = S>) -> Self {
365 self.metadata.comments = comments.into_iter().map(|s| s.as_ref().into()).collect();
366 self
367 }
368 /// Register the function into the specified [`Engine`].
369 #[inline]
370 pub fn register_into_engine<A: 'static, const N: usize, const X: bool, R, const F: bool, FUNC>(
371 self,
372 engine: &mut Engine,
373 func: FUNC,
374 ) -> &FuncMetadata
375 where
376 R: Variant + Clone,
377 FUNC: RhaiNativeFunc<A, N, X, R, F> + SendSync + 'static,
378 {
379 self.with_namespace(FnNamespace::Global)
380 .set_into_module(engine.global_namespace_mut(), func)
381 }
382 /// Register the function into the specified [`Module`].
383 #[inline]
384 pub fn set_into_module<A: 'static, const N: usize, const X: bool, R, const F: bool, FUNC>(
385 self,
386 module: &mut Module,
387 func: FUNC,
388 ) -> &FuncMetadata
389 where
390 R: Variant + Clone,
391 FUNC: RhaiNativeFunc<A, N, X, R, F> + SendSync + 'static,
392 {
393 let is_pure = self.purity.unwrap_or_else(|| {
394 // default to pure unless specified
395 let is_pure = true;
396
397 #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
398 let is_pure = is_pure
399 && (FUNC::num_params() != 3 || self.metadata.name != crate::engine::FN_IDX_SET);
400
401 #[cfg(not(feature = "no_object"))]
402 let is_pure = is_pure
403 && (FUNC::num_params() != 2
404 || !self.metadata.name.starts_with(crate::engine::FN_SET));
405 is_pure
406 });
407 let is_volatile = self.volatility.unwrap_or(false);
408
409 let func = func.into_rhai_function(is_pure, is_volatile);
410
411 // Clear flags
412 let mut reg = self;
413 reg.purity = None;
414 reg.volatility = None;
415
416 reg.set_into_module_raw(module, FUNC::param_types(), func)
417 }
418 /// Register the function into the specified [`Module`].
419 ///
420 /// # WARNING - Low Level API
421 ///
422 /// This function is very low level. It takes a list of [`TypeId`][std::any::TypeId]'s
423 /// indicating the actual types of the parameters.
424 ///
425 /// # Panics
426 ///
427 /// Panics if the type of the first parameter is [`Array`][crate::Array], [`Map`][crate::Map],
428 /// [`String`], [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT] and
429 /// the function name indicates that it is an index getter or setter.
430 ///
431 /// Indexers for arrays, object maps, strings and integers cannot be registered.
432 #[inline]
433 pub fn set_into_module_raw(
434 self,
435 module: &mut Module,
436 arg_types: impl AsRef<[TypeId]>,
437 func: RhaiFunc,
438 ) -> &FuncMetadata {
439 // Make sure that conflicting flags should not be set.
440 debug_assert!(self.purity.is_none());
441 debug_assert!(self.volatility.is_none());
442
443 let mut f = self.metadata;
444
445 f.num_params = arg_types.as_ref().len();
446 f.param_types.extend(arg_types.as_ref().iter().copied());
447
448 #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
449 if (f.name == crate::engine::FN_IDX_GET && f.num_params == 2)
450 || (f.name == crate::engine::FN_IDX_SET && f.num_params == 3)
451 {
452 if let Some(&type_id) = f.param_types.first() {
453 #[cfg(not(feature = "no_index"))]
454 assert!(
455 type_id != TypeId::of::<crate::Array>(),
456 "Cannot register indexer for arrays."
457 );
458
459 #[cfg(not(feature = "no_object"))]
460 assert!(
461 type_id != TypeId::of::<crate::Map>(),
462 "Cannot register indexer for object maps."
463 );
464
465 assert!(
466 type_id != TypeId::of::<String>()
467 && type_id != TypeId::of::<&str>()
468 && type_id != TypeId::of::<crate::ImmutableString>(),
469 "Cannot register indexer for strings."
470 );
471
472 assert!(
473 type_id != TypeId::of::<crate::INT>(),
474 "Cannot register indexer for integers."
475 );
476 }
477 }
478
479 let is_method = func.is_method();
480
481 f.param_types
482 .iter_mut()
483 .enumerate()
484 .for_each(|(i, type_id)| *type_id = Module::map_type(!is_method || i > 0, *type_id));
485
486 let is_dynamic = f
487 .param_types
488 .iter()
489 .any(|&type_id| type_id == TypeId::of::<Dynamic>());
490
491 #[cfg(feature = "metadata")]
492 if f.params_info.len() > f.param_types.len() {
493 f.return_type = f.params_info.pop().unwrap();
494 }
495
496 let hash_base = calc_fn_hash(None, &f.name, f.param_types.len());
497 let hash_fn = calc_fn_hash_full(hash_base, f.param_types.iter().copied());
498 f.hash = hash_fn;
499
500 // Catch hash collisions in testing environment only.
501 #[cfg(feature = "testing-environ")]
502 if let Some(fx) = module.functions.as_ref().and_then(|f| f.get(&hash_base)) {
503 unreachable!(
504 "Hash {} already exists when registering function {}:\n{:#?}",
505 hash_base, f.name, fx
506 );
507 }
508
509 if is_dynamic {
510 module.dynamic_functions_filter.mark(hash_base);
511 }
512
513 module
514 .flags
515 .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
516
517 let entry = match module
518 .functions
519 .get_or_insert_with(|| new_hash_map(FN_MAP_SIZE))
520 .entry(hash_fn)
521 {
522 Entry::Occupied(mut entry) => {
523 entry.insert((func, f.into()));
524 entry.into_mut()
525 }
526 Entry::Vacant(entry) => entry.insert((func, f.into())),
527 };
528
529 &*entry.1
530 }
531}
532
533bitflags! {
534 /// Bit-flags containing all status for [`Module`].
535 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
536 pub struct ModuleFlags: u8 {
537 /// Is the [`Module`] internal?
538 const INTERNAL = 0b0000_0001;
539 /// Is the [`Module`] part of a standard library?
540 const STANDARD_LIB = 0b0000_0010;
541 /// Is the [`Module`] indexed?
542 const INDEXED = 0b0000_0100;
543 /// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
544 const INDEXED_GLOBAL_FUNCTIONS = 0b0000_1000;
545 }
546}
547
548/// A module which may contain variables, sub-modules, external Rust functions,
549/// and/or script-defined functions.
550#[derive(Clone)]
551pub struct Module {
552 /// ID identifying the module.
553 id: Option<ImmutableString>,
554 /// Module documentation.
555 #[cfg(feature = "metadata")]
556 doc: SmartString,
557 /// Custom types.
558 custom_types: CustomTypesCollection,
559 /// Sub-modules.
560 modules: BTreeMap<Identifier, SharedModule>,
561 /// [`Module`] variables.
562 variables: BTreeMap<Identifier, Dynamic>,
563 /// Flattened collection of all [`Module`] variables, including those in sub-modules.
564 all_variables: Option<StraightHashMap<Dynamic>>,
565 /// Functions (both native Rust and scripted).
566 functions: Option<StraightHashMap<(RhaiFunc, Box<FuncMetadata>)>>,
567 /// Flattened collection of all functions, native Rust and scripted.
568 /// including those in sub-modules.
569 all_functions: Option<StraightHashMap<RhaiFunc>>,
570 /// Bloom filter on native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters.
571 dynamic_functions_filter: BloomFilterU64,
572 /// Iterator functions, keyed by the type producing the iterator.
573 type_iterators: BTreeMap<TypeId, Shared<FnIterator>>,
574 /// Flattened collection of iterator functions, including those in sub-modules.
575 all_type_iterators: BTreeMap<TypeId, Shared<FnIterator>>,
576 /// Flags.
577 pub(crate) flags: ModuleFlags,
578}
579
580impl Default for Module {
581 #[inline(always)]
582 #[must_use]
583 fn default() -> Self {
584 Self::new()
585 }
586}
587
588impl fmt::Debug for Module {
589 #[cold]
590 #[inline(never)]
591 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
592 let mut d = f.debug_struct("Module");
593
594 d.field("id", &self.id)
595 .field(
596 "custom_types",
597 &self.custom_types.iter().map(|(k, _)| k).collect::<Vec<_>>(),
598 )
599 .field(
600 "modules",
601 &self
602 .modules
603 .keys()
604 .map(SmartString::as_str)
605 .collect::<Vec<_>>(),
606 )
607 .field("vars", &self.variables)
608 .field(
609 "functions",
610 &self
611 .iter_fn()
612 .map(|(_f, _m)| {
613 #[cfg(not(feature = "metadata"))]
614 return _f.to_string();
615 #[cfg(feature = "metadata")]
616 return _m.gen_signature(|s| s.into());
617 })
618 .collect::<Vec<_>>(),
619 )
620 .field("flags", &self.flags);
621
622 #[cfg(feature = "metadata")]
623 d.field("doc", &self.doc);
624
625 d.finish()
626 }
627}
628
629#[cfg(not(feature = "no_function"))]
630impl<T: IntoIterator<Item = Shared<crate::ast::ScriptFuncDef>>> From<T> for Module {
631 fn from(iter: T) -> Self {
632 let mut module = Self::new();
633 iter.into_iter().for_each(|fn_def| {
634 module.set_script_fn(fn_def);
635 });
636 module
637 }
638}
639
640impl<M: AsRef<Module>> Add<M> for &Module {
641 type Output = Module;
642
643 #[inline]
644 fn add(self, rhs: M) -> Self::Output {
645 let mut module = self.clone();
646 module.merge(rhs.as_ref());
647 module
648 }
649}
650
651impl<M: AsRef<Self>> Add<M> for Module {
652 type Output = Self;
653
654 #[inline(always)]
655 fn add(mut self, rhs: M) -> Self::Output {
656 self.merge(rhs.as_ref());
657 self
658 }
659}
660
661impl<M: Into<Self>> AddAssign<M> for Module {
662 #[inline(always)]
663 fn add_assign(&mut self, rhs: M) {
664 self.combine(rhs.into());
665 }
666}
667
668#[inline(always)]
669fn new_hash_map<T>(size: usize) -> StraightHashMap<T> {
670 StraightHashMap::with_capacity_and_hasher(size, <_>::default())
671}
672
673impl Module {
674 /// Create a new [`Module`].
675 ///
676 /// # Example
677 ///
678 /// ```
679 /// # use rhai::Module;
680 /// let mut module = Module::new();
681 /// module.set_var("answer", 42_i64);
682 /// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
683 /// ```
684 #[inline(always)]
685 #[must_use]
686 pub const fn new() -> Self {
687 Self {
688 id: None,
689 #[cfg(feature = "metadata")]
690 doc: SmartString::new_const(),
691 custom_types: CustomTypesCollection::new(),
692 modules: BTreeMap::new(),
693 variables: BTreeMap::new(),
694 all_variables: None,
695 functions: None,
696 all_functions: None,
697 dynamic_functions_filter: BloomFilterU64::new(),
698 type_iterators: BTreeMap::new(),
699 all_type_iterators: BTreeMap::new(),
700 flags: ModuleFlags::INDEXED,
701 }
702 }
703
704 /// Get the ID of the [`Module`], if any.
705 ///
706 /// # Example
707 ///
708 /// ```
709 /// # use rhai::Module;
710 /// let mut module = Module::new();
711 /// module.set_id("hello");
712 /// assert_eq!(module.id(), Some("hello"));
713 /// ```
714 #[inline]
715 #[must_use]
716 pub fn id(&self) -> Option<&str> {
717 self.id.as_deref()
718 }
719
720 /// Get the ID of the [`Module`] as an [`Identifier`], if any.
721 #[inline(always)]
722 #[must_use]
723 pub(crate) const fn id_raw(&self) -> Option<&ImmutableString> {
724 self.id.as_ref()
725 }
726
727 /// Set the ID of the [`Module`].
728 ///
729 /// If the string is empty, it is equivalent to clearing the ID.
730 ///
731 /// # Example
732 ///
733 /// ```
734 /// # use rhai::Module;
735 /// let mut module = Module::new();
736 /// module.set_id("hello");
737 /// assert_eq!(module.id(), Some("hello"));
738 /// ```
739 #[inline(always)]
740 pub fn set_id(&mut self, id: impl Into<ImmutableString>) -> &mut Self {
741 let id = id.into();
742 self.id = (!id.is_empty()).then_some(id);
743 self
744 }
745
746 /// Clear the ID of the [`Module`].
747 ///
748 /// # Example
749 ///
750 /// ```
751 /// # use rhai::Module;
752 /// let mut module = Module::new();
753 /// module.set_id("hello");
754 /// assert_eq!(module.id(), Some("hello"));
755 /// module.clear_id();
756 /// assert_eq!(module.id(), None);
757 /// ```
758 #[inline(always)]
759 pub fn clear_id(&mut self) -> &mut Self {
760 self.id = None;
761 self
762 }
763
764 /// Get the documentation of the [`Module`], if any.
765 /// Exported under the `metadata` feature only.
766 ///
767 /// # Example
768 ///
769 /// ```
770 /// # use rhai::Module;
771 /// let mut module = Module::new();
772 /// module.set_doc("//! This is my special module.");
773 /// assert_eq!(module.doc(), "//! This is my special module.");
774 /// ```
775 #[cfg(feature = "metadata")]
776 #[inline(always)]
777 #[must_use]
778 pub fn doc(&self) -> &str {
779 &self.doc
780 }
781
782 /// Set the documentation of the [`Module`].
783 /// Exported under the `metadata` feature only.
784 ///
785 /// If the string is empty, it is equivalent to clearing the documentation.
786 ///
787 /// # Example
788 ///
789 /// ```
790 /// # use rhai::Module;
791 /// let mut module = Module::new();
792 /// module.set_doc("//! This is my special module.");
793 /// assert_eq!(module.doc(), "//! This is my special module.");
794 /// ```
795 #[cfg(feature = "metadata")]
796 #[inline(always)]
797 pub fn set_doc(&mut self, doc: impl Into<crate::SmartString>) -> &mut Self {
798 self.doc = doc.into();
799 self
800 }
801
802 /// Clear the documentation of the [`Module`].
803 ///
804 /// # Example
805 ///
806 /// ```
807 /// # use rhai::Module;
808 /// let mut module = Module::new();
809 /// module.set_doc("//! This is my special module.");
810 /// assert_eq!(module.doc(), "//! This is my special module.");
811 /// module.clear_doc();
812 /// assert_eq!(module.doc(), "");
813 /// ```
814 #[cfg(feature = "metadata")]
815 #[inline(always)]
816 pub fn clear_doc(&mut self) -> &mut Self {
817 self.doc.clear();
818 self
819 }
820
821 /// Clear the [`Module`].
822 #[inline(always)]
823 pub fn clear(&mut self) {
824 #[cfg(feature = "metadata")]
825 self.doc.clear();
826 self.custom_types.clear();
827 self.modules.clear();
828 self.variables.clear();
829 self.all_variables = None;
830 self.functions = None;
831 self.all_functions = None;
832 self.dynamic_functions_filter.clear();
833 self.type_iterators.clear();
834 self.all_type_iterators.clear();
835 self.flags
836 .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
837 }
838
839 /// Map a custom type to a friendly display name.
840 ///
841 /// # Example
842 ///
843 /// ```
844 /// # use rhai::Module;
845 /// #[derive(Clone)]
846 /// struct TestStruct;
847 ///
848 /// let name = std::any::type_name::<TestStruct>();
849 ///
850 /// let mut module = Module::new();
851 ///
852 /// module.set_custom_type::<TestStruct>("MyType");
853 ///
854 /// assert_eq!(module.get_custom_type_display_by_name(name), Some("MyType"));
855 /// ```
856 #[inline(always)]
857 pub fn set_custom_type<T>(&mut self, name: &str) -> &mut Self {
858 self.custom_types.add_type::<T>(name);
859 self
860 }
861 /// Map a custom type to a friendly display name.
862 /// Exported under the `metadata` feature only.
863 ///
864 /// ## Comments
865 ///
866 /// Block doc-comments should be kept in a separate string slice.
867 ///
868 /// Line doc-comments should be merged, with line-breaks, into a single string slice without a final termination line-break.
869 ///
870 /// Leading white-spaces should be stripped, and each string slice always starts with the corresponding
871 /// doc-comment leader: `///` or `/**`.
872 ///
873 /// Each line in non-block doc-comments should start with `///`.
874 #[cfg(feature = "metadata")]
875 #[inline(always)]
876 pub fn set_custom_type_with_comments<T>(&mut self, name: &str, comments: &[&str]) -> &mut Self {
877 self.custom_types
878 .add_type_with_comments::<T>(name, comments);
879 self
880 }
881 /// Map a custom type to a friendly display name.
882 ///
883 /// ```
884 /// # use rhai::Module;
885 /// #[derive(Clone)]
886 /// struct TestStruct;
887 ///
888 /// let name = std::any::type_name::<TestStruct>();
889 ///
890 /// let mut module = Module::new();
891 ///
892 /// module.set_custom_type_raw(name, "MyType");
893 ///
894 /// assert_eq!(module.get_custom_type_display_by_name(name), Some("MyType"));
895 /// ```
896 #[inline(always)]
897 pub fn set_custom_type_raw(
898 &mut self,
899 type_name: impl Into<Identifier>,
900 display_name: impl Into<Identifier>,
901 ) -> &mut Self {
902 self.custom_types.add(type_name, display_name);
903 self
904 }
905 /// Map a custom type to a friendly display name.
906 /// Exported under the `metadata` feature only.
907 ///
908 /// ## Comments
909 ///
910 /// Block doc-comments should be kept in a separate string slice.
911 ///
912 /// Line doc-comments should be merged, with line-breaks, into a single string slice without a final termination line-break.
913 ///
914 /// Leading white-spaces should be stripped, and each string slice always starts with the corresponding
915 /// doc-comment leader: `///` or `/**`.
916 ///
917 /// Each line in non-block doc-comments should start with `///`.
918 #[cfg(feature = "metadata")]
919 #[inline(always)]
920 pub fn set_custom_type_with_comments_raw<C: Into<SmartString>>(
921 &mut self,
922 type_name: impl Into<Identifier>,
923 display_name: impl Into<Identifier>,
924 comments: impl IntoIterator<Item = C>,
925 ) -> &mut Self {
926 self.custom_types
927 .add_with_comments(type_name, display_name, comments);
928 self
929 }
930 /// Get the display name of a registered custom type.
931 ///
932 /// # Example
933 ///
934 /// ```
935 /// # use rhai::Module;
936 /// #[derive(Clone)]
937 /// struct TestStruct;
938 ///
939 /// let name = std::any::type_name::<TestStruct>();
940 ///
941 /// let mut module = Module::new();
942 ///
943 /// module.set_custom_type::<TestStruct>("MyType");
944 ///
945 /// assert_eq!(module.get_custom_type_display_by_name(name), Some("MyType"));
946 /// ```
947 #[inline]
948 #[must_use]
949 pub fn get_custom_type_display_by_name(&self, type_name: &str) -> Option<&str> {
950 self.get_custom_type_by_name_raw(type_name)
951 .map(|typ| typ.display_name.as_str())
952 }
953 /// Get the display name of a registered custom type.
954 ///
955 /// # Example
956 ///
957 /// ```
958 /// # use rhai::Module;
959 /// #[derive(Clone)]
960 /// struct TestStruct;
961 ///
962 /// let name = std::any::type_name::<TestStruct>();
963 ///
964 /// let mut module = Module::new();
965 ///
966 /// module.set_custom_type::<TestStruct>("MyType");
967 ///
968 /// assert_eq!(module.get_custom_type_display::<TestStruct>(), Some("MyType"));
969 /// ```
970 #[inline(always)]
971 #[must_use]
972 pub fn get_custom_type_display<T>(&self) -> Option<&str> {
973 self.get_custom_type_display_by_name(type_name::<T>())
974 }
975 /// _(internals)_ Get a registered custom type .
976 /// Exported under the `internals` feature only.
977 #[cfg(feature = "internals")]
978 #[inline(always)]
979 #[must_use]
980 pub fn get_custom_type_raw<T>(&self) -> Option<&CustomTypeInfo> {
981 self.get_custom_type_by_name_raw(type_name::<T>())
982 }
983 /// Get a registered custom type .
984 #[cfg(not(feature = "internals"))]
985 #[inline(always)]
986 #[must_use]
987 pub fn get_custom_type_raw<T>(&self) -> Option<&CustomTypeInfo> {
988 self.get_custom_type_by_name_raw(type_name::<T>())
989 }
990 /// _(internals)_ Get a registered custom type by its type name.
991 /// Exported under the `internals` feature only.
992 #[cfg(feature = "internals")]
993 #[inline(always)]
994 #[must_use]
995 pub fn get_custom_type_by_name_raw(&self, type_name: &str) -> Option<&CustomTypeInfo> {
996 self.custom_types.get(type_name)
997 }
998 /// Get a registered custom type by its type name.
999 #[cfg(not(feature = "internals"))]
1000 #[inline(always)]
1001 #[must_use]
1002 fn get_custom_type_by_name_raw(&self, type_name: &str) -> Option<&CustomTypeInfo> {
1003 self.custom_types.get(type_name)
1004 }
1005
1006 /// Returns `true` if this [`Module`] contains no items.
1007 ///
1008 /// # Example
1009 ///
1010 /// ```
1011 /// # use rhai::Module;
1012 /// let module = Module::new();
1013 /// assert!(module.is_empty());
1014 /// ```
1015 #[inline]
1016 #[must_use]
1017 pub fn is_empty(&self) -> bool {
1018 !self.flags.intersects(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS)
1019 && self
1020 .functions
1021 .as_ref()
1022 .map_or(true, StraightHashMap::is_empty)
1023 && self.variables.is_empty()
1024 && self.modules.is_empty()
1025 && self.type_iterators.is_empty()
1026 && self
1027 .all_functions
1028 .as_ref()
1029 .map_or(true, StraightHashMap::is_empty)
1030 && self
1031 .all_variables
1032 .as_ref()
1033 .map_or(true, StraightHashMap::is_empty)
1034 && self.all_type_iterators.is_empty()
1035 }
1036
1037 /// Is the [`Module`] indexed?
1038 ///
1039 /// A module must be indexed before it can be used in an `import` statement.
1040 ///
1041 /// # Example
1042 ///
1043 /// ```
1044 /// # use rhai::Module;
1045 /// let mut module = Module::new();
1046 /// assert!(module.is_indexed());
1047 ///
1048 /// module.set_native_fn("foo", |x: &mut i64, y: i64| { *x = y; Ok(()) });
1049 /// assert!(!module.is_indexed());
1050 ///
1051 /// # #[cfg(not(feature = "no_module"))]
1052 /// # {
1053 /// module.build_index();
1054 /// assert!(module.is_indexed());
1055 /// # }
1056 /// ```
1057 #[inline(always)]
1058 #[must_use]
1059 pub const fn is_indexed(&self) -> bool {
1060 self.flags.intersects(ModuleFlags::INDEXED)
1061 }
1062
1063 /// _(metadata)_ Generate signatures for all the non-private functions in the [`Module`].
1064 /// Exported under the `metadata` feature only.
1065 #[cfg(feature = "metadata")]
1066 #[inline]
1067 pub fn gen_fn_signatures_with_mapper<'a>(
1068 &'a self,
1069 type_mapper: impl Fn(&'a str) -> std::borrow::Cow<'a, str> + 'a,
1070 ) -> impl Iterator<Item = String> + 'a {
1071 self.iter_fn()
1072 .map(|(_, m)| m)
1073 .filter(|&f| match f.access {
1074 FnAccess::Public => true,
1075 FnAccess::Private => false,
1076 })
1077 .map(move |m| m.gen_signature(&type_mapper))
1078 }
1079
1080 /// Does a variable exist in the [`Module`]?
1081 ///
1082 /// # Example
1083 ///
1084 /// ```
1085 /// # use rhai::Module;
1086 /// let mut module = Module::new();
1087 /// module.set_var("answer", 42_i64);
1088 /// assert!(module.contains_var("answer"));
1089 /// ```
1090 #[inline(always)]
1091 #[must_use]
1092 pub fn contains_var(&self, name: &str) -> bool {
1093 self.variables.contains_key(name)
1094 }
1095
1096 /// Get the value of a [`Module`] variable.
1097 ///
1098 /// # Example
1099 ///
1100 /// ```
1101 /// # use rhai::Module;
1102 /// let mut module = Module::new();
1103 /// module.set_var("answer", 42_i64);
1104 /// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
1105 /// ```
1106 #[inline]
1107 #[must_use]
1108 pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
1109 self.get_var(name).and_then(Dynamic::try_cast::<T>)
1110 }
1111
1112 /// Get a [`Module`] variable as a [`Dynamic`].
1113 ///
1114 /// # Example
1115 ///
1116 /// ```
1117 /// # use rhai::Module;
1118 /// let mut module = Module::new();
1119 /// module.set_var("answer", 42_i64);
1120 /// assert_eq!(module.get_var("answer").expect("answer should exist").cast::<i64>(), 42);
1121 /// ```
1122 #[inline(always)]
1123 #[must_use]
1124 pub fn get_var(&self, name: &str) -> Option<Dynamic> {
1125 self.variables.get(name).cloned()
1126 }
1127
1128 /// Set a variable into the [`Module`].
1129 ///
1130 /// If there is an existing variable of the same name, it is replaced.
1131 ///
1132 /// # Example
1133 ///
1134 /// ```
1135 /// # use rhai::Module;
1136 /// let mut module = Module::new();
1137 /// module.set_var("answer", 42_i64);
1138 /// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
1139 /// ```
1140 #[inline]
1141 pub fn set_var(
1142 &mut self,
1143 name: impl Into<Identifier>,
1144 value: impl Variant + Clone,
1145 ) -> &mut Self {
1146 let ident = name.into();
1147 let value = Dynamic::from(value);
1148
1149 if self.is_indexed() {
1150 let hash_var = crate::calc_var_hash(Some(""), &ident);
1151
1152 // Catch hash collisions in testing environment only.
1153 #[cfg(feature = "testing-environ")]
1154 assert!(
1155 self.all_variables
1156 .as_ref()
1157 .map_or(true, |map| !map.contains_key(&hash_var)),
1158 "Hash {} already exists when registering variable {}",
1159 hash_var,
1160 ident
1161 );
1162
1163 self.all_variables
1164 .get_or_insert_with(Default::default)
1165 .insert(hash_var, value.clone());
1166 }
1167 self.variables.insert(ident, value);
1168 self
1169 }
1170
1171 /// Get a namespace-qualified [`Module`] variable as a [`Dynamic`].
1172 #[cfg(not(feature = "no_module"))]
1173 #[inline]
1174 pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Option<Dynamic> {
1175 self.all_variables
1176 .as_ref()
1177 .and_then(|c| c.get(&hash_var).cloned())
1178 }
1179
1180 /// Set a script-defined function into the [`Module`].
1181 ///
1182 /// If there is an existing function of the same name and number of arguments, it is replaced.
1183 #[cfg(not(feature = "no_function"))]
1184 #[inline]
1185 pub fn set_script_fn(&mut self, fn_def: impl Into<Shared<crate::ast::ScriptFuncDef>>) -> u64 {
1186 let fn_def = fn_def.into();
1187
1188 // None + function name + number of arguments.
1189 let namespace = FnNamespace::Internal;
1190 let num_params = fn_def.params.len();
1191 let hash_script = crate::calc_fn_hash(None, &fn_def.name, num_params);
1192 #[cfg(not(feature = "no_object"))]
1193 let (hash_script, namespace) =
1194 fn_def
1195 .this_type
1196 .as_ref()
1197 .map_or((hash_script, namespace), |this_type| {
1198 (
1199 crate::calc_typed_method_hash(hash_script, this_type),
1200 FnNamespace::Global,
1201 )
1202 });
1203
1204 // Catch hash collisions in testing environment only.
1205 #[cfg(feature = "testing-environ")]
1206 if let Some(f) = self.functions.as_ref().and_then(|f| f.get(&hash_script)) {
1207 unreachable!(
1208 "Hash {} already exists when registering function {:#?}:\n{:#?}",
1209 hash_script, fn_def, f
1210 );
1211 }
1212
1213 let metadata = FuncMetadata {
1214 hash: hash_script,
1215 name: fn_def.name.as_str().into(),
1216 namespace,
1217 access: fn_def.access,
1218 num_params,
1219 param_types: FnArgsVec::new_const(),
1220 #[cfg(feature = "metadata")]
1221 params_info: fn_def.params.iter().map(Into::into).collect(),
1222 #[cfg(feature = "metadata")]
1223 return_type: <_>::default(),
1224 #[cfg(feature = "metadata")]
1225 comments: crate::StaticVec::new_const(),
1226 };
1227
1228 self.functions
1229 .get_or_insert_with(|| new_hash_map(FN_MAP_SIZE))
1230 .insert(hash_script, (fn_def.into(), metadata.into()));
1231
1232 self.flags
1233 .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
1234
1235 hash_script
1236 }
1237
1238 /// Get a shared reference to the script-defined function in the [`Module`] based on name
1239 /// and number of parameters.
1240 #[cfg(not(feature = "no_function"))]
1241 #[inline]
1242 #[must_use]
1243 pub fn get_script_fn(
1244 &self,
1245 name: impl AsRef<str>,
1246 num_params: usize,
1247 ) -> Option<&Shared<crate::ast::ScriptFuncDef>> {
1248 self.functions.as_ref().and_then(|lib| {
1249 let name = name.as_ref();
1250
1251 lib.values()
1252 .find(|(_, f)| f.num_params == num_params && f.name == name)
1253 .and_then(|(f, _)| f.get_script_fn_def())
1254 })
1255 }
1256
1257 /// Get a mutable reference to the underlying [`BTreeMap`] of sub-modules,
1258 /// creating one if empty.
1259 ///
1260 /// # WARNING
1261 ///
1262 /// By taking a mutable reference, it is assumed that some sub-modules will be modified.
1263 /// Thus the [`Module`] is automatically set to be non-indexed.
1264 #[cfg(not(feature = "no_module"))]
1265 #[inline]
1266 #[must_use]
1267 pub(crate) fn get_sub_modules_mut(&mut self) -> &mut BTreeMap<Identifier, SharedModule> {
1268 // We must assume that the user has changed the sub-modules
1269 // (otherwise why take a mutable reference?)
1270 self.all_functions = None;
1271 self.all_variables = None;
1272 self.all_type_iterators.clear();
1273 self.flags
1274 .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
1275
1276 &mut self.modules
1277 }
1278
1279 /// Does a sub-module exist in the [`Module`]?
1280 ///
1281 /// # Example
1282 ///
1283 /// ```
1284 /// # use rhai::Module;
1285 /// let mut module = Module::new();
1286 /// let sub_module = Module::new();
1287 /// module.set_sub_module("question", sub_module);
1288 /// assert!(module.contains_sub_module("question"));
1289 /// ```
1290 #[inline(always)]
1291 #[must_use]
1292 pub fn contains_sub_module(&self, name: &str) -> bool {
1293 self.modules.contains_key(name)
1294 }
1295
1296 /// Get a sub-module in the [`Module`].
1297 ///
1298 /// # Example
1299 ///
1300 /// ```
1301 /// # use rhai::Module;
1302 /// let mut module = Module::new();
1303 /// let sub_module = Module::new();
1304 /// module.set_sub_module("question", sub_module);
1305 /// assert!(module.get_sub_module("question").is_some());
1306 /// ```
1307 #[inline]
1308 #[must_use]
1309 pub fn get_sub_module(&self, name: &str) -> Option<&Self> {
1310 self.modules.get(name).map(|m| &**m)
1311 }
1312
1313 /// Set a sub-module into the [`Module`].
1314 ///
1315 /// If there is an existing sub-module of the same name, it is replaced.
1316 ///
1317 /// # Example
1318 ///
1319 /// ```
1320 /// # use rhai::Module;
1321 /// let mut module = Module::new();
1322 /// let sub_module = Module::new();
1323 /// module.set_sub_module("question", sub_module);
1324 /// assert!(module.get_sub_module("question").is_some());
1325 /// ```
1326 #[inline]
1327 pub fn set_sub_module(
1328 &mut self,
1329 name: impl Into<Identifier>,
1330 sub_module: impl Into<SharedModule>,
1331 ) -> &mut Self {
1332 self.modules.insert(name.into(), sub_module.into());
1333 self.flags
1334 .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
1335 self
1336 }
1337
1338 /// Does the particular Rust function exist in the [`Module`]?
1339 ///
1340 /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
1341 ///
1342 /// # Example
1343 ///
1344 /// ```
1345 /// # use rhai::Module;
1346 /// let mut module = Module::new();
1347 /// let hash = module.set_native_fn("calc", |x: i64| Ok(42 + x));
1348 /// assert!(module.contains_fn(hash));
1349 /// ```
1350 #[inline]
1351 #[must_use]
1352 pub fn contains_fn(&self, hash_fn: u64) -> bool {
1353 self.functions
1354 .as_ref()
1355 .map_or(false, |m| m.contains_key(&hash_fn))
1356 }
1357
1358 /// _(metadata)_ Update the metadata (parameter names/types, return type and doc-comments) of a registered function.
1359 /// Exported under the `metadata` feature only.
1360 ///
1361 /// # Deprecated
1362 ///
1363 /// This method is deprecated.
1364 /// Use the [`FuncRegistration`] API instead.
1365 ///
1366 /// This method will be removed in the next major version.
1367 #[deprecated(since = "1.17.0", note = "use the `FuncRegistration` API instead")]
1368 #[cfg(feature = "metadata")]
1369 #[inline]
1370 pub fn update_fn_metadata_with_comments<A: Into<Identifier>, C: Into<SmartString>>(
1371 &mut self,
1372 hash_fn: u64,
1373 arg_names: impl IntoIterator<Item = A>,
1374 comments: impl IntoIterator<Item = C>,
1375 ) -> &mut Self {
1376 let mut params_info = arg_names
1377 .into_iter()
1378 .map(Into::into)
1379 .collect::<FnArgsVec<_>>();
1380
1381 if let Some((_, f)) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) {
1382 let (params_info, return_type_name) = if params_info.len() > f.num_params {
1383 let return_type = params_info.pop().unwrap();
1384 (params_info, return_type)
1385 } else {
1386 (params_info, crate::SmartString::new_const())
1387 };
1388 f.params_info = params_info;
1389 f.return_type = return_type_name;
1390 f.comments = comments.into_iter().map(Into::into).collect();
1391 }
1392
1393 self
1394 }
1395
1396 /// Update the namespace of a registered function.
1397 ///
1398 /// # Deprecated
1399 ///
1400 /// This method is deprecated.
1401 /// Use the [`FuncRegistration`] API instead.
1402 ///
1403 /// This method will be removed in the next major version.
1404 #[deprecated(since = "1.17.0", note = "use the `FuncRegistration` API instead")]
1405 #[inline]
1406 pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
1407 if let Some((_, f)) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) {
1408 f.namespace = namespace;
1409 self.flags
1410 .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
1411 }
1412 self
1413 }
1414
1415 /// Remap type ID.
1416 #[inline]
1417 #[must_use]
1418 fn map_type(map: bool, type_id: TypeId) -> TypeId {
1419 if !map {
1420 return type_id;
1421 }
1422 if type_id == TypeId::of::<&str>() {
1423 // Map &str to ImmutableString
1424 return TypeId::of::<ImmutableString>();
1425 }
1426 if type_id == TypeId::of::<String>() {
1427 // Map String to ImmutableString
1428 return TypeId::of::<ImmutableString>();
1429 }
1430
1431 type_id
1432 }
1433
1434 /// Set a native Rust function into the [`Module`] based on a [`FuncRegistration`].
1435 ///
1436 /// # WARNING - Low Level API
1437 ///
1438 /// This function is very low level. It takes a list of [`TypeId`][std::any::TypeId]'s
1439 /// indicating the actual types of the parameters.
1440 #[inline(always)]
1441 pub fn set_fn_raw_with_options(
1442 &mut self,
1443 options: FuncRegistration,
1444 arg_types: impl AsRef<[TypeId]>,
1445 func: RhaiFunc,
1446 ) -> &FuncMetadata {
1447 options.set_into_module_raw(self, arg_types, func)
1448 }
1449
1450 /// Set a native Rust function into the [`Module`], returning a [`u64`] hash key.
1451 ///
1452 /// If there is a similar existing Rust function, it is replaced.
1453 ///
1454 /// # Use `FuncRegistration` API
1455 ///
1456 /// It is recommended that the [`FuncRegistration`] API be used instead.
1457 ///
1458 /// Essentially, this method is a shortcut for:
1459 ///
1460 /// ```text
1461 /// FuncRegistration::new(name)
1462 /// .with_namespace(FnNamespace::Internal)
1463 /// .with_purity(true)
1464 /// .with_volatility(false)
1465 /// .set_into_module(module, func)
1466 /// .hash
1467 /// ```
1468 ///
1469 /// # Assumptions
1470 ///
1471 /// * **Accessibility**: The function namespace is [`FnNamespace::Internal`].
1472 ///
1473 /// * **Purity**: The function is assumed to be _pure_ unless it is a property setter or an index setter.
1474 ///
1475 /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s).
1476 ///
1477 /// * **Metadata**: No metadata for the function is registered.
1478 ///
1479 /// To change these assumptions, use the [`FuncRegistration`] API instead.
1480 ///
1481 /// # Example
1482 ///
1483 /// ```
1484 /// # use rhai::Module;
1485 /// let mut module = Module::new();
1486 /// let hash = module.set_native_fn("calc", |x: i64| Ok(42 + x));
1487 /// assert!(module.contains_fn(hash));
1488 /// ```
1489 #[inline]
1490 pub fn set_native_fn<A: 'static, const N: usize, const X: bool, R, FUNC>(
1491 &mut self,
1492 name: impl Into<Identifier>,
1493 func: FUNC,
1494 ) -> u64
1495 where
1496 R: Variant + Clone,
1497 FUNC: RhaiNativeFunc<A, N, X, R, true> + SendSync + 'static,
1498 {
1499 FuncRegistration::new(name)
1500 .with_namespace(FnNamespace::Internal)
1501 .with_purity(true)
1502 .with_volatility(false)
1503 .set_into_module(self, func)
1504 .hash
1505 }
1506
1507 /// Set a Rust getter function taking one mutable parameter, returning a [`u64`] hash key.
1508 /// This function is automatically exposed to the global namespace.
1509 ///
1510 /// If there is a similar existing Rust getter function, it is replaced.
1511 ///
1512 /// # Assumptions
1513 ///
1514 /// * **Accessibility**: The function namespace is [`FnNamespace::Global`].
1515 ///
1516 /// * **Purity**: The function is assumed to be _pure_ (so it can be called on constants).
1517 ///
1518 /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s).
1519 ///
1520 /// * **Metadata**: No metadata for the function is registered.
1521 ///
1522 /// To change these assumptions, use the [`FuncRegistration`] API instead.
1523 ///
1524 /// # Example
1525 ///
1526 /// ```
1527 /// # use rhai::Module;
1528 /// let mut module = Module::new();
1529 /// let hash = module.set_getter_fn("value", |x: &mut i64| Ok(*x));
1530 /// assert!(module.contains_fn(hash));
1531 /// ```
1532 #[cfg(not(feature = "no_object"))]
1533 #[inline(always)]
1534 pub fn set_getter_fn<A, const X: bool, R, FUNC>(
1535 &mut self,
1536 name: impl AsRef<str>,
1537 func: FUNC,
1538 ) -> u64
1539 where
1540 A: Variant + Clone,
1541 R: Variant + Clone,
1542 FUNC: RhaiNativeFunc<(Mut<A>,), 1, X, R, true> + SendSync + 'static,
1543 {
1544 FuncRegistration::new(crate::engine::make_getter(name.as_ref()))
1545 .with_namespace(FnNamespace::Global)
1546 .with_purity(true)
1547 .with_volatility(false)
1548 .set_into_module(self, func)
1549 .hash
1550 }
1551
1552 /// Set a Rust setter function taking two parameters (the first one mutable) into the [`Module`],
1553 /// returning a [`u64`] hash key.
1554 /// This function is automatically exposed to the global namespace.
1555 ///
1556 /// If there is a similar existing setter Rust function, it is replaced.
1557 ///
1558 /// # Assumptions
1559 ///
1560 /// * **Accessibility**: The function namespace is [`FnNamespace::Global`].
1561 ///
1562 /// * **Purity**: The function is assumed to be _non-pure_ (so it cannot be called on constants).
1563 ///
1564 /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s).
1565 ///
1566 /// * **Metadata**: No metadata for the function is registered.
1567 ///
1568 /// To change these assumptions, use the [`FuncRegistration`] API instead.
1569 ///
1570 /// # Example
1571 ///
1572 /// ```
1573 /// use rhai::{Module, ImmutableString};
1574 ///
1575 /// let mut module = Module::new();
1576 /// let hash = module.set_setter_fn("value", |x: &mut i64, y: ImmutableString| {
1577 /// *x = y.len() as i64; Ok(())
1578 /// });
1579 /// assert!(module.contains_fn(hash));
1580 /// ```
1581 #[cfg(not(feature = "no_object"))]
1582 #[inline(always)]
1583 pub fn set_setter_fn<A, const X: bool, R, FUNC>(
1584 &mut self,
1585 name: impl AsRef<str>,
1586 func: FUNC,
1587 ) -> u64
1588 where
1589 A: Variant + Clone,
1590 R: Variant + Clone,
1591 FUNC: RhaiNativeFunc<(Mut<A>, R), 2, X, (), true> + SendSync + 'static,
1592 {
1593 FuncRegistration::new(crate::engine::make_setter(name.as_ref()))
1594 .with_namespace(FnNamespace::Global)
1595 .with_purity(false)
1596 .with_volatility(false)
1597 .set_into_module(self, func)
1598 .hash
1599 }
1600
1601 /// Set a pair of Rust getter and setter functions into the [`Module`], returning both [`u64`] hash keys.
1602 /// This is a short-hand for [`set_getter_fn`][Module::set_getter_fn] and [`set_setter_fn`][Module::set_setter_fn].
1603 ///
1604 /// These function are automatically exposed to the global namespace.
1605 ///
1606 /// If there are similar existing Rust functions, they are replaced.
1607 ///
1608 /// To change these assumptions, use the [`FuncRegistration`] API instead.
1609 ///
1610 /// # Example
1611 ///
1612 /// ```
1613 /// use rhai::{Module, ImmutableString};
1614 ///
1615 /// let mut module = Module::new();
1616 /// let (hash_get, hash_set) =
1617 /// module.set_getter_setter_fn("value",
1618 /// |x: &mut i64| Ok(x.to_string().into()),
1619 /// |x: &mut i64, y: ImmutableString| { *x = y.len() as i64; Ok(()) }
1620 /// );
1621 /// assert!(module.contains_fn(hash_get));
1622 /// assert!(module.contains_fn(hash_set));
1623 /// ```
1624 #[cfg(not(feature = "no_object"))]
1625 #[inline(always)]
1626 pub fn set_getter_setter_fn<
1627 A: Variant + Clone,
1628 const X1: bool,
1629 const X2: bool,
1630 R: Variant + Clone,
1631 >(
1632 &mut self,
1633 name: impl AsRef<str>,
1634 getter: impl RhaiNativeFunc<(Mut<A>,), 1, X1, R, true> + SendSync + 'static,
1635 setter: impl RhaiNativeFunc<(Mut<A>, R), 2, X2, (), true> + SendSync + 'static,
1636 ) -> (u64, u64) {
1637 (
1638 self.set_getter_fn(name.as_ref(), getter),
1639 self.set_setter_fn(name.as_ref(), setter),
1640 )
1641 }
1642
1643 /// Set a Rust index getter taking two parameters (the first one mutable) into the [`Module`],
1644 /// returning a [`u64`] hash key.
1645 /// This function is automatically exposed to the global namespace.
1646 ///
1647 /// If there is a similar existing setter Rust function, it is replaced.
1648 ///
1649 /// # Assumptions
1650 ///
1651 /// * **Accessibility**: The function namespace is [`FnNamespace::Global`].
1652 ///
1653 /// * **Purity**: The function is assumed to be _pure_ (so it can be called on constants).
1654 ///
1655 /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s).
1656 ///
1657 /// * **Metadata**: No metadata for the function is registered.
1658 ///
1659 /// To change these assumptions, use the [`FuncRegistration`] API instead.
1660 ///
1661 /// # Panics
1662 ///
1663 /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`],
1664 /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT].
1665 ///
1666 /// Indexers for arrays, object maps, strings and integers cannot be registered.
1667 ///
1668 /// # Example
1669 ///
1670 /// ```
1671 /// use rhai::{Module, ImmutableString};
1672 ///
1673 /// #[derive(Clone)]
1674 /// struct TestStruct(i64);
1675 ///
1676 /// let mut module = Module::new();
1677 ///
1678 /// let hash = module.set_indexer_get_fn(
1679 /// |x: &mut TestStruct, y: ImmutableString| Ok(x.0 + y.len() as i64)
1680 /// );
1681 ///
1682 /// assert!(module.contains_fn(hash));
1683 /// ```
1684 #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
1685 #[inline]
1686 pub fn set_indexer_get_fn<A, B, const X: bool, R, FUNC>(&mut self, func: FUNC) -> u64
1687 where
1688 A: Variant + Clone,
1689 B: Variant + Clone,
1690 R: Variant + Clone,
1691 FUNC: RhaiNativeFunc<(Mut<A>, B), 2, X, R, true> + SendSync + 'static,
1692 {
1693 FuncRegistration::new(crate::engine::FN_IDX_GET)
1694 .with_namespace(FnNamespace::Global)
1695 .with_purity(true)
1696 .with_volatility(false)
1697 .set_into_module(self, func)
1698 .hash
1699 }
1700
1701 /// Set a Rust index setter taking three parameters (the first one mutable) into the [`Module`],
1702 /// returning a [`u64`] hash key.
1703 /// This function is automatically exposed to the global namespace.
1704 ///
1705 /// If there is a similar existing Rust function, it is replaced.
1706 ///
1707 /// # Assumptions
1708 ///
1709 /// * **Accessibility**: The function namespace is [`FnNamespace::Global`].
1710 ///
1711 /// * **Purity**: The function is assumed to be _non-pure_ (so it cannot be called on constants).
1712 ///
1713 /// * **Volatility**: The function is assumed to be _non-volatile_ -- i.e. it guarantees the same result for the same input(s).
1714 ///
1715 /// # Panics
1716 ///
1717 /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`],
1718 /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT].
1719 ///
1720 /// Indexers for arrays, object maps, strings and integers cannot be registered.
1721 ///
1722 /// # Example
1723 ///
1724 /// ```
1725 /// use rhai::{Module, ImmutableString};
1726 ///
1727 /// #[derive(Clone)]
1728 /// struct TestStruct(i64);
1729 ///
1730 /// let mut module = Module::new();
1731 ///
1732 /// let hash = module.set_indexer_set_fn(|x: &mut TestStruct, y: ImmutableString, value: i64| {
1733 /// *x = TestStruct(y.len() as i64 + value);
1734 /// Ok(())
1735 /// });
1736 ///
1737 /// assert!(module.contains_fn(hash));
1738 /// ```
1739 #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
1740 #[inline]
1741 pub fn set_indexer_set_fn<A, B, const X: bool, R, FUNC>(&mut self, func: FUNC) -> u64
1742 where
1743 A: Variant + Clone,
1744 B: Variant + Clone,
1745 R: Variant + Clone,
1746 FUNC: RhaiNativeFunc<(Mut<A>, B, R), 3, X, (), true> + SendSync + 'static,
1747 {
1748 FuncRegistration::new(crate::engine::FN_IDX_SET)
1749 .with_namespace(FnNamespace::Global)
1750 .with_purity(false)
1751 .with_volatility(false)
1752 .set_into_module(self, func)
1753 .hash
1754 }
1755
1756 /// Set a pair of Rust index getter and setter functions into the [`Module`], returning both [`u64`] hash keys.
1757 /// This is a short-hand for [`set_indexer_get_fn`][Module::set_indexer_get_fn] and
1758 /// [`set_indexer_set_fn`][Module::set_indexer_set_fn].
1759 ///
1760 /// These functions are automatically exposed to the global namespace.
1761 ///
1762 /// If there are similar existing Rust functions, they are replaced.
1763 ///
1764 /// # Panics
1765 ///
1766 /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`],
1767 /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT].
1768 ///
1769 /// Indexers for arrays, object maps, strings and integers cannot be registered.
1770 ///
1771 /// # Example
1772 ///
1773 /// ```
1774 /// use rhai::{Module, ImmutableString};
1775 ///
1776 /// #[derive(Clone)]
1777 /// struct TestStruct(i64);
1778 ///
1779 /// let mut module = Module::new();
1780 ///
1781 /// let (hash_get, hash_set) = module.set_indexer_get_set_fn(
1782 /// |x: &mut TestStruct, y: ImmutableString| Ok(x.0 + y.len() as i64),
1783 /// |x: &mut TestStruct, y: ImmutableString, value: i64| { *x = TestStruct(y.len() as i64 + value); Ok(()) }
1784 /// );
1785 ///
1786 /// assert!(module.contains_fn(hash_get));
1787 /// assert!(module.contains_fn(hash_set));
1788 /// ```
1789 #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
1790 #[inline(always)]
1791 pub fn set_indexer_get_set_fn<
1792 A: Variant + Clone,
1793 B: Variant + Clone,
1794 const X1: bool,
1795 const X2: bool,
1796 R: Variant + Clone,
1797 >(
1798 &mut self,
1799 get_fn: impl RhaiNativeFunc<(Mut<A>, B), 2, X1, R, true> + SendSync + 'static,
1800 set_fn: impl RhaiNativeFunc<(Mut<A>, B, R), 3, X2, (), true> + SendSync + 'static,
1801 ) -> (u64, u64) {
1802 (
1803 self.set_indexer_get_fn(get_fn),
1804 self.set_indexer_set_fn(set_fn),
1805 )
1806 }
1807
1808 /// Look up a native Rust function by hash.
1809 #[inline]
1810 #[must_use]
1811 pub(crate) fn get_fn(&self, hash_native: u64) -> Option<&RhaiFunc> {
1812 self.functions
1813 .as_ref()
1814 .and_then(|m| m.get(&hash_native))
1815 .map(|(f, _)| f)
1816 }
1817
1818 /// Can the particular function with [`Dynamic`] parameter(s) exist in the [`Module`]?
1819 ///
1820 /// A `true` return value does not automatically imply that the function _must_ exist.
1821 #[inline(always)]
1822 #[must_use]
1823 pub(crate) const fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool {
1824 !self.dynamic_functions_filter.is_absent(hash_script)
1825 }
1826
1827 /// Does the particular namespace-qualified function exist in the [`Module`]?
1828 ///
1829 /// The [`u64`] hash is calculated by [`build_index`][Module::build_index].
1830 #[inline(always)]
1831 #[must_use]
1832 pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
1833 self.all_functions
1834 .as_ref()
1835 .map_or(false, |m| m.contains_key(&hash_fn))
1836 }
1837
1838 /// Get a namespace-qualified function.
1839 ///
1840 /// The [`u64`] hash is calculated by [`build_index`][Module::build_index].
1841 #[cfg(not(feature = "no_module"))]
1842 #[inline]
1843 #[must_use]
1844 pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&RhaiFunc> {
1845 self.all_functions
1846 .as_ref()
1847 .and_then(|m| m.get(&hash_qualified_fn))
1848 }
1849
1850 /// Combine another [`Module`] into this [`Module`].
1851 /// The other [`Module`] is _consumed_ to merge into this [`Module`].
1852 #[inline]
1853 pub fn combine(&mut self, other: Self) -> &mut Self {
1854 self.modules.extend(other.modules);
1855 self.variables.extend(other.variables);
1856 match self.functions {
1857 Some(ref mut m) if other.functions.is_some() => m.extend(other.functions.unwrap()),
1858 Some(_) => (),
1859 None => self.functions = other.functions,
1860 }
1861 self.dynamic_functions_filter += other.dynamic_functions_filter;
1862 self.type_iterators.extend(other.type_iterators);
1863 self.all_functions = None;
1864 self.all_variables = None;
1865 self.all_type_iterators.clear();
1866 self.flags
1867 .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
1868
1869 #[cfg(feature = "metadata")]
1870 {
1871 if !self.doc.is_empty() {
1872 self.doc.push('\n');
1873 }
1874 self.doc.push_str(&other.doc);
1875 }
1876
1877 self
1878 }
1879
1880 /// Combine another [`Module`] into this [`Module`].
1881 /// The other [`Module`] is _consumed_ to merge into this [`Module`].
1882 /// Sub-modules are flattened onto the root [`Module`], with higher level overriding lower level.
1883 #[inline]
1884 pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
1885 for m in other.modules.into_values() {
1886 self.combine_flatten(shared_take_or_clone(m));
1887 }
1888 self.variables.extend(other.variables);
1889 match self.functions {
1890 Some(ref mut m) if other.functions.is_some() => m.extend(other.functions.unwrap()),
1891 Some(_) => (),
1892 None => self.functions = other.functions,
1893 }
1894 self.dynamic_functions_filter += other.dynamic_functions_filter;
1895 self.type_iterators.extend(other.type_iterators);
1896 self.all_functions = None;
1897 self.all_variables = None;
1898 self.all_type_iterators.clear();
1899 self.flags
1900 .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
1901
1902 #[cfg(feature = "metadata")]
1903 {
1904 if !self.doc.is_empty() {
1905 self.doc.push('\n');
1906 }
1907 self.doc.push_str(&other.doc);
1908 }
1909
1910 self
1911 }
1912
1913 /// Polyfill this [`Module`] with another [`Module`].
1914 /// Only items not existing in this [`Module`] are added.
1915 #[inline]
1916 pub fn fill_with(&mut self, other: &Self) -> &mut Self {
1917 for (k, v) in &other.modules {
1918 if !self.modules.contains_key(k) {
1919 self.modules.insert(k.clone(), v.clone());
1920 }
1921 }
1922 for (k, v) in &other.variables {
1923 if !self.variables.contains_key(k) {
1924 self.variables.insert(k.clone(), v.clone());
1925 }
1926 }
1927 if let Some(ref functions) = other.functions {
1928 let others_len = functions.len();
1929
1930 for (&k, f) in functions {
1931 let map = self
1932 .functions
1933 .get_or_insert_with(|| new_hash_map(FN_MAP_SIZE));
1934 map.reserve(others_len);
1935 map.entry(k).or_insert_with(|| f.clone());
1936 }
1937 }
1938 self.dynamic_functions_filter += &other.dynamic_functions_filter;
1939 for (&k, v) in &other.type_iterators {
1940 self.type_iterators.entry(k).or_insert_with(|| v.clone());
1941 }
1942
1943 self.all_functions = None;
1944 self.all_variables = None;
1945 self.all_type_iterators.clear();
1946 self.flags
1947 .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
1948
1949 #[cfg(feature = "metadata")]
1950 {
1951 if !self.doc.is_empty() {
1952 self.doc.push('\n');
1953 }
1954 self.doc.push_str(&other.doc);
1955 }
1956
1957 self
1958 }
1959
1960 /// Merge another [`Module`] into this [`Module`].
1961 #[inline(always)]
1962 pub fn merge(&mut self, other: &Self) -> &mut Self {
1963 self.merge_filtered(other, |_, _, _, _, _| true)
1964 }
1965
1966 /// Merge another [`Module`] into this [`Module`] based on a filter predicate.
1967 pub(crate) fn merge_filtered(
1968 &mut self,
1969 other: &Self,
1970 _filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool + Copy,
1971 ) -> &mut Self {
1972 for (k, v) in &other.modules {
1973 let mut m = Self::new();
1974 m.merge_filtered(v, _filter);
1975 self.set_sub_module(k.clone(), m);
1976 }
1977 #[cfg(feature = "no_function")]
1978 self.modules.extend(other.modules.clone());
1979
1980 self.variables.extend(other.variables.clone());
1981
1982 if let Some(ref functions) = other.functions {
1983 match self.functions {
1984 Some(ref mut m) => m.extend(
1985 functions
1986 .iter()
1987 .filter(|(.., (f, m))| {
1988 _filter(m.namespace, m.access, f.is_script(), &m.name, m.num_params)
1989 })
1990 .map(|(&k, f)| (k, f.clone())),
1991 ),
1992 None => self.functions = other.functions.clone(),
1993 }
1994 }
1995 self.dynamic_functions_filter += &other.dynamic_functions_filter;
1996
1997 self.type_iterators.extend(other.type_iterators.clone());
1998 self.all_functions = None;
1999 self.all_variables = None;
2000 self.all_type_iterators.clear();
2001 self.flags
2002 .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
2003
2004 #[cfg(feature = "metadata")]
2005 {
2006 if !self.doc.is_empty() {
2007 self.doc.push('\n');
2008 }
2009 self.doc.push_str(&other.doc);
2010 }
2011
2012 self
2013 }
2014
2015 /// Filter out the functions, retaining only some script-defined functions based on a filter predicate.
2016 #[cfg(not(feature = "no_function"))]
2017 #[inline]
2018 pub(crate) fn retain_script_functions(
2019 &mut self,
2020 filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
2021 ) -> &mut Self {
2022 self.functions = std::mem::take(&mut self.functions).map(|m| {
2023 m.into_iter()
2024 .filter(|(.., (f, m))| {
2025 if f.is_script() {
2026 filter(m.namespace, m.access, &m.name, m.num_params)
2027 } else {
2028 false
2029 }
2030 })
2031 .collect()
2032 });
2033
2034 self.dynamic_functions_filter.clear();
2035 self.all_functions = None;
2036 self.all_variables = None;
2037 self.all_type_iterators.clear();
2038 self.flags
2039 .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
2040 self
2041 }
2042
2043 /// Get the number of variables, functions and type iterators in the [`Module`].
2044 #[inline(always)]
2045 #[must_use]
2046 pub fn count(&self) -> (usize, usize, usize) {
2047 (
2048 self.variables.len(),
2049 self.functions.as_ref().map_or(0, StraightHashMap::len),
2050 self.type_iterators.len(),
2051 )
2052 }
2053
2054 /// Get an iterator to the sub-modules in the [`Module`].
2055 #[inline(always)]
2056 pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &SharedModule)> {
2057 self.iter_sub_modules_raw().map(|(k, m)| (k.as_str(), m))
2058 }
2059 /// Get an iterator to the sub-modules in the [`Module`].
2060 #[inline(always)]
2061 pub(crate) fn iter_sub_modules_raw(
2062 &self,
2063 ) -> impl Iterator<Item = (&Identifier, &SharedModule)> {
2064 self.modules.iter()
2065 }
2066
2067 /// Get an iterator to the variables in the [`Module`].
2068 #[inline(always)]
2069 pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
2070 self.iter_var_raw().map(|(k, v)| (k.as_str(), v))
2071 }
2072 /// Get an iterator to the variables in the [`Module`].
2073 #[inline(always)]
2074 pub(crate) fn iter_var_raw(&self) -> impl Iterator<Item = (&Identifier, &Dynamic)> {
2075 self.variables.iter()
2076 }
2077
2078 /// Get an iterator to the custom types in the [`Module`].
2079 #[inline(always)]
2080 #[allow(dead_code)]
2081 pub(crate) fn iter_custom_types(&self) -> impl Iterator<Item = (&str, &CustomTypeInfo)> {
2082 self.custom_types.iter()
2083 }
2084
2085 /// Get an iterator to the functions in the [`Module`].
2086 #[inline]
2087 #[allow(dead_code)]
2088 pub(crate) fn iter_fn(&self) -> impl Iterator<Item = (&RhaiFunc, &FuncMetadata)> {
2089 self.functions
2090 .iter()
2091 .flat_map(StraightHashMap::values)
2092 .map(|(f, m)| (f, &**m))
2093 }
2094
2095 /// Get an iterator over all script-defined functions in the [`Module`].
2096 ///
2097 /// Function metadata includes:
2098 /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]).
2099 /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]).
2100 /// 3) Function name (as string slice).
2101 /// 4) Number of parameters.
2102 /// 5) Shared reference to function definition [`ScriptFuncDef`][crate::ast::ScriptFuncDef].
2103 #[cfg(not(feature = "no_function"))]
2104 #[inline]
2105 pub(crate) fn iter_script_fn(
2106 &self,
2107 ) -> impl Iterator<
2108 Item = (
2109 FnNamespace,
2110 FnAccess,
2111 &str,
2112 usize,
2113 &Shared<crate::ast::ScriptFuncDef>,
2114 ),
2115 > + '_ {
2116 self.iter_fn().filter(|(f, _)| f.is_script()).map(|(f, m)| {
2117 (
2118 m.namespace,
2119 m.access,
2120 m.name.as_str(),
2121 m.num_params,
2122 f.get_script_fn_def().expect("`ScriptFuncDef`"),
2123 )
2124 })
2125 }
2126
2127 /// Get an iterator over all script-defined functions in the [`Module`].
2128 ///
2129 /// Function metadata includes:
2130 /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]).
2131 /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]).
2132 /// 3) Function name (as string slice).
2133 /// 4) Number of parameters.
2134 #[cfg(not(feature = "no_function"))]
2135 #[cfg(not(feature = "internals"))]
2136 #[inline]
2137 pub fn iter_script_fn_info(
2138 &self,
2139 ) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize)> {
2140 self.iter_fn()
2141 .filter(|(f, _)| f.is_script())
2142 .map(|(_, f)| (f.namespace, f.access, f.name.as_str(), f.num_params))
2143 }
2144
2145 /// _(internals)_ Get an iterator over all script-defined functions in the [`Module`].
2146 /// Exported under the `internals` feature only.
2147 ///
2148 /// Function metadata includes:
2149 /// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]).
2150 /// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]).
2151 /// 3) Function name (as string slice).
2152 /// 4) Number of parameters.
2153 /// 5) _(internals)_ Shared reference to function definition [`ScriptFuncDef`][crate::ast::ScriptFuncDef].
2154 #[cfg(not(feature = "no_function"))]
2155 #[cfg(feature = "internals")]
2156 #[inline(always)]
2157 pub fn iter_script_fn_info(
2158 &self,
2159 ) -> impl Iterator<
2160 Item = (
2161 FnNamespace,
2162 FnAccess,
2163 &str,
2164 usize,
2165 &Shared<crate::ast::ScriptFuncDef>,
2166 ),
2167 > {
2168 self.iter_script_fn()
2169 }
2170
2171 /// Create a new [`Module`] by evaluating an [`AST`][crate::AST].
2172 ///
2173 /// The entire [`AST`][crate::AST] is encapsulated into each function, allowing functions to
2174 /// cross-call each other.
2175 ///
2176 /// # Example
2177 ///
2178 /// ```
2179 /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
2180 /// use rhai::{Engine, Module, Scope};
2181 ///
2182 /// let engine = Engine::new();
2183 /// let ast = engine.compile("let answer = 42; export answer;")?;
2184 /// let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
2185 /// assert!(module.contains_var("answer"));
2186 /// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
2187 /// # Ok(())
2188 /// # }
2189 /// ```
2190 #[cfg(not(feature = "no_module"))]
2191 #[inline(always)]
2192 pub fn eval_ast_as_new(
2193 scope: crate::Scope,
2194 ast: &crate::AST,
2195 engine: &crate::Engine,
2196 ) -> RhaiResultOf<Self> {
2197 let mut scope = scope;
2198 let global = &mut crate::eval::GlobalRuntimeState::new(engine);
2199
2200 Self::eval_ast_as_new_raw(engine, &mut scope, global, ast)
2201 }
2202 /// Create a new [`Module`] by evaluating an [`AST`][crate::AST].
2203 ///
2204 /// The entire [`AST`][crate::AST] is encapsulated into each function, allowing functions to
2205 /// cross-call each other.
2206 ///
2207 /// # WARNING - Low Level API
2208 ///
2209 /// This function is very low level.
2210 ///
2211 /// In particular, the [`global`][crate::GlobalRuntimeState] parameter allows the entire
2212 /// calling environment to be encapsulated, including automatic global constants.
2213 #[cfg(not(feature = "no_module"))]
2214 pub fn eval_ast_as_new_raw(
2215 engine: &crate::Engine,
2216 scope: &mut crate::Scope,
2217 global: &mut crate::eval::GlobalRuntimeState,
2218 ast: &crate::AST,
2219 ) -> RhaiResultOf<Self> {
2220 // Save global state
2221 let orig_scope_len = scope.len();
2222 let orig_imports_len = global.num_imports();
2223 let orig_source = global.source.clone();
2224
2225 #[cfg(not(feature = "no_function"))]
2226 let orig_lib_len = global.lib.len();
2227
2228 #[cfg(not(feature = "no_function"))]
2229 let orig_constants = std::mem::take(&mut global.constants);
2230
2231 // Run the script
2232 let caches = &mut crate::eval::Caches::new();
2233
2234 let result = engine.eval_ast_with_scope_raw(global, caches, scope, ast);
2235
2236 // Create new module
2237 let mut module = Self::new();
2238
2239 // Extra modules left become sub-modules
2240 let mut imports = crate::ThinVec::new();
2241
2242 if result.is_ok() {
2243 global
2244 .scan_imports_raw()
2245 .skip(orig_imports_len)
2246 .for_each(|(k, m)| {
2247 imports.push((k.clone(), m.clone()));
2248 module.set_sub_module(k.clone(), m.clone());
2249 });
2250 }
2251
2252 // Restore global state
2253 #[cfg(not(feature = "no_function"))]
2254 let constants = std::mem::replace(&mut global.constants, orig_constants);
2255
2256 global.truncate_imports(orig_imports_len);
2257
2258 #[cfg(not(feature = "no_function"))]
2259 global.lib.truncate(orig_lib_len);
2260
2261 global.source = orig_source;
2262
2263 // The return value is thrown away and not used
2264 let _ = result?;
2265
2266 // Encapsulated environment
2267 let environ = Shared::new(crate::ast::EncapsulatedEnviron {
2268 #[cfg(not(feature = "no_function"))]
2269 lib: ast.shared_lib().clone(),
2270 imports,
2271 #[cfg(not(feature = "no_function"))]
2272 constants,
2273 });
2274
2275 // Variables with an alias left in the scope become module variables
2276 let mut i = scope.len();
2277 while i > 0 {
2278 i -= 1;
2279
2280 let (mut value, mut aliases) = if i >= orig_scope_len {
2281 let (_, v, a) = scope.pop_entry().unwrap();
2282 (v, a)
2283 } else {
2284 let (_, v, a) = scope.get_entry_by_index(i);
2285 (v.clone(), a.to_vec())
2286 };
2287
2288 value.deep_scan(|v| {
2289 if let Some(fn_ptr) = v.downcast_mut::<crate::FnPtr>() {
2290 fn_ptr.environ = Some(environ.clone());
2291 }
2292 });
2293
2294 match aliases.len() {
2295 0 => (),
2296 1 => {
2297 let alias = aliases.pop().unwrap();
2298 if !module.contains_var(&alias) {
2299 module.set_var(alias, value);
2300 }
2301 }
2302 _ => {
2303 // Avoid cloning the last value
2304 let mut first_alias = None;
2305
2306 for alias in aliases {
2307 if module.contains_var(&alias) {
2308 continue;
2309 }
2310 if first_alias.is_none() {
2311 first_alias = Some(alias);
2312 } else {
2313 module.set_var(alias, value.clone());
2314 }
2315 }
2316
2317 if let Some(alias) = first_alias {
2318 module.set_var(alias, value);
2319 }
2320 }
2321 }
2322 }
2323
2324 // Non-private functions defined become module functions
2325 #[cfg(not(feature = "no_function"))]
2326 ast.iter_fn_def()
2327 .filter(|&f| match f.access {
2328 FnAccess::Public => true,
2329 FnAccess::Private => false,
2330 })
2331 .for_each(|f| {
2332 let hash = module.set_script_fn(f.clone());
2333 if let (
2334 RhaiFunc::Script {
2335 environ: ref mut e, ..
2336 },
2337 _,
2338 ) = module.functions.as_mut().unwrap().get_mut(&hash).unwrap()
2339 {
2340 // Encapsulate AST environment
2341 *e = Some(environ.clone());
2342 }
2343 });
2344
2345 module.id = ast.source_raw().cloned();
2346
2347 #[cfg(feature = "metadata")]
2348 module.set_doc(ast.doc());
2349
2350 module.build_index();
2351
2352 Ok(module)
2353 }
2354
2355 /// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
2356 ///
2357 /// # Panics
2358 ///
2359 /// Panics if the [`Module`] is not yet indexed via [`build_index`][Module::build_index].
2360 #[inline(always)]
2361 #[must_use]
2362 pub const fn contains_indexed_global_functions(&self) -> bool {
2363 self.flags.intersects(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS)
2364 }
2365
2366 /// Scan through all the sub-modules in the [`Module`] and build a hash index of all
2367 /// variables and functions as one flattened namespace.
2368 ///
2369 /// If the [`Module`] is already indexed, this method has no effect.
2370 pub fn build_index(&mut self) -> &mut Self {
2371 // Collect a particular module.
2372 fn index_module<'a>(
2373 module: &'a Module,
2374 path: &mut Vec<&'a str>,
2375 variables: &mut StraightHashMap<Dynamic>,
2376 functions: &mut StraightHashMap<RhaiFunc>,
2377 type_iterators: &mut BTreeMap<TypeId, Shared<FnIterator>>,
2378 ) -> bool {
2379 let mut contains_indexed_global_functions = false;
2380
2381 for (name, m) in &module.modules {
2382 // Index all the sub-modules first.
2383 path.push(name);
2384 if index_module(m, path, variables, functions, type_iterators) {
2385 contains_indexed_global_functions = true;
2386 }
2387 path.pop();
2388 }
2389
2390 // Index all variables
2391 for (var_name, value) in &module.variables {
2392 let hash_var = crate::calc_var_hash(path.iter().copied(), var_name);
2393
2394 // Catch hash collisions in testing environment only.
2395 #[cfg(feature = "testing-environ")]
2396 assert!(
2397 !variables.contains_key(&hash_var),
2398 "Hash {} already exists when indexing variable {}",
2399 hash_var,
2400 var_name
2401 );
2402
2403 variables.insert(hash_var, value.clone());
2404 }
2405
2406 // Index all type iterators
2407 for (&type_id, func) in &module.type_iterators {
2408 type_iterators.insert(type_id, func.clone());
2409 }
2410
2411 // Index all functions
2412 for (&hash, (f, m)) in module.functions.iter().flatten() {
2413 match m.namespace {
2414 FnNamespace::Global => {
2415 // Catch hash collisions in testing environment only.
2416 #[cfg(feature = "testing-environ")]
2417 if let Some(fx) = functions.get(&hash) {
2418 unreachable!(
2419 "Hash {} already exists when indexing function {:#?}:\n{:#?}",
2420 hash, f, fx
2421 );
2422 }
2423
2424 // Flatten all functions with global namespace
2425 functions.insert(hash, f.clone());
2426 contains_indexed_global_functions = true;
2427 }
2428 FnNamespace::Internal => (),
2429 }
2430 match m.access {
2431 FnAccess::Public => (),
2432 FnAccess::Private => continue, // Do not index private functions
2433 }
2434
2435 if f.is_script() {
2436 #[cfg(not(feature = "no_function"))]
2437 {
2438 let hash_script =
2439 crate::calc_fn_hash(path.iter().copied(), &m.name, m.num_params);
2440 #[cfg(not(feature = "no_object"))]
2441 let hash_script = f
2442 .get_script_fn_def()
2443 .unwrap()
2444 .this_type
2445 .as_ref()
2446 .map_or(hash_script, |this_type| {
2447 crate::calc_typed_method_hash(hash_script, this_type)
2448 });
2449
2450 // Catch hash collisions in testing environment only.
2451 #[cfg(feature = "testing-environ")]
2452 if let Some(fx) = functions.get(&hash_script) {
2453 unreachable!(
2454 "Hash {} already exists when indexing function {:#?}:\n{:#?}",
2455 hash_script, f, fx
2456 );
2457 }
2458
2459 functions.insert(hash_script, f.clone());
2460 }
2461 } else {
2462 let hash_fn =
2463 calc_native_fn_hash(path.iter().copied(), &m.name, &m.param_types);
2464
2465 // Catch hash collisions in testing environment only.
2466 #[cfg(feature = "testing-environ")]
2467 if let Some(fx) = functions.get(&hash_fn) {
2468 unreachable!(
2469 "Hash {} already exists when indexing function {:#?}:\n{:#?}",
2470 hash_fn, f, fx
2471 );
2472 }
2473
2474 functions.insert(hash_fn, f.clone());
2475 }
2476 }
2477
2478 contains_indexed_global_functions
2479 }
2480
2481 if !self.is_indexed() {
2482 let mut path = Vec::with_capacity(4);
2483 let mut variables = new_hash_map(self.variables.len());
2484 let mut functions =
2485 new_hash_map(self.functions.as_ref().map_or(0, StraightHashMap::len));
2486 let mut type_iterators = BTreeMap::new();
2487
2488 path.push("");
2489
2490 let has_global_functions = index_module(
2491 self,
2492 &mut path,
2493 &mut variables,
2494 &mut functions,
2495 &mut type_iterators,
2496 );
2497
2498 self.flags
2499 .set(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS, has_global_functions);
2500
2501 self.all_variables = (!variables.is_empty()).then_some(variables);
2502 self.all_functions = (!functions.is_empty()).then_some(functions);
2503 self.all_type_iterators = type_iterators;
2504
2505 self.flags |= ModuleFlags::INDEXED;
2506 }
2507
2508 self
2509 }
2510
2511 /// Does a type iterator exist in the entire module tree?
2512 #[inline(always)]
2513 #[must_use]
2514 pub fn contains_qualified_iter(&self, id: TypeId) -> bool {
2515 self.all_type_iterators.contains_key(&id)
2516 }
2517
2518 /// Does a type iterator exist in the module?
2519 #[inline(always)]
2520 #[must_use]
2521 pub fn contains_iter(&self, id: TypeId) -> bool {
2522 self.type_iterators.contains_key(&id)
2523 }
2524
2525 /// Set a type iterator into the [`Module`].
2526 #[inline(always)]
2527 pub fn set_iter(
2528 &mut self,
2529 type_id: TypeId,
2530 func: impl Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + SendSync + 'static,
2531 ) -> &mut Self {
2532 self.set_iter_result(type_id, move |x| {
2533 Box::new(func(x).map(Ok)) as Box<dyn Iterator<Item = RhaiResultOf<Dynamic>>>
2534 })
2535 }
2536
2537 /// Set a fallible type iterator into the [`Module`].
2538 #[inline]
2539 pub fn set_iter_result(
2540 &mut self,
2541 type_id: TypeId,
2542 func: impl Fn(Dynamic) -> Box<dyn Iterator<Item = RhaiResultOf<Dynamic>>> + SendSync + 'static,
2543 ) -> &mut Self {
2544 let func = Shared::new(func);
2545 if self.is_indexed() {
2546 self.all_type_iterators.insert(type_id, func.clone());
2547 }
2548 self.type_iterators.insert(type_id, func);
2549 self
2550 }
2551
2552 /// Set a type iterator into the [`Module`].
2553 #[inline(always)]
2554 pub fn set_iterable<T>(&mut self) -> &mut Self
2555 where
2556 T: Variant + Clone + IntoIterator,
2557 <T as IntoIterator>::Item: Variant + Clone,
2558 {
2559 self.set_iter(TypeId::of::<T>(), |obj: Dynamic| {
2560 Box::new(obj.cast::<T>().into_iter().map(Dynamic::from))
2561 })
2562 }
2563
2564 /// Set a fallible type iterator into the [`Module`].
2565 #[inline(always)]
2566 pub fn set_iterable_result<T, X>(&mut self) -> &mut Self
2567 where
2568 T: Variant + Clone + IntoIterator<Item = RhaiResultOf<X>>,
2569 X: Variant + Clone,
2570 {
2571 self.set_iter_result(TypeId::of::<T>(), |obj: Dynamic| {
2572 Box::new(obj.cast::<T>().into_iter().map(|v| v.map(Dynamic::from)))
2573 })
2574 }
2575
2576 /// Set an iterator type into the [`Module`] as a type iterator.
2577 #[inline(always)]
2578 pub fn set_iterator<T>(&mut self) -> &mut Self
2579 where
2580 T: Variant + Clone + Iterator,
2581 <T as Iterator>::Item: Variant + Clone,
2582 {
2583 self.set_iter(TypeId::of::<T>(), |obj: Dynamic| {
2584 Box::new(obj.cast::<T>().map(Dynamic::from))
2585 })
2586 }
2587
2588 /// Set a iterator type into the [`Module`] as a fallible type iterator.
2589 #[inline(always)]
2590 pub fn set_iterator_result<T, X>(&mut self) -> &mut Self
2591 where
2592 T: Variant + Clone + Iterator<Item = RhaiResultOf<X>>,
2593 X: Variant + Clone,
2594 {
2595 self.set_iter_result(TypeId::of::<T>(), |obj: Dynamic| {
2596 Box::new(obj.cast::<T>().map(|v| v.map(Dynamic::from)))
2597 })
2598 }
2599
2600 /// Get the specified type iterator.
2601 #[cfg(not(feature = "no_module"))]
2602 #[inline]
2603 #[must_use]
2604 pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option<&FnIterator> {
2605 self.all_type_iterators.get(&id).map(|f| &**f)
2606 }
2607
2608 /// Get the specified type iterator.
2609 #[inline]
2610 #[must_use]
2611 pub(crate) fn get_iter(&self, id: TypeId) -> Option<&FnIterator> {
2612 self.type_iterators.get(&id).map(|f| &**f)
2613 }
2614}
2615
2616/// Module containing all built-in [module resolvers][ModuleResolver].
2617#[cfg(not(feature = "no_module"))]
2618pub mod resolvers;
2619
2620#[cfg(not(feature = "no_module"))]
2621pub use resolvers::ModuleResolver;