| #![cfg(not(feature = "no_index"))] |
| |
| use crate::api::deprecated::deprecated_array_functions; |
| use crate::engine::OP_EQUALS; |
| use crate::eval::{calc_index, calc_offset_len}; |
| use crate::module::ModuleFlags; |
| use crate::plugin::*; |
| |
| use crate::{ |
| def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext, |
| Position, RhaiResultOf, StaticVec, ERR, INT, MAX_USIZE_INT, |
| }; |
| #[cfg(feature = "no_std")] |
| use std::prelude::v1::*; |
| use std::{any::TypeId, cmp::Ordering, mem}; |
| |
| def_package! { |
| /// Package of basic array utilities. |
| pub BasicArrayPackage(lib) { |
| lib.flags |= ModuleFlags::STANDARD_LIB; |
| |
| combine_with_exported_module!(lib, "array", array_functions); |
| combine_with_exported_module!(lib, "deprecated_array", deprecated_array_functions); |
| |
| // Register array iterator |
| lib.set_iterable::<Array>(); |
| } |
| } |
| |
| #[export_module] |
| pub mod array_functions { |
| /// Number of elements in the array. |
| #[rhai_fn(name = "len", get = "len", pure)] |
| pub fn len(array: &mut Array) -> INT { |
| array.len() as INT |
| } |
| /// Return true if the array is empty. |
| #[rhai_fn(name = "is_empty", get = "is_empty", pure)] |
| pub fn is_empty(array: &mut Array) -> bool { |
| array.len() == 0 |
| } |
| /// Get a copy of the element at the `index` position in the array. |
| /// |
| /// * If `index` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `index` < -length of array, `()` is returned. |
| /// * If `index` ≥ length of array, `()` is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3]; |
| /// |
| /// print(x.get(0)); // prints 1 |
| /// |
| /// print(x.get(-1)); // prints 3 |
| /// |
| /// print(x.get(99)); // prints empty (for '()') |
| /// ``` |
| pub fn get(array: &mut Array, index: INT) -> Dynamic { |
| if array.is_empty() { |
| return Dynamic::UNIT; |
| } |
| |
| let (index, ..) = calc_offset_len(array.len(), index, 0); |
| |
| if index >= array.len() { |
| Dynamic::UNIT |
| } else { |
| array[index].clone() |
| } |
| } |
| /// Set the element at the `index` position in the array to a new `value`. |
| /// |
| /// * If `index` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `index` < -length of array, the array is not modified. |
| /// * If `index` ≥ length of array, the array is not modified. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3]; |
| /// |
| /// x.set(0, 42); |
| /// |
| /// print(x); // prints "[42, 2, 3]" |
| /// |
| /// x.set(-3, 0); |
| /// |
| /// print(x); // prints "[0, 2, 3]" |
| /// |
| /// x.set(99, 123); |
| /// |
| /// print(x); // prints "[0, 2, 3]" |
| /// ``` |
| pub fn set(array: &mut Array, index: INT, value: Dynamic) { |
| if array.is_empty() { |
| return; |
| } |
| |
| let (index, ..) = calc_offset_len(array.len(), index, 0); |
| |
| if index < array.len() { |
| array[index] = value; |
| } |
| } |
| /// Add a new element, which is not another array, to the end of the array. |
| /// |
| /// If `item` is `Array`, then `append` is more specific and will be called instead. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3]; |
| /// |
| /// x.push("hello"); |
| /// |
| /// print(x); // prints [1, 2, 3, "hello"] |
| /// ``` |
| pub fn push(array: &mut Array, item: Dynamic) { |
| array.push(item); |
| } |
| /// Add all the elements of another array to the end of the array. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3]; |
| /// let y = [true, 'x']; |
| /// |
| /// x.append(y); |
| /// |
| /// print(x); // prints "[1, 2, 3, true, 'x']" |
| /// ``` |
| pub fn append(array: &mut Array, new_array: Array) { |
| if !new_array.is_empty() { |
| if array.is_empty() { |
| *array = new_array; |
| } else { |
| array.extend(new_array); |
| } |
| } |
| } |
| /// Combine two arrays into a new array and return it. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3]; |
| /// let y = [true, 'x']; |
| /// |
| /// print(x + y); // prints "[1, 2, 3, true, 'x']" |
| /// |
| /// print(x); // prints "[1, 2, 3" |
| /// ``` |
| #[rhai_fn(name = "+")] |
| pub fn concat(array1: Array, array2: Array) -> Array { |
| if array2.is_empty() { |
| array1 |
| } else if array1.is_empty() { |
| array2 |
| } else { |
| let mut array = array1; |
| array.extend(array2); |
| array |
| } |
| } |
| /// Add a new element into the array at a particular `index` position. |
| /// |
| /// * If `index` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `index` < -length of array, the element is added to the beginning of the array. |
| /// * If `index` ≥ length of array, the element is appended to the end of the array. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3]; |
| /// |
| /// x.insert(0, "hello"); |
| /// |
| /// x.insert(2, true); |
| /// |
| /// x.insert(-2, 42); |
| /// |
| /// print(x); // prints ["hello", 1, true, 2, 42, 3] |
| /// ``` |
| pub fn insert(array: &mut Array, index: INT, item: Dynamic) { |
| if array.is_empty() { |
| array.push(item); |
| return; |
| } |
| |
| let (index, ..) = calc_offset_len(array.len(), index, 0); |
| |
| if index >= array.len() { |
| array.push(item); |
| } else { |
| array.insert(index, item); |
| } |
| } |
| /// Pad the array to at least the specified length with copies of a specified element. |
| /// |
| /// If `len` ≤ length of array, no padding is done. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3]; |
| /// |
| /// x.pad(5, 42); |
| /// |
| /// print(x); // prints "[1, 2, 3, 42, 42]" |
| /// |
| /// x.pad(3, 123); |
| /// |
| /// print(x); // prints "[1, 2, 3, 42, 42]" |
| /// ``` |
| #[rhai_fn(return_raw)] |
| pub fn pad( |
| ctx: NativeCallContext, |
| array: &mut Array, |
| len: INT, |
| item: Dynamic, |
| ) -> RhaiResultOf<()> { |
| if len <= 0 { |
| return Ok(()); |
| } |
| |
| #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] |
| let len = len.min(MAX_USIZE_INT) as usize; |
| |
| if len <= array.len() { |
| return Ok(()); |
| } |
| |
| let _ctx = ctx; |
| |
| // Check if array will be over max size limit |
| #[cfg(not(feature = "unchecked"))] |
| if _ctx.engine().max_array_size() > 0 { |
| let pad = len - array.len(); |
| let (a, m, s) = crate::eval::calc_array_sizes(array); |
| let (ax, mx, sx) = item.calc_data_sizes(true); |
| |
| _ctx.engine() |
| .throw_on_size((a + pad + ax * pad, m + mx * pad, s + sx * pad))?; |
| } |
| |
| array.resize(len, item); |
| |
| Ok(()) |
| } |
| /// Remove the last element from the array and return it. |
| /// |
| /// If the array is empty, `()` is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3]; |
| /// |
| /// print(x.pop()); // prints 3 |
| /// |
| /// print(x); // prints "[1, 2]" |
| /// ``` |
| pub fn pop(array: &mut Array) -> Dynamic { |
| if array.is_empty() { |
| Dynamic::UNIT |
| } else { |
| array.pop().unwrap_or(Dynamic::UNIT) |
| } |
| } |
| /// Remove the first element from the array and return it. |
| /// |
| /// If the array is empty, `()` is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3]; |
| /// |
| /// print(x.shift()); // prints 1 |
| /// |
| /// print(x); // prints "[2, 3]" |
| /// ``` |
| pub fn shift(array: &mut Array) -> Dynamic { |
| if array.is_empty() { |
| Dynamic::UNIT |
| } else { |
| array.remove(0) |
| } |
| } |
| /// Remove the element at the specified `index` from the array and return it. |
| /// |
| /// * If `index` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `index` < -length of array, `()` is returned. |
| /// * If `index` ≥ length of array, `()` is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3]; |
| /// |
| /// print(x.remove(1)); // prints 2 |
| /// |
| /// print(x); // prints "[1, 3]" |
| /// |
| /// print(x.remove(-2)); // prints 1 |
| /// |
| /// print(x); // prints "[3]" |
| /// ``` |
| pub fn remove(array: &mut Array, index: INT) -> Dynamic { |
| let index = match calc_index(array.len(), index, true, || Err(())) { |
| Ok(n) => n, |
| Err(_) => return Dynamic::UNIT, |
| }; |
| |
| array.remove(index) |
| } |
| /// Clear the array. |
| pub fn clear(array: &mut Array) { |
| if !array.is_empty() { |
| array.clear(); |
| } |
| } |
| /// Cut off the array at the specified length. |
| /// |
| /// * If `len` ≤ 0, the array is cleared. |
| /// * If `len` ≥ length of array, the array is not truncated. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// x.truncate(3); |
| /// |
| /// print(x); // prints "[1, 2, 3]" |
| /// |
| /// x.truncate(10); |
| /// |
| /// print(x); // prints "[1, 2, 3]" |
| /// ``` |
| pub fn truncate(array: &mut Array, len: INT) { |
| if len <= 0 { |
| array.clear(); |
| return; |
| } |
| if !array.is_empty() { |
| #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] |
| let len = len.min(MAX_USIZE_INT) as usize; |
| |
| if len > 0 { |
| array.truncate(len); |
| } else { |
| array.clear(); |
| } |
| } |
| } |
| /// Cut off the head of the array, leaving a tail of the specified length. |
| /// |
| /// * If `len` ≤ 0, the array is cleared. |
| /// * If `len` ≥ length of array, the array is not modified. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// x.chop(3); |
| /// |
| /// print(x); // prints "[3, 4, 5]" |
| /// |
| /// x.chop(10); |
| /// |
| /// print(x); // prints "[3, 4, 5]" |
| /// ``` |
| pub fn chop(array: &mut Array, len: INT) { |
| if len <= 0 { |
| array.clear(); |
| return; |
| } |
| if !array.is_empty() { |
| #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] |
| let len = len.min(MAX_USIZE_INT) as usize; |
| |
| if len <= 0 { |
| array.clear(); |
| } else if len < array.len() { |
| array.drain(0..array.len() - len); |
| } |
| } |
| } |
| /// Reverse all the elements in the array. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// x.reverse(); |
| /// |
| /// print(x); // prints "[5, 4, 3, 2, 1]" |
| /// ``` |
| pub fn reverse(array: &mut Array) { |
| if !array.is_empty() { |
| array.reverse(); |
| } |
| } |
| /// Replace an exclusive range of the array with another array. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// let y = [7, 8, 9, 10]; |
| /// |
| /// x.splice(1..3, y); |
| /// |
| /// print(x); // prints "[1, 7, 8, 9, 10, 4, 5]" |
| /// ``` |
| #[rhai_fn(name = "splice")] |
| pub fn splice_range(array: &mut Array, range: ExclusiveRange, replace: Array) { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| splice(array, start, end - start, replace); |
| } |
| /// Replace an inclusive range of the array with another array. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// let y = [7, 8, 9, 10]; |
| /// |
| /// x.splice(1..=3, y); |
| /// |
| /// print(x); // prints "[1, 7, 8, 9, 10, 5]" |
| /// ``` |
| #[rhai_fn(name = "splice")] |
| pub fn splice_inclusive_range(array: &mut Array, range: InclusiveRange, replace: Array) { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| splice(array, start, end - start + 1, replace); |
| } |
| /// Replace a portion of the array with another array. |
| /// |
| /// * If `start` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `start` < -length of array, position counts from the beginning of the array. |
| /// * If `start` ≥ length of array, the other array is appended to the end of the array. |
| /// * If `len` ≤ 0, the other array is inserted into the array at the `start` position without replacing any element. |
| /// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is replaced. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// let y = [7, 8, 9, 10]; |
| /// |
| /// x.splice(1, 2, y); |
| /// |
| /// print(x); // prints "[1, 7, 8, 9, 10, 4, 5]" |
| /// |
| /// x.splice(-5, 4, y); |
| /// |
| /// print(x); // prints "[1, 7, 7, 8, 9, 10, 5]" |
| /// ``` |
| pub fn splice(array: &mut Array, start: INT, len: INT, replace: Array) { |
| if array.is_empty() { |
| *array = replace; |
| return; |
| } |
| |
| let (start, len) = calc_offset_len(array.len(), start, len); |
| |
| if start >= array.len() { |
| array.extend(replace); |
| } else { |
| array.splice(start..start + len, replace); |
| } |
| } |
| /// Copy an exclusive range of the array and return it as a new array. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// print(x.extract(1..3)); // prints "[2, 3]" |
| /// |
| /// print(x); // prints "[1, 2, 3, 4, 5]" |
| /// ``` |
| #[rhai_fn(name = "extract")] |
| pub fn extract_range(array: &mut Array, range: ExclusiveRange) -> Array { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| extract(array, start, end - start) |
| } |
| /// Copy an inclusive range of the array and return it as a new array. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// print(x.extract(1..=3)); // prints "[2, 3, 4]" |
| /// |
| /// print(x); // prints "[1, 2, 3, 4, 5]" |
| /// ``` |
| #[rhai_fn(name = "extract")] |
| pub fn extract_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| extract(array, start, end - start + 1) |
| } |
| /// Copy a portion of the array and return it as a new array. |
| /// |
| /// * If `start` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `start` < -length of array, position counts from the beginning of the array. |
| /// * If `start` ≥ length of array, an empty array is returned. |
| /// * If `len` ≤ 0, an empty array is returned. |
| /// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is copied and returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// print(x.extract(1, 3)); // prints "[2, 3, 4]" |
| /// |
| /// print(x.extract(-3, 2)); // prints "[3, 4]" |
| /// |
| /// print(x); // prints "[1, 2, 3, 4, 5]" |
| /// ``` |
| pub fn extract(array: &mut Array, start: INT, len: INT) -> Array { |
| if array.is_empty() || len <= 0 { |
| return Array::new(); |
| } |
| |
| let (start, len) = calc_offset_len(array.len(), start, len); |
| |
| if len == 0 { |
| Array::new() |
| } else { |
| array[start..start + len].to_vec() |
| } |
| } |
| /// Copy a portion of the array beginning at the `start` position till the end and return it as |
| /// a new array. |
| /// |
| /// * If `start` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `start` < -length of array, the entire array is copied and returned. |
| /// * If `start` ≥ length of array, an empty array is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// print(x.extract(2)); // prints "[3, 4, 5]" |
| /// |
| /// print(x.extract(-3)); // prints "[3, 4, 5]" |
| /// |
| /// print(x); // prints "[1, 2, 3, 4, 5]" |
| /// ``` |
| #[rhai_fn(name = "extract")] |
| pub fn extract_tail(array: &mut Array, start: INT) -> Array { |
| extract(array, start, INT::MAX) |
| } |
| /// Cut off the array at `index` and return it as a new array. |
| /// |
| /// * If `index` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `index` is zero, the entire array is cut and returned. |
| /// * If `index` < -length of array, the entire array is cut and returned. |
| /// * If `index` ≥ length of array, nothing is cut from the array and an empty array is returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.split(2); |
| /// |
| /// print(y); // prints "[3, 4, 5]" |
| /// |
| /// print(x); // prints "[1, 2]" |
| /// ``` |
| #[rhai_fn(name = "split")] |
| pub fn split_at(array: &mut Array, index: INT) -> Array { |
| if array.is_empty() { |
| return Array::new(); |
| } |
| |
| let (start, len) = calc_offset_len(array.len(), index, INT::MAX); |
| |
| if start == 0 { |
| if len >= array.len() { |
| mem::take(array) |
| } else { |
| let mut result = Array::new(); |
| result.extend(array.drain(array.len() - len..)); |
| result |
| } |
| } else if start >= array.len() { |
| Array::new() |
| } else { |
| let mut result = Array::new(); |
| result.extend(array.drain(start as usize..)); |
| result |
| } |
| } |
| |
| /// Iterate through all the elements in the array, applying a `process` function to each element in turn. |
| /// Each element is bound to `this` before calling the function. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `this`: bound to array element (mutable) |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// x.for_each(|| this *= this); |
| /// |
| /// print(x); // prints "[1, 4, 9, 16, 25]" |
| /// |
| /// x.for_each(|i| this *= i); |
| /// |
| /// print(x); // prints "[0, 2, 6, 12, 20]" |
| /// ``` |
| #[rhai_fn(return_raw)] |
| pub fn for_each(ctx: NativeCallContext, array: &mut Array, map: FnPtr) -> RhaiResultOf<()> { |
| if array.is_empty() { |
| return Ok(()); |
| } |
| |
| for (i, item) in array.iter_mut().enumerate() { |
| let ex = [(i as INT).into()]; |
| |
| let _ = map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex, None)?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Iterate through all the elements in the array, applying a `mapper` function to each element |
| /// in turn, and return the results as a new array. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// This method is marked _pure_; the `mapper` function should not mutate array elements. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.map(|v| v * v); |
| /// |
| /// print(y); // prints "[1, 4, 9, 16, 25]" |
| /// |
| /// let y = x.map(|v, i| v * i); |
| /// |
| /// print(y); // prints "[0, 2, 6, 12, 20]" |
| /// ``` |
| #[rhai_fn(return_raw, pure)] |
| pub fn map(ctx: NativeCallContext, array: &mut Array, map: FnPtr) -> RhaiResultOf<Array> { |
| if array.is_empty() { |
| return Ok(Array::new()); |
| } |
| |
| let mut ar = Array::with_capacity(array.len()); |
| |
| for (i, item) in array.iter_mut().enumerate() { |
| let ex = [(i as INT).into()]; |
| ar.push(map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex, Some(0))?); |
| } |
| |
| Ok(ar) |
| } |
| |
| /// Iterate through all the elements in the array, applying a `filter` function to each element |
| /// in turn, and return a copy of all elements (in order) that return `true` as a new array. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// This method is marked _pure_; the `filter` function should not mutate array elements. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.filter(|v| v >= 3); |
| /// |
| /// print(y); // prints "[3, 4, 5]" |
| /// |
| /// let y = x.filter(|v, i| v * i >= 10); |
| /// |
| /// print(y); // prints "[12, 20]" |
| /// ``` |
| #[rhai_fn(return_raw, pure)] |
| pub fn filter(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> { |
| if array.is_empty() { |
| return Ok(Array::new()); |
| } |
| |
| let mut ar = Array::new(); |
| |
| for (i, item) in array.iter_mut().enumerate() { |
| let ex = [(i as INT).into()]; |
| |
| if filter |
| .call_raw_with_extra_args("filter", &ctx, Some(item), [], ex, Some(0))? |
| .as_bool() |
| .unwrap_or(false) |
| { |
| ar.push(item.clone()); |
| } |
| } |
| |
| Ok(ar) |
| } |
| /// Return `true` if the array contains an element that equals `value`. |
| /// |
| /// The operator `==` is used to compare elements with `value` and must be defined, |
| /// otherwise `false` is assumed. |
| /// |
| /// This function also drives the `in` operator. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// // The 'in' operator calls 'contains' in the background |
| /// if 4 in x { |
| /// print("found!"); |
| /// } |
| /// ``` |
| #[rhai_fn(return_raw, pure)] |
| pub fn contains( |
| ctx: NativeCallContext, |
| array: &mut Array, |
| value: Dynamic, |
| ) -> RhaiResultOf<bool> { |
| if array.is_empty() { |
| return Ok(false); |
| } |
| |
| for item in array { |
| if ctx |
| .call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()]) |
| .or_else(|err| match *err { |
| ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => { |
| if item.type_id() == value.type_id() { |
| // No default when comparing same type |
| Err(err) |
| } else { |
| Ok(Dynamic::FALSE) |
| } |
| } |
| _ => Err(err), |
| })? |
| .as_bool() |
| .unwrap_or(false) |
| { |
| return Ok(true); |
| } |
| } |
| |
| Ok(false) |
| } |
| /// Find the first element in the array that equals a particular `value` and return its index. |
| /// If no element equals `value`, `-1` is returned. |
| /// |
| /// The operator `==` is used to compare elements with `value` and must be defined, |
| /// otherwise `false` is assumed. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5]; |
| /// |
| /// print(x.index_of(4)); // prints 3 (first index) |
| /// |
| /// print(x.index_of(9)); // prints -1 |
| /// |
| /// print(x.index_of("foo")); // prints -1: strings do not equal numbers |
| /// ``` |
| #[rhai_fn(return_raw, pure)] |
| pub fn index_of( |
| ctx: NativeCallContext, |
| array: &mut Array, |
| value: Dynamic, |
| ) -> RhaiResultOf<INT> { |
| if array.is_empty() { |
| Ok(-1) |
| } else { |
| index_of_starting_from(ctx, array, value, 0) |
| } |
| } |
| /// Find the first element in the array, starting from a particular `start` position, that |
| /// equals a particular `value` and return its index. If no element equals `value`, `-1` is returned. |
| /// |
| /// * If `start` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `start` < -length of array, position counts from the beginning of the array. |
| /// * If `start` ≥ length of array, `-1` is returned. |
| /// |
| /// The operator `==` is used to compare elements with `value` and must be defined, |
| /// otherwise `false` is assumed. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5]; |
| /// |
| /// print(x.index_of(4, 2)); // prints 3 |
| /// |
| /// print(x.index_of(4, 5)); // prints 7 |
| /// |
| /// print(x.index_of(4, 15)); // prints -1: nothing found past end of array |
| /// |
| /// print(x.index_of(4, -5)); // prints 11: -5 = start from index 8 |
| /// |
| /// print(x.index_of(9, 1)); // prints -1: nothing equals 9 |
| /// |
| /// print(x.index_of("foo", 1)); // prints -1: strings do not equal numbers |
| /// ``` |
| #[rhai_fn(name = "index_of", return_raw, pure)] |
| pub fn index_of_starting_from( |
| ctx: NativeCallContext, |
| array: &mut Array, |
| value: Dynamic, |
| start: INT, |
| ) -> RhaiResultOf<INT> { |
| if array.is_empty() { |
| return Ok(-1); |
| } |
| |
| let (start, ..) = calc_offset_len(array.len(), start, 0); |
| |
| for (i, item) in array.iter_mut().enumerate().skip(start) { |
| if ctx |
| .call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()]) |
| .or_else(|err| match *err { |
| ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => { |
| if item.type_id() == value.type_id() { |
| // No default when comparing same type |
| Err(err) |
| } else { |
| Ok(Dynamic::FALSE) |
| } |
| } |
| _ => Err(err), |
| })? |
| .as_bool() |
| .unwrap_or(false) |
| { |
| return Ok(i as INT); |
| } |
| } |
| |
| Ok(-1 as INT) |
| } |
| /// Iterate through all the elements in the array, applying a `filter` function to each element |
| /// in turn, and return the index of the first element that returns `true`. |
| /// If no element returns `true`, `-1` is returned. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// This method is marked _pure_; the `filter` function should not mutate array elements. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5]; |
| /// |
| /// print(x.index_of(|v| v > 3)); // prints 3: 4 > 3 |
| /// |
| /// print(x.index_of(|v| v > 8)); // prints -1: nothing is > 8 |
| /// |
| /// print(x.index_of(|v, i| v * i > 20)); // prints 7: 4 * 7 > 20 |
| /// ``` |
| #[rhai_fn(name = "index_of", return_raw, pure)] |
| pub fn index_of_filter( |
| ctx: NativeCallContext, |
| array: &mut Array, |
| filter: FnPtr, |
| ) -> RhaiResultOf<INT> { |
| if array.is_empty() { |
| Ok(-1) |
| } else { |
| index_of_filter_starting_from(ctx, array, filter, 0) |
| } |
| } |
| /// Iterate through all the elements in the array, starting from a particular `start` position, |
| /// applying a `filter` function to each element in turn, and return the index of the first |
| /// element that returns `true`. If no element returns `true`, `-1` is returned. |
| /// |
| /// * If `start` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `start` < -length of array, position counts from the beginning of the array. |
| /// * If `start` ≥ length of array, `-1` is returned. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// This method is marked _pure_; the `filter` function should not mutate array elements. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5]; |
| /// |
| /// print(x.index_of(|v| v > 1, 3)); // prints 5: 2 > 1 |
| /// |
| /// print(x.index_of(|v| v < 2, 9)); // prints -1: nothing < 2 past index 9 |
| /// |
| /// print(x.index_of(|v| v > 1, 15)); // prints -1: nothing found past end of array |
| /// |
| /// print(x.index_of(|v| v > 1, -5)); // prints 9: -5 = start from index 8 |
| /// |
| /// print(x.index_of(|v| v > 1, -99)); // prints 1: -99 = start from beginning |
| /// |
| /// print(x.index_of(|v, i| v * i > 20, 8)); // prints 10: 3 * 10 > 20 |
| /// ``` |
| #[rhai_fn(name = "index_of", return_raw, pure)] |
| pub fn index_of_filter_starting_from( |
| ctx: NativeCallContext, |
| array: &mut Array, |
| filter: FnPtr, |
| start: INT, |
| ) -> RhaiResultOf<INT> { |
| if array.is_empty() { |
| return Ok(-1); |
| } |
| |
| let (start, ..) = calc_offset_len(array.len(), start, 0); |
| |
| for (i, item) in array.iter_mut().enumerate().skip(start) { |
| let ex = [(i as INT).into()]; |
| |
| if filter |
| .call_raw_with_extra_args("index_of", &ctx, Some(item), [], ex, Some(0))? |
| .as_bool() |
| .unwrap_or(false) |
| { |
| return Ok(i as INT); |
| } |
| } |
| |
| Ok(-1 as INT) |
| } |
| /// Iterate through all the elements in the array, applying a `filter` function to each element |
| /// in turn, and return a copy of the first element that returns `true`. If no element returns |
| /// `true`, `()` is returned. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 5, 8, 13]; |
| /// |
| /// print(x.find(|v| v > 3)); // prints 5: 5 > 3 |
| /// |
| /// print(x.find(|v| v > 13) ?? "not found"); // prints "not found": nothing is > 13 |
| /// |
| /// print(x.find(|v, i| v * i > 13)); // prints 5: 3 * 5 > 13 |
| /// ``` |
| #[rhai_fn(return_raw, pure)] |
| pub fn find(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResult { |
| find_starting_from(ctx, array, filter, 0) |
| } |
| /// Iterate through all the elements in the array, starting from a particular `start` position, |
| /// applying a `filter` function to each element in turn, and return a copy of the first element |
| /// that returns `true`. If no element returns `true`, `()` is returned. |
| /// |
| /// * If `start` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `start` < -length of array, position counts from the beginning of the array. |
| /// * If `start` ≥ length of array, `-1` is returned. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// This method is marked _pure_; the `filter` function should not mutate array elements. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 5, 8, 13]; |
| /// |
| /// print(x.find(|v| v > 1, 2)); // prints 3: 3 > 1 |
| /// |
| /// print(x.find(|v| v < 2, 3) ?? "not found"); // prints "not found": nothing < 2 past index 3 |
| /// |
| /// print(x.find(|v| v > 1, 8) ?? "not found"); // prints "not found": nothing found past end of array |
| /// |
| /// print(x.find(|v| v > 1, -3)); // prints 5: -3 = start from index 4 |
| /// |
| /// print(x.find(|v| v > 0, -99)); // prints 1: -99 = start from beginning |
| /// |
| /// print(x.find(|v, i| v * i > 6, 3)); // prints 5: 5 * 4 > 6 |
| /// ``` |
| #[rhai_fn(name = "find", return_raw, pure)] |
| pub fn find_starting_from( |
| ctx: NativeCallContext, |
| array: &mut Array, |
| filter: FnPtr, |
| start: INT, |
| ) -> RhaiResult { |
| let index = index_of_filter_starting_from(ctx, array, filter, start)?; |
| |
| if index < 0 { |
| return Ok(Dynamic::UNIT); |
| } |
| |
| Ok(get(array, index)) |
| } |
| /// Iterate through all the elements in the array, applying a `mapper` function to each element |
| /// in turn, and return the first result that is not `()`. Otherwise, `()` is returned. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// This method is marked _pure_; the `mapper` function should not mutate array elements. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [#{alice: 1}, #{bob: 2}, #{clara: 3}]; |
| /// |
| /// print(x.find_map(|v| v.alice)); // prints 1 |
| /// |
| /// print(x.find_map(|v| v.dave) ?? "not found"); // prints "not found" |
| /// |
| /// print(x.find_map(|| this.dave) ?? "not found"); // prints "not found" |
| /// ``` |
| #[rhai_fn(return_raw, pure)] |
| pub fn find_map(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResult { |
| find_map_starting_from(ctx, array, filter, 0) |
| } |
| /// Iterate through all the elements in the array, starting from a particular `start` position, |
| /// applying a `mapper` function to each element in turn, and return the first result that is not `()`. |
| /// Otherwise, `()` is returned. |
| /// |
| /// * If `start` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `start` < -length of array, position counts from the beginning of the array. |
| /// * If `start` ≥ length of array, `-1` is returned. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// This method is marked _pure_; the `mapper` function should not mutate array elements. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [#{alice: 1}, #{bob: 2}, #{bob: 3}, #{clara: 3}, #{alice: 0}, #{clara: 5}]; |
| /// |
| /// print(x.find_map(|v| v.alice, 2)); // prints 0 |
| /// |
| /// print(x.find_map(|v| v.bob, 4) ?? "not found"); // prints "not found" |
| /// |
| /// print(x.find_map(|v| v.alice, 8) ?? "not found"); // prints "not found" |
| /// |
| /// print(x.find_map(|| this.alice, 8) ?? "not found"); // prints "not found" |
| /// |
| /// print(x.find_map(|v| v.bob, -4)); // prints 3: -4 = start from index 2 |
| /// |
| /// print(x.find_map(|v| v.alice, -99)); // prints 1: -99 = start from beginning |
| /// |
| /// print(x.find_map(|| this.alice, -99)); // prints 1: -99 = start from beginning |
| /// ``` |
| #[rhai_fn(name = "find_map", return_raw, pure)] |
| pub fn find_map_starting_from( |
| ctx: NativeCallContext, |
| array: &mut Array, |
| filter: FnPtr, |
| start: INT, |
| ) -> RhaiResult { |
| if array.is_empty() { |
| return Ok(Dynamic::UNIT); |
| } |
| |
| let (start, ..) = calc_offset_len(array.len(), start, 0); |
| |
| for (i, item) in array.iter_mut().enumerate().skip(start) { |
| let ex = [(i as INT).into()]; |
| |
| let value = |
| filter.call_raw_with_extra_args("find_map", &ctx, Some(item), [], ex, Some(0))?; |
| |
| if !value.is_unit() { |
| return Ok(value); |
| } |
| } |
| |
| Ok(Dynamic::UNIT) |
| } |
| /// Return `true` if any element in the array that returns `true` when applied the `filter` function. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// This method is marked _pure_; the `filter` function should not mutate array elements. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5]; |
| /// |
| /// print(x.some(|v| v > 3)); // prints true |
| /// |
| /// print(x.some(|v| v > 10)); // prints false |
| /// |
| /// print(x.some(|v, i| i > v)); // prints true |
| /// ``` |
| #[rhai_fn(return_raw, pure)] |
| pub fn some(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<bool> { |
| if array.is_empty() { |
| return Ok(false); |
| } |
| |
| for (i, item) in array.iter_mut().enumerate() { |
| let ex = [(i as INT).into()]; |
| |
| if filter |
| .call_raw_with_extra_args("some", &ctx, Some(item), [], ex, Some(0))? |
| .as_bool() |
| .unwrap_or(false) |
| { |
| return Ok(true); |
| } |
| } |
| |
| Ok(false) |
| } |
| /// Return `true` if all elements in the array return `true` when applied the `filter` function. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// This method is marked _pure_; the `filter` function should not mutate array elements. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5]; |
| /// |
| /// print(x.all(|v| v > 3)); // prints false |
| /// |
| /// print(x.all(|v| v > 1)); // prints true |
| /// |
| /// print(x.all(|v, i| i > v)); // prints false |
| /// ``` |
| #[rhai_fn(return_raw, pure)] |
| pub fn all(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<bool> { |
| if array.is_empty() { |
| return Ok(true); |
| } |
| |
| for (i, item) in array.iter_mut().enumerate() { |
| let ex = [(i as INT).into()]; |
| |
| if !filter |
| .call_raw_with_extra_args("all", &ctx, Some(item), [], ex, Some(0))? |
| .as_bool() |
| .unwrap_or(false) |
| { |
| return Ok(false); |
| } |
| } |
| |
| Ok(true) |
| } |
| /// Remove duplicated _consecutive_ elements from the array. |
| /// |
| /// The operator `==` is used to compare elements and must be defined, |
| /// otherwise `false` is assumed. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 2, 2, 3, 4, 3, 3, 2, 1]; |
| /// |
| /// x.dedup(); |
| /// |
| /// print(x); // prints "[1, 2, 3, 4, 3, 2, 1]" |
| /// ``` |
| pub fn dedup(ctx: NativeCallContext, array: &mut Array) { |
| let comparer = FnPtr::new_unchecked(OP_EQUALS, StaticVec::new_const()); |
| dedup_by_comparer(ctx, array, comparer); |
| } |
| /// Remove duplicated _consecutive_ elements from the array that return `true` when applied the |
| /// `comparer` function. |
| /// |
| /// No element is removed if the correct `comparer` function does not exist. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element1`: copy of the current array element to compare |
| /// * `element2`: copy of the next array element to compare |
| /// |
| /// ## Return Value |
| /// |
| /// `true` if `element1 == element2`, otherwise `false`. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 2, 2, 3, 1, 2, 3, 4, 3, 3, 2, 1]; |
| /// |
| /// x.dedup(|a, b| a >= b); |
| /// |
| /// print(x); // prints "[1, 2, 3, 4]" |
| /// ``` |
| #[rhai_fn(name = "dedup")] |
| pub fn dedup_by_comparer(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) { |
| if array.is_empty() { |
| return; |
| } |
| |
| array.dedup_by(|x, y| { |
| comparer |
| .call_raw(&ctx, None, [y.clone(), x.clone()]) |
| .unwrap_or(Dynamic::FALSE) |
| .as_bool() |
| .unwrap_or(false) |
| }); |
| } |
| /// Reduce an array by iterating through all elements while applying the `reducer` function. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `result`: accumulated result, initially `()` |
| /// * `element`: copy of array element, or bound to `this` if omitted |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// This method is marked _pure_; the `reducer` function should not mutate array elements. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.reduce(|r, v| v + (r ?? 0)); |
| /// |
| /// print(y); // prints 15 |
| /// |
| /// let y = x.reduce(|r, v, i| v + i + (r ?? 0)); |
| /// |
| /// print(y); // prints 25 |
| /// ``` |
| #[rhai_fn(return_raw, pure)] |
| pub fn reduce(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult { |
| reduce_with_initial(ctx, array, reducer, Dynamic::UNIT) |
| } |
| /// Reduce an array by iterating through all elements while applying the `reducer` function. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `result`: accumulated result, starting with the value of `initial` |
| /// * `element`: copy of array element, or bound to `this` if omitted |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// This method is marked _pure_; the `reducer` function should not mutate array elements. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.reduce(|r, v| v + r, 5); |
| /// |
| /// print(y); // prints 20 |
| /// |
| /// let y = x.reduce(|r, v, i| v + i + r, 5); |
| /// |
| /// print(y); // prints 30 |
| /// ``` |
| #[rhai_fn(name = "reduce", return_raw, pure)] |
| pub fn reduce_with_initial( |
| ctx: NativeCallContext, |
| array: &mut Array, |
| reducer: FnPtr, |
| initial: Dynamic, |
| ) -> RhaiResult { |
| if array.is_empty() { |
| return Ok(initial); |
| } |
| |
| array |
| .iter_mut() |
| .enumerate() |
| .try_fold(initial, |result, (i, item)| { |
| let ex = [(i as INT).into()]; |
| reducer.call_raw_with_extra_args("reduce", &ctx, Some(item), [result], ex, Some(1)) |
| }) |
| } |
| /// Reduce an array by iterating through all elements, in _reverse_ order, |
| /// while applying the `reducer` function. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `result`: accumulated result, initially `()` |
| /// * `element`: copy of array element, or bound to `this` if omitted |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// This method is marked _pure_; the `reducer` function should not mutate array elements. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.reduce_rev(|r, v| v + (r ?? 0)); |
| /// |
| /// print(y); // prints 15 |
| /// |
| /// let y = x.reduce_rev(|r, v, i| v + i + (r ?? 0)); |
| /// |
| /// print(y); // prints 25 |
| /// ``` |
| #[rhai_fn(return_raw, pure)] |
| pub fn reduce_rev(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult { |
| reduce_rev_with_initial(ctx, array, reducer, Dynamic::UNIT) |
| } |
| /// Reduce an array by iterating through all elements, in _reverse_ order, |
| /// while applying the `reducer` function. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `result`: accumulated result, starting with the value of `initial` |
| /// * `element`: copy of array element, or bound to `this` if omitted |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// This method is marked _pure_; the `reducer` function should not mutate array elements. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.reduce_rev(|r, v| v + r, 5); |
| /// |
| /// print(y); // prints 20 |
| /// |
| /// let y = x.reduce_rev(|r, v, i| v + i + r, 5); |
| /// |
| /// print(y); // prints 30 |
| /// ``` |
| #[rhai_fn(name = "reduce_rev", return_raw, pure)] |
| pub fn reduce_rev_with_initial( |
| ctx: NativeCallContext, |
| array: &mut Array, |
| reducer: FnPtr, |
| initial: Dynamic, |
| ) -> RhaiResult { |
| if array.is_empty() { |
| return Ok(initial); |
| } |
| |
| let len = array.len(); |
| |
| array |
| .iter_mut() |
| .rev() |
| .enumerate() |
| .try_fold(initial, |result, (i, item)| { |
| let ex = [((len - 1 - i) as INT).into()]; |
| |
| reducer.call_raw_with_extra_args( |
| "reduce_rev", |
| &ctx, |
| Some(item), |
| [result], |
| ex, |
| Some(1), |
| ) |
| }) |
| } |
| /// Sort the array based on applying the `comparer` function. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element1`: copy of the current array element to compare |
| /// * `element2`: copy of the next array element to compare |
| /// |
| /// ## Return Value |
| /// |
| /// * Any integer > 0 if `element1 > element2` |
| /// * Zero if `element1 == element2` |
| /// * Any integer < 0 if `element1 < element2` |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]; |
| /// |
| /// // Do comparisons in reverse |
| /// x.sort(|a, b| if a > b { -1 } else if a < b { 1 } else { 0 }); |
| /// |
| /// print(x); // prints "[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]" |
| /// ``` |
| pub fn sort(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) { |
| if array.len() <= 1 { |
| return; |
| } |
| |
| array.sort_by(|x, y| { |
| comparer |
| .call_raw(&ctx, None, [x.clone(), y.clone()]) |
| .ok() |
| .and_then(|v| v.as_int().ok()) |
| .map_or_else( |
| || x.type_id().cmp(&y.type_id()), |
| |v| match v { |
| v if v > 0 => Ordering::Greater, |
| v if v < 0 => Ordering::Less, |
| 0 => Ordering::Equal, |
| _ => unreachable!("v is {}", v), |
| }, |
| ) |
| }); |
| } |
| /// Sort the array. |
| /// |
| /// All elements in the array must be of the same data type. |
| /// |
| /// # Supported Data Types |
| /// |
| /// * integer numbers |
| /// * floating-point numbers |
| /// * decimal numbers |
| /// * characters |
| /// * strings |
| /// * booleans |
| /// * `()` |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]; |
| /// |
| /// x.sort(); |
| /// |
| /// print(x); // prints "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" |
| /// ``` |
| #[rhai_fn(name = "sort", return_raw)] |
| pub fn sort_with_builtin(array: &mut Array) -> RhaiResultOf<()> { |
| if array.len() <= 1 { |
| return Ok(()); |
| } |
| |
| let type_id = array[0].type_id(); |
| |
| if array.iter().any(|a| a.type_id() != type_id) { |
| return Err(ERR::ErrorFunctionNotFound( |
| "sort() cannot be called with elements of different types".into(), |
| Position::NONE, |
| ) |
| .into()); |
| } |
| |
| if type_id == TypeId::of::<INT>() { |
| array.sort_by(|a, b| { |
| let a = a.as_int().expect("`INT`"); |
| let b = b.as_int().expect("`INT`"); |
| a.cmp(&b) |
| }); |
| return Ok(()); |
| } |
| if type_id == TypeId::of::<char>() { |
| array.sort_by(|a, b| { |
| let a = a.as_char().expect("char"); |
| let b = b.as_char().expect("char"); |
| a.cmp(&b) |
| }); |
| return Ok(()); |
| } |
| #[cfg(not(feature = "no_float"))] |
| if type_id == TypeId::of::<crate::FLOAT>() { |
| array.sort_by(|a, b| { |
| let a = a.as_float().expect("`FLOAT`"); |
| let b = b.as_float().expect("`FLOAT`"); |
| a.partial_cmp(&b).unwrap_or(Ordering::Equal) |
| }); |
| return Ok(()); |
| } |
| if type_id == TypeId::of::<ImmutableString>() { |
| array.sort_by(|a, b| { |
| let a = a.read_lock::<ImmutableString>().expect("`ImmutableString`"); |
| let b = b.read_lock::<ImmutableString>().expect("`ImmutableString`"); |
| a.as_str().cmp(b.as_str()) |
| }); |
| return Ok(()); |
| } |
| #[cfg(feature = "decimal")] |
| if type_id == TypeId::of::<rust_decimal::Decimal>() { |
| array.sort_by(|a, b| { |
| let a = a.as_decimal().expect("`Decimal`"); |
| let b = b.as_decimal().expect("`Decimal`"); |
| a.cmp(&b) |
| }); |
| return Ok(()); |
| } |
| if type_id == TypeId::of::<bool>() { |
| array.sort_by(|a, b| { |
| let a = a.as_bool().expect("`bool`"); |
| let b = b.as_bool().expect("`bool`"); |
| a.cmp(&b) |
| }); |
| return Ok(()); |
| } |
| if type_id == TypeId::of::<()>() { |
| return Ok(()); |
| } |
| |
| Ok(()) |
| } |
| /// Remove all elements in the array that returns `true` when applied the `filter` function and |
| /// return them as a new array. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.drain(|v| v < 3); |
| /// |
| /// print(x); // prints "[3, 4, 5]" |
| /// |
| /// print(y); // prints "[1, 2]" |
| /// |
| /// let z = x.drain(|v, i| v + i > 5); |
| /// |
| /// print(x); // prints "[3, 4]" |
| /// |
| /// print(z); // prints "[5]" |
| /// ``` |
| #[rhai_fn(return_raw)] |
| pub fn drain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> { |
| if array.is_empty() { |
| return Ok(Array::new()); |
| } |
| |
| let mut drained = Array::with_capacity(array.len()); |
| |
| let mut i = 0; |
| let mut x = 0; |
| |
| while x < array.len() { |
| let ex = [(i as INT).into()]; |
| |
| if filter |
| .call_raw_with_extra_args("drain", &ctx, Some(&mut array[x]), [], ex, Some(0))? |
| .as_bool() |
| .unwrap_or(false) |
| { |
| drained.push(array.remove(x)); |
| } else { |
| x += 1; |
| } |
| |
| i += 1; |
| } |
| |
| Ok(drained) |
| } |
| /// Remove all elements in the array within an exclusive `range` and return them as a new array. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.drain(1..3); |
| /// |
| /// print(x); // prints "[1, 4, 5]" |
| /// |
| /// print(y); // prints "[2, 3]" |
| /// |
| /// let z = x.drain(2..3); |
| /// |
| /// print(x); // prints "[1, 4]" |
| /// |
| /// print(z); // prints "[5]" |
| /// ``` |
| #[rhai_fn(name = "drain")] |
| pub fn drain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| drain_range(array, start, end - start) |
| } |
| /// Remove all elements in the array within an inclusive `range` and return them as a new array. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.drain(1..=2); |
| /// |
| /// print(x); // prints "[1, 4, 5]" |
| /// |
| /// print(y); // prints "[2, 3]" |
| /// |
| /// let z = x.drain(2..=2); |
| /// |
| /// print(x); // prints "[1, 4]" |
| /// |
| /// print(z); // prints "[5]" |
| /// ``` |
| #[rhai_fn(name = "drain")] |
| pub fn drain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| drain_range(array, start, end - start + 1) |
| } |
| /// Remove all elements within a portion of the array and return them as a new array. |
| /// |
| /// * If `start` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `start` < -length of array, position counts from the beginning of the array. |
| /// * If `start` ≥ length of array, no element is removed and an empty array is returned. |
| /// * If `len` ≤ 0, no element is removed and an empty array is returned. |
| /// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is removed and returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.drain(1, 2); |
| /// |
| /// print(x); // prints "[1, 4, 5]" |
| /// |
| /// print(y); // prints "[2, 3]" |
| /// |
| /// let z = x.drain(-1, 1); |
| /// |
| /// print(x); // prints "[1, 4]" |
| /// |
| /// print(z); // prints "[5]" |
| /// ``` |
| #[rhai_fn(name = "drain")] |
| pub fn drain_range(array: &mut Array, start: INT, len: INT) -> Array { |
| if array.is_empty() || len <= 0 { |
| return Array::new(); |
| } |
| |
| let (start, len) = calc_offset_len(array.len(), start, len); |
| |
| if len == 0 { |
| Array::new() |
| } else { |
| array.drain(start..start + len).collect() |
| } |
| } |
| /// Remove all elements in the array that do not return `true` when applied the `filter` |
| /// function and return them as a new array. |
| /// |
| /// # No Function Parameter |
| /// |
| /// Array element (mutable) is bound to `this`. |
| /// |
| /// # Function Parameters |
| /// |
| /// * `element`: copy of array element |
| /// * `index` _(optional)_: current index in the array |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.retain(|v| v >= 3); |
| /// |
| /// print(x); // prints "[3, 4, 5]" |
| /// |
| /// print(y); // prints "[1, 2]" |
| /// |
| /// let z = x.retain(|v, i| v + i <= 5); |
| /// |
| /// print(x); // prints "[3, 4]" |
| /// |
| /// print(z); // prints "[5]" |
| /// ``` |
| #[rhai_fn(return_raw)] |
| pub fn retain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> { |
| if array.is_empty() { |
| return Ok(Array::new()); |
| } |
| |
| let mut drained = Array::new(); |
| |
| let mut i = 0; |
| let mut x = 0; |
| |
| while x < array.len() { |
| let ex = [(i as INT).into()]; |
| |
| if filter |
| .call_raw_with_extra_args("retain", &ctx, Some(&mut array[x]), [], ex, Some(0))? |
| .as_bool() |
| .unwrap_or(false) |
| { |
| x += 1; |
| } else { |
| drained.push(array.remove(x)); |
| } |
| |
| i += 1; |
| } |
| |
| Ok(drained) |
| } |
| /// Remove all elements in the array not within an exclusive `range` and return them as a new array. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.retain(1..4); |
| /// |
| /// print(x); // prints "[2, 3, 4]" |
| /// |
| /// print(y); // prints "[1, 5]" |
| /// |
| /// let z = x.retain(1..3); |
| /// |
| /// print(x); // prints "[3, 4]" |
| /// |
| /// print(z); // prints "[1]" |
| /// ``` |
| #[rhai_fn(name = "retain")] |
| pub fn retain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array { |
| let start = INT::max(range.start, 0); |
| let end = INT::max(range.end, start); |
| retain_range(array, start, end - start) |
| } |
| /// Remove all elements in the array not within an inclusive `range` and return them as a new array. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.retain(1..=3); |
| /// |
| /// print(x); // prints "[2, 3, 4]" |
| /// |
| /// print(y); // prints "[1, 5]" |
| /// |
| /// let z = x.retain(1..=2); |
| /// |
| /// print(x); // prints "[3, 4]" |
| /// |
| /// print(z); // prints "[1]" |
| /// ``` |
| #[rhai_fn(name = "retain")] |
| pub fn retain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array { |
| let start = INT::max(*range.start(), 0); |
| let end = INT::max(*range.end(), start); |
| retain_range(array, start, end - start + 1) |
| } |
| /// Remove all elements not within a portion of the array and return them as a new array. |
| /// |
| /// * If `start` < 0, position counts from the end of the array (`-1` is the last element). |
| /// * If `start` < -length of array, position counts from the beginning of the array. |
| /// * If `start` ≥ length of array, all elements are removed returned. |
| /// * If `len` ≤ 0, all elements are removed and returned. |
| /// * If `start` position + `len` ≥ length of array, entire portion of the array before the `start` position is removed and returned. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// |
| /// let y = x.retain(1, 2); |
| /// |
| /// print(x); // prints "[2, 3]" |
| /// |
| /// print(y); // prints "[1, 4, 5]" |
| /// |
| /// let z = x.retain(-1, 1); |
| /// |
| /// print(x); // prints "[3]" |
| /// |
| /// print(z); // prints "[2]" |
| /// ``` |
| #[rhai_fn(name = "retain")] |
| pub fn retain_range(array: &mut Array, start: INT, len: INT) -> Array { |
| if array.is_empty() || len <= 0 { |
| return Array::new(); |
| } |
| |
| let (start, len) = calc_offset_len(array.len(), start, len); |
| |
| if len == 0 { |
| Array::new() |
| } else { |
| let mut drained: Array = array.drain(..start).collect(); |
| drained.extend(array.drain(len..)); |
| |
| drained |
| } |
| } |
| /// Return `true` if two arrays are equal (i.e. all elements are equal and in the same order). |
| /// |
| /// The operator `==` is used to compare elements and must be defined, |
| /// otherwise `false` is assumed. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// let y = [1, 2, 3, 4, 5]; |
| /// let z = [1, 2, 3, 4]; |
| /// |
| /// print(x == y); // prints true |
| /// |
| /// print(x == z); // prints false |
| /// ``` |
| #[rhai_fn(name = "==", return_raw, pure)] |
| pub fn equals(ctx: NativeCallContext, array1: &mut Array, array2: Array) -> RhaiResultOf<bool> { |
| if array1.len() != array2.len() { |
| return Ok(false); |
| } |
| if array1.is_empty() { |
| return Ok(true); |
| } |
| |
| let mut array2 = array2; |
| |
| for (a1, a2) in array1.iter_mut().zip(array2.iter_mut()) { |
| if !ctx |
| .call_native_fn_raw(OP_EQUALS, true, &mut [a1, a2]) |
| .or_else(|err| match *err { |
| ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => { |
| if a1.type_id() == a2.type_id() { |
| // No default when comparing same type |
| Err(err) |
| } else { |
| Ok(Dynamic::FALSE) |
| } |
| } |
| _ => Err(err), |
| })? |
| .as_bool() |
| .unwrap_or(false) |
| { |
| return Ok(false); |
| } |
| } |
| |
| Ok(true) |
| } |
| /// Return `true` if two arrays are not-equal (i.e. any element not equal or not in the same order). |
| /// |
| /// The operator `==` is used to compare elements and must be defined, |
| /// otherwise `false` is assumed. |
| /// |
| /// # Example |
| /// |
| /// ```rhai |
| /// let x = [1, 2, 3, 4, 5]; |
| /// let y = [1, 2, 3, 4, 5]; |
| /// let z = [1, 2, 3, 4]; |
| /// |
| /// print(x != y); // prints false |
| /// |
| /// print(x != z); // prints true |
| /// ``` |
| #[rhai_fn(name = "!=", return_raw, pure)] |
| pub fn not_equals( |
| ctx: NativeCallContext, |
| array1: &mut Array, |
| array2: Array, |
| ) -> RhaiResultOf<bool> { |
| equals(ctx, array1, array2).map(|r| !r) |
| } |
| } |