2020-05-06 13:45:17 +02:00
|
|
|
#![cfg(not(feature = "no_object"))]
|
|
|
|
|
2020-11-16 14:14:32 +01:00
|
|
|
use crate::engine::OP_EQUALS;
|
2022-11-23 04:36:30 +01:00
|
|
|
use crate::module::ModuleFlags;
|
2020-08-14 18:04:10 +02:00
|
|
|
use crate::plugin::*;
|
2022-09-29 16:46:59 +02:00
|
|
|
use crate::{def_package, Dynamic, ImmutableString, Map, NativeCallContext, RhaiResultOf, INT};
|
2021-04-17 09:15:54 +02:00
|
|
|
#[cfg(feature = "no_std")]
|
|
|
|
use std::prelude::v1::*;
|
2020-04-20 18:11:25 +02:00
|
|
|
|
2020-10-15 16:11:18 +02:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-11-16 09:28:04 +01:00
|
|
|
use crate::Array;
|
2020-04-24 06:39:24 +02:00
|
|
|
|
2021-12-20 04:42:39 +01:00
|
|
|
def_package! {
|
|
|
|
/// Package of basic object map utilities.
|
2022-02-10 05:33:48 +01:00
|
|
|
pub BasicMapPackage(lib) {
|
2022-11-23 04:36:30 +01:00
|
|
|
lib.flags |= ModuleFlags::STANDARD_LIB;
|
2021-11-05 16:22:05 +01:00
|
|
|
|
2021-12-20 04:42:39 +01:00
|
|
|
combine_with_exported_module!(lib, "map", map_functions);
|
|
|
|
}
|
|
|
|
}
|
2020-08-14 18:04:10 +02:00
|
|
|
|
|
|
|
#[export_module]
|
|
|
|
mod map_functions {
|
2022-01-15 16:34:38 +01:00
|
|
|
/// Return the number of properties in the object map.
|
2021-02-19 16:13:53 +01:00
|
|
|
#[rhai_fn(pure)]
|
2020-08-14 18:04:10 +02:00
|
|
|
pub fn len(map: &mut Map) -> INT {
|
2020-08-16 17:41:59 +02:00
|
|
|
map.len() as INT
|
|
|
|
}
|
2022-08-24 09:54:41 +02:00
|
|
|
/// Return true if the map is empty.
|
2022-08-24 15:58:08 +02:00
|
|
|
#[rhai_fn(pure)]
|
2022-08-24 09:54:41 +02:00
|
|
|
pub fn is_empty(map: &mut Map) -> bool {
|
|
|
|
map.len() == 0
|
|
|
|
}
|
2022-09-25 17:03:18 +02:00
|
|
|
/// Returns `true` if the object map contains a specified property.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m = #{a: 1, b: 2, c: 3};
|
|
|
|
///
|
|
|
|
/// print(m.contains("b")); // prints true
|
|
|
|
///
|
|
|
|
/// print(m.contains("x")); // prints false
|
|
|
|
/// ```
|
|
|
|
pub fn contains(map: &mut Map, property: &str) -> bool {
|
|
|
|
map.contains_key(property)
|
|
|
|
}
|
2022-01-17 03:21:03 +01:00
|
|
|
/// Get the value of the `property` in the object map and return a copy.
|
|
|
|
///
|
|
|
|
/// If `property` does not exist in the object map, `()` is returned.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m = #{a: 1, b: 2, c: 3};
|
|
|
|
///
|
|
|
|
/// print(m.get("b")); // prints 2
|
|
|
|
///
|
|
|
|
/// print(m.get("x")); // prints empty (for '()')
|
|
|
|
/// ```
|
|
|
|
pub fn get(map: &mut Map, property: &str) -> Dynamic {
|
|
|
|
if map.is_empty() {
|
|
|
|
return Dynamic::UNIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
map.get(property).cloned().unwrap_or(Dynamic::UNIT)
|
|
|
|
}
|
|
|
|
/// Set the value of the `property` in the object map to a new `value`.
|
|
|
|
///
|
|
|
|
/// If `property` does not exist in the object map, it is added.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m = #{a: 1, b: 2, c: 3};
|
|
|
|
///
|
|
|
|
/// m.set("b", 42)'
|
|
|
|
///
|
|
|
|
/// print(m); // prints "#{a: 1, b: 42, c: 3}"
|
|
|
|
///
|
|
|
|
/// x.set("x", 0);
|
|
|
|
///
|
|
|
|
/// print(m); // prints "#{a: 1, b: 42, c: 3, x: 0}"
|
|
|
|
/// ```
|
|
|
|
pub fn set(map: &mut Map, property: &str, value: Dynamic) {
|
2022-10-10 10:46:35 +02:00
|
|
|
match map.get_mut(property) {
|
|
|
|
Some(value_ref) => *value_ref = value,
|
|
|
|
_ => {
|
|
|
|
map.insert(property.into(), value);
|
|
|
|
}
|
2022-01-17 03:21:03 +01:00
|
|
|
}
|
|
|
|
}
|
2022-01-15 16:34:38 +01:00
|
|
|
/// Clear the object map.
|
2020-08-14 18:04:10 +02:00
|
|
|
pub fn clear(map: &mut Map) {
|
2021-09-20 12:35:23 +02:00
|
|
|
if !map.is_empty() {
|
|
|
|
map.clear();
|
|
|
|
}
|
2020-08-14 18:04:10 +02:00
|
|
|
}
|
2022-01-15 16:34:38 +01:00
|
|
|
/// Remove any property of the specified `name` from the object map, returning its value.
|
|
|
|
///
|
|
|
|
/// If the property does not exist, `()` is returned.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m = #{a:1, b:2, c:3};
|
|
|
|
///
|
|
|
|
/// let x = m.remove("b");
|
|
|
|
///
|
|
|
|
/// print(x); // prints 2
|
|
|
|
///
|
|
|
|
/// print(m); // prints "#{a:1, c:3}"
|
|
|
|
/// ```
|
2022-01-17 03:21:03 +01:00
|
|
|
pub fn remove(map: &mut Map, property: &str) -> Dynamic {
|
2022-07-27 12:04:59 +02:00
|
|
|
if map.is_empty() {
|
2021-09-20 12:35:23 +02:00
|
|
|
Dynamic::UNIT
|
2022-07-27 12:04:59 +02:00
|
|
|
} else {
|
|
|
|
map.remove(property).unwrap_or(Dynamic::UNIT)
|
2021-09-20 12:35:23 +02:00
|
|
|
}
|
2020-08-14 18:04:10 +02:00
|
|
|
}
|
2022-01-15 16:34:38 +01:00
|
|
|
/// Add all property values of another object map into the object map.
|
|
|
|
/// Existing property values of the same names are replaced.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m = #{a:1, b:2, c:3};
|
|
|
|
/// let n = #{a: 42, d:0};
|
|
|
|
///
|
|
|
|
/// m.mixin(n);
|
|
|
|
///
|
|
|
|
/// print(m); // prints "#{a:42, b:2, c:3, d:0}"
|
|
|
|
/// ```
|
2020-09-08 12:01:34 +02:00
|
|
|
#[rhai_fn(name = "mixin", name = "+=")]
|
2021-02-19 16:13:53 +01:00
|
|
|
pub fn mixin(map: &mut Map, map2: Map) {
|
2021-09-20 12:35:23 +02:00
|
|
|
if !map2.is_empty() {
|
|
|
|
map.extend(map2.into_iter());
|
|
|
|
}
|
2020-08-14 18:04:10 +02:00
|
|
|
}
|
2022-01-15 16:34:38 +01:00
|
|
|
/// Make a copy of the object map, add all property values of another object map
|
|
|
|
/// (existing property values of the same names are replaced), then returning it.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m = #{a:1, b:2, c:3};
|
|
|
|
/// let n = #{a: 42, d:0};
|
|
|
|
///
|
|
|
|
/// print(m + n); // prints "#{a:42, b:2, c:3, d:0}"
|
|
|
|
///
|
|
|
|
/// print(m); // prints "#{a:1, b:2, c:3}"
|
|
|
|
/// ```
|
2020-08-16 17:41:59 +02:00
|
|
|
#[rhai_fn(name = "+")]
|
2021-08-13 07:42:39 +02:00
|
|
|
pub fn merge(map1: Map, map2: Map) -> Map {
|
2021-09-20 12:35:23 +02:00
|
|
|
if map2.is_empty() {
|
|
|
|
map1
|
|
|
|
} else if map1.is_empty() {
|
|
|
|
map2
|
|
|
|
} else {
|
|
|
|
let mut map1 = map1;
|
|
|
|
map1.extend(map2.into_iter());
|
|
|
|
map1
|
|
|
|
}
|
2020-08-14 18:04:10 +02:00
|
|
|
}
|
2022-01-15 16:34:38 +01:00
|
|
|
/// Add all property values of another object map into the object map.
|
|
|
|
/// Only properties that do not originally exist in the object map are added.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m = #{a:1, b:2, c:3};
|
|
|
|
/// let n = #{a: 42, d:0};
|
|
|
|
///
|
|
|
|
/// m.fill_with(n);
|
|
|
|
///
|
|
|
|
/// print(m); // prints "#{a:1, b:2, c:3, d:0}"
|
|
|
|
/// ```
|
2021-02-19 16:13:53 +01:00
|
|
|
pub fn fill_with(map: &mut Map, map2: Map) {
|
2021-09-20 12:35:23 +02:00
|
|
|
if !map2.is_empty() {
|
|
|
|
if map.is_empty() {
|
|
|
|
*map = map2;
|
|
|
|
} else {
|
2022-07-27 12:04:59 +02:00
|
|
|
for (key, value) in map2 {
|
2021-09-20 12:35:23 +02:00
|
|
|
map.entry(key).or_insert(value);
|
2022-07-27 12:04:59 +02:00
|
|
|
}
|
2021-09-20 12:35:23 +02:00
|
|
|
}
|
|
|
|
}
|
2020-08-14 18:04:10 +02:00
|
|
|
}
|
2022-01-15 16:34:38 +01:00
|
|
|
/// Return `true` if two object maps are equal (i.e. all property values are equal).
|
|
|
|
///
|
|
|
|
/// The operator `==` is used to compare property values and must be defined,
|
|
|
|
/// otherwise `false` is assumed.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m1 = #{a:1, b:2, c:3};
|
|
|
|
/// let m2 = #{a:1, b:2, c:3};
|
|
|
|
/// let m3 = #{a:1, c:3};
|
|
|
|
///
|
|
|
|
/// print(m1 == m2); // prints true
|
|
|
|
///
|
|
|
|
/// print(m1 == m3); // prints false
|
|
|
|
/// ```
|
2021-02-19 16:13:53 +01:00
|
|
|
#[rhai_fn(name = "==", return_raw, pure)]
|
2021-12-25 16:49:14 +01:00
|
|
|
pub fn equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf<bool> {
|
2021-08-13 07:42:39 +02:00
|
|
|
if map1.len() != map2.len() {
|
2021-03-22 04:18:09 +01:00
|
|
|
return Ok(false);
|
2020-11-08 16:00:37 +01:00
|
|
|
}
|
|
|
|
|
2021-09-20 12:35:23 +02:00
|
|
|
if !map1.is_empty() {
|
|
|
|
let mut map2 = map2;
|
2021-08-13 07:42:39 +02:00
|
|
|
|
2022-07-05 10:26:38 +02:00
|
|
|
for (m1, v1) in map1 {
|
2021-09-20 12:35:23 +02:00
|
|
|
if let Some(v2) = map2.get_mut(m1) {
|
|
|
|
let equals = ctx
|
2022-10-14 10:57:14 +02:00
|
|
|
.call_native_fn_raw(OP_EQUALS, true, &mut [v1, v2])?
|
2022-01-13 11:13:27 +01:00
|
|
|
.as_bool()
|
|
|
|
.unwrap_or(false);
|
2020-11-08 16:00:37 +01:00
|
|
|
|
2021-09-20 12:35:23 +02:00
|
|
|
if !equals {
|
|
|
|
return Ok(false);
|
|
|
|
}
|
|
|
|
} else {
|
2021-03-22 04:18:09 +01:00
|
|
|
return Ok(false);
|
2020-11-08 16:00:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-22 04:18:09 +01:00
|
|
|
Ok(true)
|
2020-11-08 16:00:37 +01:00
|
|
|
}
|
2022-01-15 16:34:38 +01:00
|
|
|
/// Return `true` if two object maps are not equal (i.e. at least one property value is not equal).
|
|
|
|
///
|
|
|
|
/// The operator `==` is used to compare property values and must be defined,
|
|
|
|
/// otherwise `false` is assumed.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m1 = #{a:1, b:2, c:3};
|
|
|
|
/// let m2 = #{a:1, b:2, c:3};
|
|
|
|
/// let m3 = #{a:1, c:3};
|
|
|
|
///
|
|
|
|
/// print(m1 != m2); // prints false
|
|
|
|
///
|
|
|
|
/// print(m1 != m3); // prints true
|
|
|
|
/// ```
|
2021-02-19 16:13:53 +01:00
|
|
|
#[rhai_fn(name = "!=", return_raw, pure)]
|
2021-12-25 16:49:14 +01:00
|
|
|
pub fn not_equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf<bool> {
|
2021-08-13 07:42:39 +02:00
|
|
|
equals(ctx, map1, map2).map(|r| !r)
|
2020-11-08 16:00:37 +01:00
|
|
|
}
|
2020-04-21 17:01:10 +02:00
|
|
|
|
2022-01-15 16:34:38 +01:00
|
|
|
/// Return an array with all the property names in the object map.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m = #{a:1, b:2, c:3};
|
|
|
|
///
|
|
|
|
/// print(m.keys()); // prints ["a", "b", "c"]
|
|
|
|
/// ```
|
2020-08-22 16:26:49 +02:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
2021-10-20 10:22:12 +02:00
|
|
|
#[rhai_fn(pure)]
|
|
|
|
pub fn keys(map: &mut Map) -> Array {
|
|
|
|
if map.is_empty() {
|
|
|
|
Array::new()
|
|
|
|
} else {
|
2021-12-27 09:59:05 +01:00
|
|
|
map.keys().cloned().map(Into::into).collect()
|
2020-08-22 16:26:49 +02:00
|
|
|
}
|
2021-10-20 10:22:12 +02:00
|
|
|
}
|
2022-01-15 16:34:38 +01:00
|
|
|
/// Return an array with all the property values in the object map.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m = #{a:1, b:2, c:3};
|
|
|
|
///
|
|
|
|
/// print(m.values()); // prints "[1, 2, 3]""
|
|
|
|
/// ```
|
2021-10-20 10:22:12 +02:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
|
|
|
#[rhai_fn(pure)]
|
|
|
|
pub fn values(map: &mut Map) -> Array {
|
|
|
|
if map.is_empty() {
|
|
|
|
Array::new()
|
|
|
|
} else {
|
|
|
|
map.values().cloned().collect()
|
2020-08-22 16:26:49 +02:00
|
|
|
}
|
2020-08-14 18:04:10 +02:00
|
|
|
}
|
2022-04-21 06:15:21 +02:00
|
|
|
/// Return the JSON representation of the object map.
|
|
|
|
///
|
|
|
|
/// # Data types
|
|
|
|
///
|
|
|
|
/// Only the following data types should be kept inside the object map:
|
|
|
|
/// `INT`, `FLOAT`, `ImmutableString`, `char`, `bool`, `()`, `Array`, `Map`.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
///
|
|
|
|
/// Data types not supported by JSON serialize into formats that may
|
|
|
|
/// invalidate the result.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rhai
|
|
|
|
/// let m = #{a:1, b:2, c:3};
|
|
|
|
///
|
|
|
|
/// print(m.to_json()); // prints {"a":1, "b":2, "c":3}
|
|
|
|
/// ```
|
|
|
|
pub fn to_json(map: &mut Map) -> String {
|
2022-09-29 16:46:59 +02:00
|
|
|
#[cfg(feature = "metadata")]
|
|
|
|
return serde_json::to_string(map).unwrap_or_else(|_| "ERROR".into());
|
|
|
|
#[cfg(not(feature = "metadata"))]
|
|
|
|
return crate::format_map_as_json(map);
|
2022-04-21 06:15:21 +02:00
|
|
|
}
|
2020-08-14 18:04:10 +02:00
|
|
|
}
|