326 lines
9.7 KiB
Rust
326 lines
9.7 KiB
Rust
use crate::def_package;
|
|
use crate::module::ModuleFlags;
|
|
use crate::plugin::*;
|
|
use crate::types::dynamic::Tag;
|
|
use crate::{Dynamic, RhaiResultOf, ERR, INT};
|
|
#[cfg(feature = "no_std")]
|
|
use std::prelude::v1::*;
|
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
#[cfg(not(feature = "no_std"))]
|
|
use crate::FLOAT;
|
|
|
|
def_package! {
|
|
/// Package of core language features.
|
|
pub LanguageCorePackage(lib) {
|
|
lib.flags |= ModuleFlags::STANDARD_LIB;
|
|
|
|
combine_with_exported_module!(lib, "core", core_functions);
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
#[cfg(not(feature = "no_index"))]
|
|
#[cfg(not(feature = "no_object"))]
|
|
combine_with_exported_module!(lib, "reflection", reflection_functions);
|
|
}
|
|
}
|
|
|
|
#[export_module]
|
|
mod core_functions {
|
|
/// Return the _tag_ of a `Dynamic` value.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rhai
|
|
/// let x = "hello, world!";
|
|
///
|
|
/// x.tag = 42;
|
|
///
|
|
/// print(x.tag); // prints 42
|
|
/// ```
|
|
#[rhai_fn(name = "tag", get = "tag", pure)]
|
|
pub fn get_tag(value: &mut Dynamic) -> INT {
|
|
value.tag() as INT
|
|
}
|
|
/// Set the _tag_ of a `Dynamic` value.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rhai
|
|
/// let x = "hello, world!";
|
|
///
|
|
/// x.tag = 42;
|
|
///
|
|
/// print(x.tag); // prints 42
|
|
/// ```
|
|
#[rhai_fn(name = "set_tag", set = "tag", return_raw)]
|
|
pub fn set_tag(value: &mut Dynamic, tag: INT) -> RhaiResultOf<()> {
|
|
const TAG_MIN: Tag = Tag::MIN;
|
|
const TAG_MAX: Tag = Tag::MAX;
|
|
|
|
if tag < TAG_MIN as INT {
|
|
Err(ERR::ErrorArithmetic(
|
|
format!(
|
|
"{tag} is too small to fit into a tag (must be between {TAG_MIN} and {TAG_MAX})"
|
|
),
|
|
Position::NONE,
|
|
)
|
|
.into())
|
|
} else if tag > TAG_MAX as INT {
|
|
Err(ERR::ErrorArithmetic(
|
|
format!(
|
|
"{tag} is too large to fit into a tag (must be between {TAG_MIN} and {TAG_MAX})"
|
|
),
|
|
Position::NONE,
|
|
)
|
|
.into())
|
|
} else {
|
|
value.set_tag(tag as Tag);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Block the current thread for a particular number of `seconds`.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rhai
|
|
/// // Do nothing for 10 seconds!
|
|
/// sleep(10.0);
|
|
/// ```
|
|
#[cfg(not(feature = "no_float"))]
|
|
#[cfg(not(feature = "no_std"))]
|
|
#[rhai_fn(name = "sleep")]
|
|
pub fn sleep_float(seconds: FLOAT) {
|
|
if seconds <= 0.0 {
|
|
return;
|
|
}
|
|
|
|
#[cfg(not(feature = "f32_float"))]
|
|
std::thread::sleep(std::time::Duration::from_secs_f64(seconds));
|
|
#[cfg(feature = "f32_float")]
|
|
std::thread::sleep(std::time::Duration::from_secs_f32(seconds));
|
|
}
|
|
/// Block the current thread for a particular number of `seconds`.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rhai
|
|
/// // Do nothing for 10 seconds!
|
|
/// sleep(10);
|
|
/// ```
|
|
#[cfg(not(feature = "no_std"))]
|
|
pub fn sleep(seconds: INT) {
|
|
if seconds > 0 {
|
|
#[allow(clippy::cast_sign_loss)]
|
|
std::thread::sleep(std::time::Duration::from_secs(seconds as u64));
|
|
}
|
|
}
|
|
|
|
/// Parse a JSON string into a value.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rhai
|
|
/// let m = parse_json(`{"a":1, "b":2, "c":3}`);
|
|
///
|
|
/// print(m); // prints #{"a":1, "b":2, "c":3}
|
|
/// ```
|
|
#[cfg(not(feature = "no_index"))]
|
|
#[cfg(not(feature = "no_object"))]
|
|
#[cfg(feature = "metadata")]
|
|
#[rhai_fn(return_raw)]
|
|
pub fn parse_json(_ctx: NativeCallContext, json: &str) -> RhaiResultOf<Dynamic> {
|
|
serde_json::from_str(json).map_err(|err| err.to_string().into())
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
#[cfg(not(feature = "no_index"))]
|
|
#[cfg(not(feature = "no_object"))]
|
|
#[export_module]
|
|
mod reflection_functions {
|
|
use crate::Array;
|
|
|
|
/// Return an array of object maps containing metadata of all script-defined functions.
|
|
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> Array {
|
|
collect_fn_metadata(&ctx, |_, _, _, _, _| true)
|
|
}
|
|
/// Return an array of object maps containing metadata of all script-defined functions
|
|
/// matching the specified name.
|
|
#[rhai_fn(name = "get_fn_metadata_list")]
|
|
pub fn get_fn_metadata(ctx: NativeCallContext, name: &str) -> Array {
|
|
collect_fn_metadata(&ctx, |_, _, n, _, _| n == name)
|
|
}
|
|
/// Return an array of object maps containing metadata of all script-defined functions
|
|
/// matching the specified name and arity (number of parameters).
|
|
#[rhai_fn(name = "get_fn_metadata_list")]
|
|
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
|
pub fn get_fn_metadata2(ctx: NativeCallContext, name: &str, params: INT) -> Array {
|
|
if (0..=crate::MAX_USIZE_INT).contains(¶ms) {
|
|
collect_fn_metadata(&ctx, |_, _, n, p, _| p == (params as usize) && n == name)
|
|
} else {
|
|
Array::new()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
#[cfg(not(feature = "no_index"))]
|
|
#[cfg(not(feature = "no_object"))]
|
|
fn collect_fn_metadata(
|
|
ctx: &NativeCallContext,
|
|
filter: impl Fn(FnNamespace, FnAccess, &str, usize, &crate::Shared<crate::ast::ScriptFnDef>) -> bool
|
|
+ Copy,
|
|
) -> crate::Array {
|
|
#[cfg(not(feature = "no_module"))]
|
|
use crate::Identifier;
|
|
use crate::{ast::ScriptFnDef, engine::FN_ANONYMOUS, Array, Map};
|
|
|
|
// Create a metadata record for a function.
|
|
fn make_metadata(
|
|
engine: &Engine,
|
|
#[cfg(not(feature = "no_module"))] namespace: Identifier,
|
|
func: &ScriptFnDef,
|
|
) -> Map {
|
|
let mut map = Map::new();
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
if !namespace.is_empty() {
|
|
map.insert(
|
|
"namespace".into(),
|
|
engine.get_interned_string(namespace).into(),
|
|
);
|
|
}
|
|
map.insert(
|
|
"name".into(),
|
|
engine.get_interned_string(func.name.clone()).into(),
|
|
);
|
|
map.insert(
|
|
"access".into(),
|
|
engine
|
|
.get_interned_string(match func.access {
|
|
FnAccess::Public => "public",
|
|
FnAccess::Private => "private",
|
|
})
|
|
.into(),
|
|
);
|
|
map.insert(
|
|
"is_anonymous".into(),
|
|
func.name.starts_with(FN_ANONYMOUS).into(),
|
|
);
|
|
#[cfg(not(feature = "no_object"))]
|
|
if let Some(ref this_type) = func.this_type {
|
|
map.insert("this_type".into(), this_type.into());
|
|
}
|
|
map.insert(
|
|
"params".into(),
|
|
func.params
|
|
.iter()
|
|
.map(|p| engine.get_interned_string(p.clone()).into())
|
|
.collect::<Array>()
|
|
.into(),
|
|
);
|
|
#[cfg(feature = "metadata")]
|
|
if !func.comments.is_empty() {
|
|
map.insert(
|
|
"comments".into(),
|
|
func.comments
|
|
.iter()
|
|
.map(|s| engine.get_interned_string(s.as_str()).into())
|
|
.collect::<Array>()
|
|
.into(),
|
|
);
|
|
}
|
|
|
|
map
|
|
}
|
|
|
|
let engine = ctx.engine();
|
|
let mut list = Array::new();
|
|
|
|
ctx.iter_namespaces()
|
|
.flat_map(Module::iter_script_fn)
|
|
.filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
|
|
.for_each(|(.., f)| {
|
|
list.push(
|
|
make_metadata(
|
|
engine,
|
|
#[cfg(not(feature = "no_module"))]
|
|
Identifier::new_const(),
|
|
f,
|
|
)
|
|
.into(),
|
|
);
|
|
});
|
|
|
|
ctx.engine()
|
|
.global_modules
|
|
.iter()
|
|
.flat_map(|m| m.iter_script_fn())
|
|
.filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
|
|
.for_each(|(.., f)| {
|
|
list.push(
|
|
make_metadata(
|
|
engine,
|
|
#[cfg(not(feature = "no_module"))]
|
|
Identifier::new_const(),
|
|
f,
|
|
)
|
|
.into(),
|
|
);
|
|
});
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
ctx.engine()
|
|
.global_sub_modules
|
|
.as_ref()
|
|
.into_iter()
|
|
.flatten()
|
|
.flat_map(|(_, m)| m.iter_script_fn())
|
|
.filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
|
|
.for_each(|(.., f)| {
|
|
list.push(
|
|
make_metadata(
|
|
engine,
|
|
#[cfg(not(feature = "no_module"))]
|
|
Identifier::new_const(),
|
|
f,
|
|
)
|
|
.into(),
|
|
);
|
|
});
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
{
|
|
use crate::engine::NAMESPACE_SEPARATOR;
|
|
use crate::{Shared, SmartString};
|
|
|
|
// Recursively scan modules for script-defined functions.
|
|
fn scan_module(
|
|
engine: &Engine,
|
|
list: &mut Array,
|
|
namespace: &str,
|
|
module: &Module,
|
|
filter: impl Fn(FnNamespace, FnAccess, &str, usize, &Shared<ScriptFnDef>) -> bool + Copy,
|
|
) {
|
|
module
|
|
.iter_script_fn()
|
|
.filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
|
|
.for_each(|(.., f)| list.push(make_metadata(engine, namespace.into(), f).into()));
|
|
for (name, m) in module.iter_sub_modules() {
|
|
use std::fmt::Write;
|
|
|
|
let mut ns = SmartString::new_const();
|
|
write!(&mut ns, "{namespace}{}{name}", NAMESPACE_SEPARATOR).unwrap();
|
|
scan_module(engine, list, &ns, m, filter);
|
|
}
|
|
}
|
|
|
|
for (ns, m) in ctx.iter_imports_raw() {
|
|
scan_module(engine, &mut list, ns, m, filter);
|
|
}
|
|
}
|
|
|
|
list
|
|
}
|