diff --git a/CHANGELOG.md b/CHANGELOG.md index a17f951c..e1e53315 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,11 @@ New features * For very special needs, the ability to register fallible type iterators is added. +Enhancements +------------ + +* `is_empty` method is added to arrays, BLOB's, object maps, strings and ranges. + Version 1.9.0 ============= diff --git a/Cargo.toml b/Cargo.toml index 7ccfe77d..c4e9e2cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ keywords = ["scripting", "scripting-engine", "scripting-language", "embedded"] categories = ["no-std", "embedded", "wasm", "parser-implementations"] [dependencies] -smallvec = { version = "1.7", default-features = false, features = ["union", "const_new" ] } +smallvec = { version = "1.7", default-features = false, features = ["union", "const_new", "const_generics"] } ahash = { version = "0.8", default-features = false } num-traits = { version = "0.2", default-features = false } bitflags = { version = "1", default-features = false } diff --git a/codegen/ui_tests/non_clonable.stderr b/codegen/ui_tests/non_clonable.stderr index 5e50c242..83d12899 100644 --- a/codegen/ui_tests/non_clonable.stderr +++ b/codegen/ui_tests/non_clonable.stderr @@ -1,8 +1,6 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied --> ui_tests/non_clonable.rs:11:23 | -10 | #[export_fn] - | ------------ in this procedural macro expansion 11 | pub fn test_fn(input: NonClonable) -> bool { | ^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable` | @@ -11,7 +9,6 @@ note: required by a bound in `rhai::Dynamic::cast` | | pub fn cast(self) -> T { | ^^^^^ required by this bound in `rhai::Dynamic::cast` - = note: this error originates in the attribute macro `export_fn` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `NonClonable` with `#[derive(Clone)]` | 3 | #[derive(Clone)] diff --git a/codegen/ui_tests/non_clonable_second.stderr b/codegen/ui_tests/non_clonable_second.stderr index a4c40461..241d41d6 100644 --- a/codegen/ui_tests/non_clonable_second.stderr +++ b/codegen/ui_tests/non_clonable_second.stderr @@ -1,8 +1,6 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied --> ui_tests/non_clonable_second.rs:11:27 | -10 | #[export_fn] - | ------------ in this procedural macro expansion 11 | pub fn test_fn(a: u32, b: NonClonable) -> bool { | ^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable` | @@ -11,7 +9,6 @@ note: required by a bound in `rhai::Dynamic::cast` | | pub fn cast(self) -> T { | ^^^^^ required by this bound in `rhai::Dynamic::cast` - = note: this error originates in the attribute macro `export_fn` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `NonClonable` with `#[derive(Clone)]` | 3 | #[derive(Clone)] diff --git a/examples/definitions/.rhai/all_in_one.d.rhai b/examples/definitions/.rhai/all_in_one.d.rhai index 962551a7..9cf5fa41 100644 --- a/examples/definitions/.rhai/all_in_one.d.rhai +++ b/examples/definitions/.rhai/all_in_one.d.rhai @@ -785,6 +785,12 @@ op +(u8, u8) -> u8; /// ``` op +=(Map, Map) -> (); +op +=(String, String) -> (); + +op +=(String, char) -> (); + +op +=(String, ()) -> (); + op +=(String, ?) -> (); op +=(String, Blob) -> (); @@ -2368,6 +2374,21 @@ fn get int(x: float) -> float; /// ``` fn get is_anonymous(fn_ptr: FnPtr) -> bool; +/// Return true if the array is empty. +fn get is_empty(array: Array) -> bool; + +/// Return true if the BLOB is empty. +fn get is_empty(blob: Blob) -> bool; + +/// Return true if the range contains no items. +fn get is_empty(range: ExclusiveRange) -> bool; + +/// Return true if the range contains no items. +fn get is_empty(range: InclusiveRange) -> bool; + +/// Return true if the string is empty. +fn get is_empty(string: String) -> bool; + /// Return true if the number is even. fn get is_even(x: int) -> bool; @@ -2913,6 +2934,24 @@ fn int(x: float) -> float; /// ``` fn is_anonymous(fn_ptr: FnPtr) -> bool; +/// Return true if the array is empty. +fn is_empty(array: Array) -> bool; + +/// Return true if the BLOB is empty. +fn is_empty(blob: Blob) -> bool; + +/// Return true if the map is empty. +fn is_empty(map: Map) -> bool; + +/// Return true if the range contains no items. +fn is_empty(range: ExclusiveRange) -> bool; + +/// Return true if the range contains no items. +fn is_empty(range: InclusiveRange) -> bool; + +/// Return true if the string is empty. +fn is_empty(string: String) -> bool; + /// Return true if the number is even. fn is_even(x: int) -> bool; @@ -3047,9 +3086,6 @@ fn keys(map: Map) -> Array; /// Number of elements in the array. fn len(array: Array) -> int; -/// Return true if the array is empty. -fn is_empty(array: Array) -> bool; - /// Return the length of the BLOB. /// /// # Example @@ -3063,15 +3099,9 @@ fn is_empty(array: Array) -> bool; /// ``` fn len(blob: Blob) -> int; -/// Return true if the blob is empty. -fn is_empty(blob: Blob) -> bool; - /// Return the number of properties in the object map. fn len(map: Map) -> int; -/// Return true if the map is empty. -fn is_empty(map: Map) -> bool; - /// Return the length of the string, in number of characters. /// /// # Example @@ -3083,9 +3113,6 @@ fn is_empty(map: Map) -> bool; /// ``` fn len(string: String) -> int; -/// Return true if the string is empty. -fn is_empty(string: String) -> bool; - /// Return the natural log of the decimal number. fn ln(x: Decimal) -> Decimal; @@ -5738,6 +5765,16 @@ fn tan(x: float) -> float; fn tanh(x: float) -> float; /// Create a timestamp containing the current system time. +/// +/// # Example +/// +/// ```rhai +/// let now = timestamp(); +/// +/// sleep(10.0); // sleep for 10 seconds +/// +/// print(now.elapsed); // prints 10.??? +/// ``` fn timestamp() -> Instant; /// Convert the BLOB into an array of integers. diff --git a/examples/definitions/.rhai/definitions/__static__.d.rhai b/examples/definitions/.rhai/definitions/__static__.d.rhai index 35ceba47..f9c3867f 100644 --- a/examples/definitions/.rhai/definitions/__static__.d.rhai +++ b/examples/definitions/.rhai/definitions/__static__.d.rhai @@ -267,6 +267,12 @@ op +(u8, u8) -> u8; /// ``` op +=(Map, Map) -> (); +op +=(String, String) -> (); + +op +=(String, char) -> (); + +op +=(String, ()) -> (); + op +=(String, ?) -> (); op +=(String, Blob) -> (); @@ -1850,6 +1856,21 @@ fn get int(x: float) -> float; /// ``` fn get is_anonymous(fn_ptr: FnPtr) -> bool; +/// Return true if the array is empty. +fn get is_empty(array: Array) -> bool; + +/// Return true if the BLOB is empty. +fn get is_empty(blob: Blob) -> bool; + +/// Return true if the range contains no items. +fn get is_empty(range: ExclusiveRange) -> bool; + +/// Return true if the range contains no items. +fn get is_empty(range: InclusiveRange) -> bool; + +/// Return true if the string is empty. +fn get is_empty(string: String) -> bool; + /// Return true if the number is even. fn get is_even(x: int) -> bool; @@ -1973,9 +1994,6 @@ fn get is_zero(x: u8) -> bool; /// Number of elements in the array. fn get len(array: Array) -> int; -/// Return true if the array is empty. -fn get empty(array: Array) -> bool; - /// Return the length of the BLOB. /// /// # Example @@ -2398,6 +2416,24 @@ fn int(x: float) -> float; /// ``` fn is_anonymous(fn_ptr: FnPtr) -> bool; +/// Return true if the array is empty. +fn is_empty(array: Array) -> bool; + +/// Return true if the BLOB is empty. +fn is_empty(blob: Blob) -> bool; + +/// Return true if the map is empty. +fn is_empty(map: Map) -> bool; + +/// Return true if the range contains no items. +fn is_empty(range: ExclusiveRange) -> bool; + +/// Return true if the range contains no items. +fn is_empty(range: InclusiveRange) -> bool; + +/// Return true if the string is empty. +fn is_empty(string: String) -> bool; + /// Return true if the number is even. fn is_even(x: int) -> bool; @@ -2532,9 +2568,6 @@ fn keys(map: Map) -> Array; /// Number of elements in the array. fn len(array: Array) -> int; -/// Return true if the array is empty. -fn is_empty(array: Array) -> bool; - /// Return the length of the BLOB. /// /// # Example @@ -2548,15 +2581,9 @@ fn is_empty(array: Array) -> bool; /// ``` fn len(blob: Blob) -> int; -/// Return true if the blob is empty. -fn is_empty(blob: Blob) -> bool; - /// Return the number of properties in the object map. fn len(map: Map) -> int; -/// Return true if the map is empty. -fn is_empty(map: Map) -> bool; - /// Return the length of the string, in number of characters. /// /// # Example @@ -2568,9 +2595,6 @@ fn is_empty(map: Map) -> bool; /// ``` fn len(string: String) -> int; -/// Return true if the string is empty. -fn is_empty(string: String) -> bool; - /// Return the natural log of the decimal number. fn ln(x: Decimal) -> Decimal; diff --git a/examples/definitions/.rhai/defs.json b/examples/definitions/.rhai/defs.json index edc91805..4f007907 100644 --- a/examples/definitions/.rhai/defs.json +++ b/examples/definitions/.rhai/defs.json @@ -3,8 +3,8 @@ "general_kenobi": { "functions": [ { - "baseHash": 2647973605745432570, - "fullHash": 14052888716942083368, + "baseHash": 14798413363692662073, + "fullHash": 2039416761244929762, "namespace": "internal", "access": "public", "name": "hello_there", @@ -27,8 +27,8 @@ }, "functions": [ { - "baseHash": 6399059862174416123, - "fullHash": 15886705420394126404, + "baseHash": 17487674894006547092, + "fullHash": 13058993152904417424, "namespace": "global", "access": "public", "name": "minus", diff --git a/src/api/build_type.rs b/src/api/build_type.rs index 20556eb7..9ce59ba4 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -128,11 +128,11 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> { /// Register a custom function. #[inline(always)] - pub fn with_fn(&mut self, name: N, method: F) -> &mut Self - where - N: AsRef + Into, - F: RegisterNativeFunction, - { + pub fn with_fn( + &mut self, + name: impl AsRef + Into, + method: impl RegisterNativeFunction, + ) -> &mut Self { self.engine.register_fn(name, method); self } diff --git a/src/api/deprecated.rs b/src/api/deprecated.rs index b1a06279..c4fa140c 100644 --- a/src/api/deprecated.rs +++ b/src/api/deprecated.rs @@ -143,11 +143,11 @@ impl Engine { /// This method will be removed in the next major version. #[deprecated(since = "1.9.1", note = "use `register_fn` instead")] #[inline(always)] - pub fn register_result_fn(&mut self, name: N, func: F) -> &mut Self - where - N: AsRef + Into, - F: RegisterNativeFunction>, - { + pub fn register_result_fn( + &mut self, + name: impl AsRef + Into, + func: impl RegisterNativeFunction>, + ) -> &mut Self { self.register_fn(name, func) } /// Register a getter function for a member of a registered type with the [`Engine`]. diff --git a/src/api/register.rs b/src/api/register.rs index 01b4f048..f8102864 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -53,11 +53,11 @@ impl Engine { /// # } /// ``` #[inline] - pub fn register_fn(&mut self, name: N, func: F) -> &mut Self - where - N: AsRef + Into, - F: RegisterNativeFunction, - { + pub fn register_fn>( + &mut self, + name: impl AsRef + Into, + func: F, + ) -> &mut Self { let param_types = F::param_types(); #[cfg(feature = "metadata")] @@ -109,16 +109,12 @@ impl Engine { /// /// To access the first mutable parameter, use `args.get_mut(0).unwrap()` #[inline(always)] - pub fn register_raw_fn( + pub fn register_raw_fn( &mut self, - name: N, + name: impl AsRef + Into, arg_types: impl AsRef<[TypeId]>, func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf + SendSync + 'static, - ) -> &mut Self - where - N: AsRef + Into, - T: Variant + Clone, - { + ) -> &mut Self { self.global_namespace_mut().set_raw_fn( name, FnNamespace::Global, diff --git a/src/ast/script_fn.rs b/src/ast/script_fn.rs index 1ce664e0..d40d6a5a 100644 --- a/src/ast/script_fn.rs +++ b/src/ast/script_fn.rs @@ -36,7 +36,7 @@ pub struct ScriptFnDef { /// Encapsulated AST environment, if any. #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] - pub environ: Option, + pub environ: Option>, /// Function name. pub name: ImmutableString, /// Function access mode. diff --git a/src/func/register.rs b/src/func/register.rs index d9e45e76..40035b64 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -11,20 +11,23 @@ use crate::{reify, Dynamic, NativeCallContext, RhaiResultOf}; use std::prelude::v1::*; use std::{any::TypeId, mem}; -// These types are used to build a unique _marker_ tuple type for each combination -// of function parameter types in order to make each trait implementation unique. -// That is because stable Rust currently does not allow distinguishing implementations -// based purely on parameter types of traits (`Fn`, `FnOnce` and `FnMut`). -// -// For example: -// -// `NativeFunction<(Mut, B, Ref), R>` -// -// will have the function prototype constraint to: -// -// `FN: (&mut A, B, &C) -> R` -// -// These types are not actually used anywhere. +/// These types are used to build a unique _marker_ tuple type for each combination +/// of function parameter types in order to make each trait implementation unique. +/// +/// That is because stable Rust currently does not allow distinguishing implementations +/// based purely on parameter types of traits (`Fn`, `FnOnce` and `FnMut`). +/// +/// # Examples +/// +/// `RegisterNativeFunction<(Mut, B, Ref), R, ()>` = `Fn(&mut A, B, &C) -> R` +/// +/// `RegisterNativeFunction<(Mut, B, Ref), R, NativeCallContext>` = `Fn(NativeCallContext, &mut A, B, &C) -> R` +/// +/// `RegisterNativeFunction<(Mut, B, Ref), R, RhaiResultOf<()>>` = `Fn(&mut A, B, &C) -> Result>` +/// +/// `RegisterNativeFunction<(Mut, B, Ref), R, RhaiResultOf>` = `Fn(NativeCallContext, &mut A, B, &C) -> Result>` +/// +/// These types are not actually used anywhere. pub struct Mut(T); //pub struct Ref(T); @@ -60,6 +63,11 @@ pub fn by_value(data: &mut Dynamic) -> T { } /// Trait to register custom Rust functions. +/// +/// # Type Parameters +/// +/// * `ARGS` - a tuple containing parameter types, with `&mut T` represented by [`Mut`]. +/// * `RET` - return type of the function; if the function returns `Result`, it is the unwrapped inner value type. pub trait RegisterNativeFunction { /// Convert this function into a [`CallableFunction`]. #[must_use] diff --git a/src/func/script.rs b/src/func/script.rs index eeb41693..691ac0f6 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -111,12 +111,13 @@ impl Engine { let mut lib_merged = crate::StaticVec::with_capacity(lib.len() + 1); #[cfg(not(feature = "no_module"))] - let (lib, constants) = if let Some(crate::ast::EncapsulatedEnviron { - lib: ref fn_lib, - ref imports, - ref constants, - }) = fn_def.environ - { + let (lib, constants) = if let Some(ref environ) = fn_def.environ { + let crate::ast::EncapsulatedEnviron { + lib: fn_lib, + imports, + constants, + } = environ.as_ref(); + imports .iter() .cloned() diff --git a/src/module/mod.rs b/src/module/mod.rs index 07d9c261..5a11eae1 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1245,18 +1245,14 @@ impl Module { /// assert!(module.contains_fn(hash)); /// ``` #[inline(always)] - pub fn set_raw_fn( + pub fn set_raw_fn( &mut self, name: impl AsRef, namespace: FnNamespace, access: FnAccess, arg_types: impl AsRef<[TypeId]>, - func: F, - ) -> u64 - where - T: Variant + Clone, - F: Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf + SendSync + 'static, - { + func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf + SendSync + 'static, + ) -> u64 { let f = move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from); @@ -1293,11 +1289,14 @@ impl Module { /// assert!(module.contains_fn(hash)); /// ``` #[inline(always)] - pub fn set_native_fn(&mut self, name: N, func: F) -> u64 + pub fn set_native_fn( + &mut self, + name: impl AsRef + Into, + func: F, + ) -> u64 where - N: AsRef + Into, T: Variant + Clone, - F: RegisterNativeFunction>, + F: RegisterNativeFunction>, { self.set_fn( name, @@ -1587,16 +1586,17 @@ impl Module { /// ``` #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[inline(always)] - pub fn set_indexer_get_set_fn( - &mut self, - get_fn: impl RegisterNativeFunction<(Mut, B), T, RhaiResultOf> + SendSync + 'static, - set_fn: impl RegisterNativeFunction<(Mut, B, T), (), RhaiResultOf> + SendSync + 'static, - ) -> (u64, u64) - where + pub fn set_indexer_get_set_fn< A: Variant + Clone, B: Variant + Clone, T: Variant + Clone, - { + S1, + S2, + >( + &mut self, + get_fn: impl RegisterNativeFunction<(Mut, B), T, RhaiResultOf> + SendSync + 'static, + set_fn: impl RegisterNativeFunction<(Mut, B, T), (), RhaiResultOf> + SendSync + 'static, + ) -> (u64, u64) { ( self.set_indexer_get_fn(get_fn), self.set_indexer_set_fn(set_fn), @@ -2086,6 +2086,12 @@ impl Module { // Non-private functions defined become module functions #[cfg(not(feature = "no_function"))] { + let environ = Shared::new(crate::ast::EncapsulatedEnviron { + lib: ast.shared_lib().clone(), + imports: imports.into_boxed_slice(), + constants, + }); + ast.shared_lib() .iter_fn() .filter(|&f| match f.metadata.access { @@ -2102,11 +2108,7 @@ impl Module { .clone(); // Encapsulate AST environment - func.environ = Some(crate::ast::EncapsulatedEnviron { - lib: ast.shared_lib().clone(), - imports: imports.clone().into_boxed_slice(), - constants: constants.clone(), - }); + func.environ = Some(environ.clone()); module.set_script_fn(func); }); diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index d37f0ef0..bdf96fd5 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -140,7 +140,7 @@ pub mod blob_functions { pub fn len(blob: &mut Blob) -> INT { blob.len() as INT } - /// Return true if the blob is empty. + /// Return true if the BLOB is empty. #[rhai_fn(name = "is_empty", get = "is_empty", pure)] pub fn is_empty(blob: &mut Blob) -> bool { blob.len() == 0 diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 02089952..245f2cb3 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -666,7 +666,7 @@ mod range_functions { let _ = range; true } - /// Returns true if the range contains no items. + /// Return true if the range contains no items. #[rhai_fn(get = "is_empty", name = "is_empty", pure)] pub fn is_empty_exclusive(range: &mut ExclusiveRange) -> bool { range.is_empty() @@ -693,7 +693,7 @@ mod range_functions { let _ = range; false } - /// Returns true if the range contains no items. + /// Return true if the range contains no items. #[rhai_fn(get = "is_empty", name = "is_empty", pure)] pub fn is_empty_inclusive(range: &mut InclusiveRange) -> bool { range.is_empty() diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 4d75ba15..09ffdce6 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -26,7 +26,7 @@ mod map_functions { map.len() as INT } /// Return true if the map is empty. - #[rhai_fn(name = "is_empty", get = "is_empty", pure)] + #[rhai_fn(pure)] pub fn is_empty(map: &mut Map) -> bool { map.len() == 0 } diff --git a/src/serde/de.rs b/src/serde/de.rs index aa6bbd66..15c538cd 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -515,41 +515,31 @@ impl<'a: 'de, 'de, ITER: Iterator> serde::de::SeqAccess<'de> /// `MapAccess` implementation for maps. #[cfg(not(feature = "no_object"))] -struct IterateMap<'a, KEYS, VALUES> -where - KEYS: Iterator, - VALUES: Iterator, -{ +struct IterateMap<'a, K: Iterator, V: Iterator> { // Iterator for a stream of [`Dynamic`][crate::Dynamic] keys. - keys: KEYS, + keys: K, // Iterator for a stream of [`Dynamic`][crate::Dynamic] values. - values: VALUES, + values: V, } #[cfg(not(feature = "no_object"))] -impl<'a, KEYS, VALUES> IterateMap<'a, KEYS, VALUES> -where - KEYS: Iterator, - VALUES: Iterator, -{ +impl<'a, K: Iterator, V: Iterator> IterateMap<'a, K, V> { #[must_use] - pub fn new(keys: KEYS, values: VALUES) -> Self { + pub fn new(keys: K, values: V) -> Self { Self { keys, values } } } #[cfg(not(feature = "no_object"))] -impl<'a: 'de, 'de, KEYS, VALUES> serde::de::MapAccess<'de> for IterateMap<'a, KEYS, VALUES> -where - KEYS: Iterator, - VALUES: Iterator, +impl<'a: 'de, 'de, K: Iterator, V: Iterator> + serde::de::MapAccess<'de> for IterateMap<'a, K, V> { type Error = RhaiError; - fn next_key_seed>( + fn next_key_seed>( &mut self, - seed: K, - ) -> RhaiResultOf> { + seed: S, + ) -> RhaiResultOf> { // Deserialize each `Identifier` key coming out of the keys iterator. match self.keys.next() { None => Ok(None), @@ -559,10 +549,10 @@ where } } - fn next_value_seed>( + fn next_value_seed>( &mut self, - seed: V, - ) -> RhaiResultOf { + seed: S, + ) -> RhaiResultOf { // Deserialize each value item coming out of the iterator. seed.deserialize(&mut DynamicDeserializer::from_dynamic( self.values.next().unwrap(),