Skip to content

Commit ae662cb

Browse files
authored
Merge pull request #945 from schungx/master
Add collect_fn_metadata.
2 parents 2ed0d0e + f2eeb94 commit ae662cb

File tree

9 files changed

+261
-176
lines changed

9 files changed

+261
-176
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
Rhai Release Notes
22
==================
33

4-
Version 1.21.0
4+
Version 1.20.1
55
==============
66

77
Bug fixes
88
---------
99

1010
* Fixed bug in raw strings with newlines (thanks [`@benatkin`](https://github.com/benatkin) [940](https://github.com/rhaiscript/rhai/pull/940)).
11+
* `get_fn_metadata_list` function is marked `volatile`.
1112

1213
Enhancements
1314
------------
1415

1516
* If a string slice refers to the entire string, the slice is not cloned but returned as-is.
17+
* A new `internals` function, `Engine::collect_fn_metadata`, is added to collect all functions metadata. This is to facilitate better error reporting for missing functions (thanks [`therealprof`](https://github.com/therealprof) [899](https://github.com/rhaiscript/rhai/issues/899)).
1618

1719

1820
Version 1.20.0

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ members = [".", "codegen"]
33

44
[package]
55
name = "rhai"
6-
version = "1.21.0"
6+
version = "1.20.1"
77
rust-version = "1.66.0"
88
edition = "2018"
99
resolver = "2"

src/api/register.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,4 +752,134 @@ impl Engine {
752752

753753
signatures
754754
}
755+
756+
/// Collect the [`FuncInfo`][crate::module::FuncInfo] of all functions, native or script-defined,
757+
/// mapping them into any type.
758+
/// Exported under the `internals` feature only.
759+
///
760+
/// Return [`None`] from the `mapper` to skip a function.
761+
///
762+
/// Functions from the following sources are included, in order:
763+
/// 1) Functions defined in the current script (if any)
764+
/// 2) Functions registered into the global namespace
765+
/// 3) Functions in registered packages
766+
/// 4) Functions in standard packages (optional)
767+
/// 5) Functions defined in modules `import`-ed by the current script (if any)
768+
/// 6) Functions in registered sub-modules
769+
#[cfg(feature = "internals")]
770+
#[inline(always)]
771+
pub fn collect_fn_metadata<T>(
772+
&self,
773+
ctx: Option<&NativeCallContext>,
774+
mapper: impl Fn(crate::module::FuncInfo) -> Option<T> + Copy,
775+
include_standard_packages: bool,
776+
) -> Vec<T> {
777+
self.collect_fn_metadata_impl(ctx, mapper, include_standard_packages)
778+
}
779+
780+
/// Collect the [`FuncInfo`][crate::module::FuncInfo] of all functions, native or script-defined,
781+
/// mapping them into any type.
782+
///
783+
/// Return [`None`] from the `mapper` to skip a function.
784+
///
785+
/// Functions from the following sources are included, in order:
786+
/// 1) Functions defined in the current script (if any)
787+
/// 2) Functions registered into the global namespace
788+
/// 3) Functions in registered packages
789+
/// 4) Functions in standard packages (optional)
790+
/// 5) Functions defined in modules `import`-ed by the current script (if any)
791+
/// 6) Functions in registered sub-modules
792+
#[allow(dead_code)]
793+
pub(crate) fn collect_fn_metadata_impl<T>(
794+
&self,
795+
ctx: Option<&NativeCallContext>,
796+
mapper: impl Fn(crate::module::FuncInfo) -> Option<T> + Copy,
797+
include_standard_packages: bool,
798+
) -> Vec<T> {
799+
let mut list = Vec::new();
800+
801+
#[cfg(not(feature = "no_function"))]
802+
if let Some(ctx) = ctx {
803+
ctx.iter_namespaces()
804+
.flat_map(Module::iter_fn)
805+
.filter_map(|(func, f)| {
806+
mapper(crate::module::FuncInfo {
807+
metadata: f,
808+
#[cfg(not(feature = "no_module"))]
809+
namespace: Identifier::new_const(),
810+
script: func.get_script_fn_def().map(|f| (&**f).into()),
811+
})
812+
})
813+
.for_each(|v| list.push(v));
814+
}
815+
816+
self.global_modules
817+
.iter()
818+
.filter(|m| !m.is_internal() && (include_standard_packages || !m.is_standard_lib()))
819+
.flat_map(|m| m.iter_fn())
820+
.filter_map(|(_func, f)| {
821+
mapper(crate::module::FuncInfo {
822+
metadata: f,
823+
#[cfg(not(feature = "no_module"))]
824+
namespace: Identifier::new_const(),
825+
#[cfg(not(feature = "no_function"))]
826+
script: _func.get_script_fn_def().map(|f| (&**f).into()),
827+
})
828+
})
829+
.for_each(|v| list.push(v));
830+
831+
#[cfg(not(feature = "no_module"))]
832+
if let Some(ctx) = ctx {
833+
use crate::engine::NAMESPACE_SEPARATOR;
834+
use crate::SmartString;
835+
836+
// Recursively scan modules for script-defined functions.
837+
fn scan_module<T>(
838+
list: &mut Vec<T>,
839+
namespace: &str,
840+
module: &Module,
841+
mapper: impl Fn(crate::module::FuncInfo) -> Option<T> + Copy,
842+
) {
843+
module
844+
.iter_fn()
845+
.filter_map(|(_func, f)| {
846+
mapper(crate::module::FuncInfo {
847+
metadata: f,
848+
namespace: namespace.into(),
849+
#[cfg(not(feature = "no_function"))]
850+
script: _func.get_script_fn_def().map(|f| (&**f).into()),
851+
})
852+
})
853+
.for_each(|v| list.push(v));
854+
855+
for (name, m) in module.iter_sub_modules() {
856+
use std::fmt::Write;
857+
858+
let mut ns = SmartString::new_const();
859+
write!(&mut ns, "{namespace}{NAMESPACE_SEPARATOR}{name}").unwrap();
860+
scan_module(list, &ns, m, mapper);
861+
}
862+
}
863+
864+
for (ns, m) in ctx.global_runtime_state().iter_imports_raw() {
865+
scan_module(&mut list, ns, m, mapper);
866+
}
867+
}
868+
869+
#[cfg(not(feature = "no_module"))]
870+
self.global_sub_modules
871+
.values()
872+
.flat_map(|m| m.iter_fn())
873+
.filter_map(|(_func, f)| {
874+
mapper(crate::module::FuncInfo {
875+
metadata: f,
876+
namespace: Identifier::new_const(),
877+
#[cfg(not(feature = "no_function"))]
878+
script: _func.get_script_fn_def().map(|f| (&**f).into()),
879+
})
880+
})
881+
.for_each(|v| list.push(v));
882+
883+
list
884+
}
755885
}

src/eval/stmt.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@ use crate::func::{get_builtin_op_assignment_fn, get_hasher};
88
use crate::tokenizer::Token;
99
use crate::types::dynamic::{AccessMode, Union};
1010
use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, VarDefInfo, ERR, INT};
11+
use std::hash::{Hash, Hasher};
1112
#[cfg(feature = "no_std")]
1213
use std::prelude::v1::*;
13-
use std::{
14-
convert::TryInto,
15-
hash::{Hash, Hasher},
16-
};
1714

1815
impl Engine {
1916
/// If the value is a string, intern it.
@@ -307,6 +304,8 @@ impl Engine {
307304

308305
#[cfg(not(feature = "no_function"))]
309306
{
307+
use std::convert::TryInto;
308+
310309
let rhs_val = self
311310
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), rhs)?
312311
.flatten();

src/eval/target.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Type to hold a mutable reference to the target of an evaluation.
22
3-
use crate::{Dynamic, EvalAltResult, Position, RhaiError, RhaiResultOf};
3+
use crate::{Dynamic, Position, RhaiError, RhaiResultOf};
44
#[cfg(feature = "no_std")]
55
use std::prelude::v1::*;
66
use std::{
@@ -428,7 +428,9 @@ impl<'a> TryFrom<&'a mut Dynamic> for Target<'a> {
428428
// Cloning is cheap for a shared value
429429
let shared_value = value.clone();
430430
let Some(guard) = value.write_lock::<Dynamic>() else {
431-
return Err(EvalAltResult::ErrorDataRace(String::new(), Position::NONE).into());
431+
return Err(
432+
crate::EvalAltResult::ErrorDataRace(String::new(), Position::NONE).into(),
433+
);
432434
};
433435
return Ok(Self::SharedValue {
434436
guard,

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@ pub use optimizer::OptimizationLevel;
328328
#[cfg(feature = "internals")]
329329
pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant};
330330

331+
#[cfg(feature = "internals")]
332+
pub use module::{FuncInfo, FuncMetadata};
333+
331334
#[cfg(feature = "internals")]
332335
#[cfg(not(feature = "no_float"))]
333336
pub use types::FloatWrapper;

src/module/mod.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ impl FnNamespace {
6969
}
7070
}
7171

72-
/// A type containing the metadata of a single registered function.
72+
/// _(internals)_ A type containing the metadata of a single registered function.
73+
/// Exported under the `internals` features only.
7374
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
7475
#[non_exhaustive]
7576
pub struct FuncMetadata {
@@ -86,12 +87,15 @@ pub struct FuncMetadata {
8687
/// Parameter types (if applicable).
8788
pub param_types: FnArgsVec<TypeId>,
8889
/// Parameter names and types (if available).
90+
/// Exported under the `metadata` feature only.
8991
#[cfg(feature = "metadata")]
9092
pub params_info: FnArgsVec<Identifier>,
9193
/// Return type name.
94+
/// Exported under the `metadata` feature only.
9295
#[cfg(feature = "metadata")]
9396
pub return_type: Identifier,
9497
/// Comments.
98+
/// Exported under the `metadata` feature only.
9599
#[cfg(feature = "metadata")]
96100
pub comments: crate::StaticVec<SmartString>,
97101
}
@@ -152,6 +156,25 @@ impl FuncMetadata {
152156
}
153157
}
154158

159+
/// Information about a function, native or scripted.
160+
///
161+
/// Exported under the `internals` feature only.
162+
#[allow(dead_code)]
163+
pub struct FuncInfo<'a> {
164+
/// Function metadata.
165+
pub metadata: &'a FuncMetadata,
166+
/// Function namespace.
167+
///
168+
/// Not available under `no_module`.
169+
#[cfg(not(feature = "no_module"))]
170+
pub namespace: Identifier,
171+
/// Metadata if the function is scripted.
172+
///
173+
/// Not available under `no_function`.
174+
#[cfg(not(feature = "no_function"))]
175+
pub script: Option<crate::ScriptFnMetadata<'a>>,
176+
}
177+
155178
/// _(internals)_ Calculate a [`u64`] hash key from a namespace-qualified function name and parameter types.
156179
/// Exported under the `internals` feature only.
157180
///
@@ -400,7 +423,7 @@ impl FuncRegistration {
400423
{
401424
#[cfg(feature = "metadata")]
402425
{
403-
// Do not update parameter informations if `with_params_info` was called previously.
426+
// Do not update parameter information if `with_params_info` was called previously.
404427
if self.metadata.params_info.is_empty() {
405428
let mut param_type_names = FUNC::param_names()
406429
.iter()

0 commit comments

Comments
 (0)