diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d99ecf9..0c779c63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Enhancements * Formatting of return types in functions metadata info is improved. * Use `SmartString` for `Scope` variable names and remove `unsafe` lifetime casting. * Functions in the standard library now have doc-comments (which can be obtained via `Engine::gen_fn_metadata_to_json`). +* `get` and `set` methods are added to arrays, BLOB's, object maps and strings. Version 1.4.0 diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 83af8683..17d43376 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -31,6 +31,70 @@ pub mod array_functions { pub fn len(array: &mut Array) -> INT { array.len() as INT } + /// 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. diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index ca240df3..04ee1e17 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -100,6 +100,75 @@ pub mod blob_functions { pub fn len(blob: &mut Blob) -> INT { blob.len() as INT } + + /// Get the byte value at the `index` position in the BLOB. + /// + /// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last element). + /// * If `index` < -length of BLOB, zero is returned. + /// * If `index` ≥ length of BLOB, zero is returned. + /// + /// # Example + /// + /// ```rhai + /// let b = blob(); + /// + /// b += 1; b += 2; b += 3; b += 4; b += 5; + /// + /// print(b.get(0)); // prints 1 + /// + /// print(b.get(-1)); // prints 5 + /// + /// print(b.get(99)); // prints 0 + /// ``` + pub fn get(blob: &mut Blob, index: INT) -> INT { + if blob.is_empty() { + return 0; + } + + let (index, _) = calc_offset_len(blob.len(), index, 0); + + if index >= blob.len() { + 0 + } else { + blob[index] as INT + } + } + /// Set the particular `index` position in the BLOB to a new byte `value`. + /// + /// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last byte). + /// * If `index` < -length of BLOB, the BLOB is not modified. + /// * If `index` ≥ length of BLOB, the BLOB is not modified. + /// + /// # Example + /// + /// ```rhai + /// let b = blob(); + /// + /// b += 1; b += 2; b += 3; b += 4; b += 5; + /// + /// b.set(0, 0x42); + /// + /// print(b); // prints "[4202030405]" + /// + /// b.set(-3, 0); + /// + /// print(b); // prints "[4202000405]" + /// + /// b.set(99, 123); + /// + /// print(b); // prints "[4202000405]" + /// ``` + pub fn set(blob: &mut Blob, index: INT, value: INT) { + if blob.is_empty() { + return; + } + + let (index, _) = calc_offset_len(blob.len(), index, 0); + + if index < blob.len() { + blob[index] = (value & 0x000000ff) as u8; + } + } /// Add a new byte `value` to the end of the BLOB. /// /// Only the lower 8 bits of the `value` are used; all other bits are ignored. @@ -114,8 +183,7 @@ pub mod blob_functions { /// print(b); // prints "[42]" /// ``` pub fn push(blob: &mut Blob, value: INT) { - let value = (value & 0x000000ff) as u8; - blob.push(value); + blob.push((value & 0x000000ff) as u8); } /// Add another BLOB to the end of the BLOB. /// diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 497bb0cc..8eaf6b17 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -25,6 +25,51 @@ mod map_functions { pub fn len(map: &mut Map) -> INT { map.len() as INT } + + /// 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) { + if let Some(value_ref) = map.get_mut(property) { + *value_ref = value; + } else { + map.insert(property.into(), value); + } + } /// Clear the object map. pub fn clear(map: &mut Map) { if !map.is_empty() { @@ -46,9 +91,9 @@ mod map_functions { /// /// print(m); // prints "#{a:1, c:3}" /// ``` - pub fn remove(map: &mut Map, name: ImmutableString) -> Dynamic { + pub fn remove(map: &mut Map, property: &str) -> Dynamic { if !map.is_empty() { - map.remove(name.as_str()).unwrap_or_else(|| Dynamic::UNIT) + map.remove(property).unwrap_or_else(|| Dynamic::UNIT) } else { Dynamic::UNIT } diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 7ab926e2..202cf6ef 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -551,6 +551,85 @@ mod string_functions { } } + /// Get the character at the `index` position in the string. + /// + /// * If `index` < 0, position counts from the end of the string (`-1` is the last character). + /// * If `index` < -length of string, zero is returned. + /// * If `index` ≥ length of string, zero is returned. + /// + /// # Example + /// + /// ```rhai + /// let text = "hello, world!"; + /// + /// print(text.get(0)); // prints 'h' + /// + /// print(text.get(-1)); // prints '!' + /// + /// print(text.get(99)); // prints empty (for '()')' + /// ``` + pub fn get(string: &str, index: INT) -> Dynamic { + if index >= 0 { + string + .chars() + .nth(index as usize) + .map_or_else(|| Dynamic::UNIT, Into::into) + } else if let Some(abs_index) = index.checked_abs() { + // Count from end if negative + string + .chars() + .rev() + .nth((abs_index as usize) - 1) + .map_or_else(|| Dynamic::UNIT, Into::into) + } else { + Dynamic::UNIT + } + } + /// Set the `index` position in the string to a new `character`. + /// + /// * If `index` < 0, position counts from the end of the string (`-1` is the last character). + /// * If `index` < -length of string, the string is not modified. + /// * If `index` ≥ length of string, the string is not modified. + /// + /// # Example + /// + /// ```rhai + /// let text = "hello, world!"; + /// + /// text.set(3, 'x'); + /// + /// print(text); // prints "helxo, world!" + /// + /// text.set(-3, 'x'); + /// + /// print(text); // prints "hello, worxd!" + /// + /// text.set(99, 'x'); + /// + /// print(text); // prints "hello, worxd!" + /// ``` + pub fn set(string: &mut ImmutableString, index: INT, character: char) { + if index >= 0 { + let index = index as usize; + *string = string + .chars() + .enumerate() + .map(|(i, ch)| if i == index { character } else { ch }) + .collect(); + } else if let Some(abs_index) = index.checked_abs() { + let string_len = string.chars().count(); + + if abs_index as usize <= string_len { + let index = string_len - (abs_index as usize); + *string = string + .chars() + .enumerate() + .map(|(i, ch)| if i == index { character } else { ch }) + .collect(); + } + } + } + /// Copy an exclusive range of characters from the string and return it as a new string. /// /// # Example