Short circuit functions if empty: array, map, string.

This commit is contained in:
Stephen Chung 2021-09-20 18:35:23 +08:00
parent 6e7fbfa66f
commit adbe9a292e
3 changed files with 352 additions and 137 deletions

View File

@ -27,15 +27,29 @@ mod array_functions {
} }
#[rhai_fn(name = "append", name = "+=")] #[rhai_fn(name = "append", name = "+=")]
pub fn append(array: &mut Array, y: Array) { pub fn append(array: &mut Array, y: Array) {
if !y.is_empty() {
if array.is_empty() {
*array = y;
} else {
array.extend(y); array.extend(y);
} }
}
}
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn concat(mut array: Array, y: Array) -> Array { pub fn concat(mut array: Array, y: Array) -> Array {
if !y.is_empty() {
if array.is_empty() {
array = y;
} else {
array.extend(y); array.extend(y);
}
}
array array
} }
pub fn insert(array: &mut Array, position: INT, item: Dynamic) { pub fn insert(array: &mut Array, position: INT, item: Dynamic) {
if position < 0 { if array.is_empty() {
array.push(item);
} else if position < 0 {
if let Some(n) = position.checked_abs() { if let Some(n) = position.checked_abs() {
if n as usize > array.len() { if n as usize > array.len() {
array.insert(0, item); array.insert(0, item);
@ -46,7 +60,7 @@ mod array_functions {
array.insert(0, item); array.insert(0, item);
} }
} else if (position as usize) >= array.len() { } else if (position as usize) >= array.len() {
push(array, item); array.push(item);
} else { } else {
array.insert(position as usize, item); array.insert(position as usize, item);
} }
@ -58,61 +72,78 @@ mod array_functions {
len: INT, len: INT,
item: Dynamic, item: Dynamic,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
if len <= 0 {
return Ok(());
}
// Check if array will be over max size limit // Check if array will be over max size limit
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_array_size() > 0 if _ctx.engine().max_array_size() > 0 && (len as usize) > _ctx.engine().max_array_size() {
&& len > 0
&& (len as usize) > _ctx.engine().max_array_size()
{
return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), Position::NONE) return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), Position::NONE)
.into(); .into();
} }
if len > 0 && len as usize > array.len() { if len as usize > array.len() {
array.resize(len as usize, item); array.resize(len as usize, item);
} }
Ok(()) Ok(())
} }
pub fn pop(array: &mut Array) -> Dynamic { pub fn pop(array: &mut Array) -> Dynamic {
array.pop().unwrap_or_else(|| ().into()) if array.is_empty() {
Dynamic::UNIT
} else {
array.pop().unwrap_or_else(|| Dynamic::UNIT)
}
} }
pub fn shift(array: &mut Array) -> Dynamic { pub fn shift(array: &mut Array) -> Dynamic {
if array.is_empty() { if array.is_empty() {
().into() Dynamic::UNIT
} else { } else {
array.remove(0) array.remove(0)
} }
} }
pub fn remove(array: &mut Array, len: INT) -> Dynamic { pub fn remove(array: &mut Array, len: INT) -> Dynamic {
if len < 0 || (len as usize) >= array.len() { if len < 0 || (len as usize) >= array.len() {
().into() Dynamic::UNIT
} else { } else {
array.remove(len as usize) array.remove(len as usize)
} }
} }
pub fn clear(array: &mut Array) { pub fn clear(array: &mut Array) {
if !array.is_empty() {
array.clear(); array.clear();
} }
}
pub fn truncate(array: &mut Array, len: INT) { pub fn truncate(array: &mut Array, len: INT) {
if !array.is_empty() {
if len >= 0 { if len >= 0 {
array.truncate(len as usize); array.truncate(len as usize);
} else { } else {
array.clear(); array.clear();
} }
} }
}
pub fn chop(array: &mut Array, len: INT) { pub fn chop(array: &mut Array, len: INT) {
if len as usize >= array.len() { if !array.is_empty() && len as usize >= array.len() {
} else if len >= 0 { if len >= 0 {
array.drain(0..array.len() - len as usize); array.drain(0..array.len() - len as usize);
} else { } else {
array.clear(); array.clear();
} }
} }
}
pub fn reverse(array: &mut Array) { pub fn reverse(array: &mut Array) {
if !array.is_empty() {
array.reverse(); array.reverse();
} }
}
pub fn splice(array: &mut Array, start: INT, len: INT, replace: Array) { pub fn splice(array: &mut Array, start: INT, len: INT, replace: Array) {
if array.is_empty() {
*array = replace;
return;
}
let start = if start < 0 { let start = if start < 0 {
let arr_len = array.len(); let arr_len = array.len();
start start
@ -136,18 +167,22 @@ mod array_functions {
array.splice(start..start + len, replace.into_iter()); array.splice(start..start + len, replace.into_iter());
} }
pub fn extract(array: &mut Array, start: INT, len: INT) -> Array { pub fn extract(array: &mut Array, start: INT, len: INT) -> Array {
if array.is_empty() || len <= 0 {
return Array::new();
}
let start = if start < 0 { let start = if start < 0 {
let arr_len = array.len(); let arr_len = array.len();
start start
.checked_abs() .checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len)) .map_or(0, |n| arr_len - (n as usize).min(arr_len))
} else if start as usize >= array.len() { } else if start as usize >= array.len() {
return Default::default(); return Array::new();
} else { } else {
start as usize start as usize
}; };
let len = if len < 0 { let len = if len <= 0 {
0 0
} else if len as usize > array.len() - start { } else if len as usize > array.len() - start {
array.len() - start array.len() - start
@ -155,17 +190,25 @@ mod array_functions {
len as usize len as usize
}; };
if len == 0 {
Array::new()
} else {
array[start..start + len].to_vec() array[start..start + len].to_vec()
} }
}
#[rhai_fn(name = "extract")] #[rhai_fn(name = "extract")]
pub fn extract_tail(array: &mut Array, start: INT) -> Array { pub fn extract_tail(array: &mut Array, start: INT) -> Array {
if array.is_empty() {
return Array::new();
}
let start = if start < 0 { let start = if start < 0 {
let arr_len = array.len(); let arr_len = array.len();
start start
.checked_abs() .checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len)) .map_or(0, |n| arr_len - (n as usize).min(arr_len))
} else if start as usize >= array.len() { } else if start as usize >= array.len() {
return Default::default(); return Array::new();
} else { } else {
start as usize start as usize
}; };
@ -174,7 +217,9 @@ mod array_functions {
} }
#[rhai_fn(name = "split")] #[rhai_fn(name = "split")]
pub fn split_at(array: &mut Array, start: INT) -> Array { pub fn split_at(array: &mut Array, start: INT) -> Array {
if start < 0 { if array.is_empty() {
Array::new()
} else if start < 0 {
if let Some(n) = start.checked_abs() { if let Some(n) = start.checked_abs() {
if n as usize > array.len() { if n as usize > array.len() {
mem::take(array) mem::take(array)
@ -187,7 +232,7 @@ mod array_functions {
mem::take(array) mem::take(array)
} }
} else if start as usize >= array.len() { } else if start as usize >= array.len() {
Default::default() Array::new()
} else { } else {
let mut result = Array::new(); let mut result = Array::new();
result.extend(array.drain(start as usize..)); result.extend(array.drain(start as usize..));
@ -200,6 +245,10 @@ mod array_functions {
array: &mut Array, array: &mut Array,
mapper: FnPtr, mapper: FnPtr,
) -> Result<Array, Box<EvalAltResult>> { ) -> Result<Array, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(array.clone());
}
let mut ar = Array::with_capacity(array.len()); let mut ar = Array::with_capacity(array.len());
for (i, item) in array.iter().enumerate() { for (i, item) in array.iter().enumerate() {
@ -233,6 +282,10 @@ mod array_functions {
array: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Array, Box<EvalAltResult>> { ) -> Result<Array, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(array.clone());
}
let mut ar = Array::new(); let mut ar = Array::new();
for (i, item) in array.iter().enumerate() { for (i, item) in array.iter().enumerate() {
@ -269,6 +322,10 @@ mod array_functions {
array: &mut Array, array: &mut Array,
value: Dynamic, value: Dynamic,
) -> Result<bool, Box<EvalAltResult>> { ) -> Result<bool, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(false);
}
for item in array.iter_mut() { for item in array.iter_mut() {
if ctx if ctx
.call_fn_dynamic_raw(OP_EQUALS, true, &mut [item, &mut value.clone()]) .call_fn_dynamic_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
@ -300,8 +357,12 @@ mod array_functions {
array: &mut Array, array: &mut Array,
value: Dynamic, value: Dynamic,
) -> Result<INT, Box<EvalAltResult>> { ) -> Result<INT, Box<EvalAltResult>> {
if array.is_empty() {
Ok(-1)
} else {
index_of_starting_from(ctx, array, value, 0) index_of_starting_from(ctx, array, value, 0)
} }
}
#[rhai_fn(name = "index_of", return_raw, pure)] #[rhai_fn(name = "index_of", return_raw, pure)]
pub fn index_of_starting_from( pub fn index_of_starting_from(
ctx: NativeCallContext, ctx: NativeCallContext,
@ -309,6 +370,10 @@ mod array_functions {
value: Dynamic, value: Dynamic,
start: INT, start: INT,
) -> Result<INT, Box<EvalAltResult>> { ) -> Result<INT, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(-1);
}
let start = if start < 0 { let start = if start < 0 {
let arr_len = array.len(); let arr_len = array.len();
start start
@ -351,8 +416,12 @@ mod array_functions {
array: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<INT, Box<EvalAltResult>> { ) -> Result<INT, Box<EvalAltResult>> {
if array.is_empty() {
Ok(-1)
} else {
index_of_filter_starting_from(ctx, array, filter, 0) index_of_filter_starting_from(ctx, array, filter, 0)
} }
}
#[rhai_fn(name = "index_of", return_raw, pure)] #[rhai_fn(name = "index_of", return_raw, pure)]
pub fn index_of_filter_starting_from( pub fn index_of_filter_starting_from(
ctx: NativeCallContext, ctx: NativeCallContext,
@ -360,6 +429,10 @@ mod array_functions {
filter: FnPtr, filter: FnPtr,
start: INT, start: INT,
) -> Result<INT, Box<EvalAltResult>> { ) -> Result<INT, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(-1);
}
let start = if start < 0 { let start = if start < 0 {
let arr_len = array.len(); let arr_len = array.len();
start start
@ -405,6 +478,10 @@ mod array_functions {
array: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<bool, Box<EvalAltResult>> { ) -> Result<bool, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(false);
}
for (i, item) in array.iter().enumerate() { for (i, item) in array.iter().enumerate() {
if filter if filter
.call_dynamic(&ctx, None, [item.clone()]) .call_dynamic(&ctx, None, [item.clone()])
@ -439,6 +516,10 @@ mod array_functions {
array: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<bool, Box<EvalAltResult>> { ) -> Result<bool, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(true);
}
for (i, item) in array.iter().enumerate() { for (i, item) in array.iter().enumerate() {
if !filter if !filter
.call_dynamic(&ctx, None, [item.clone()]) .call_dynamic(&ctx, None, [item.clone()])
@ -473,6 +554,10 @@ mod array_functions {
array: &mut Array, array: &mut Array,
reducer: FnPtr, reducer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(Dynamic::UNIT);
}
let mut result = Dynamic::UNIT; let mut result = Dynamic::UNIT;
for (i, item) in array.iter().enumerate() { for (i, item) in array.iter().enumerate() {
@ -505,6 +590,10 @@ mod array_functions {
reducer: FnPtr, reducer: FnPtr,
initial: Dynamic, initial: Dynamic,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(initial);
}
let mut result = initial; let mut result = initial;
for (i, item) in array.iter().enumerate() { for (i, item) in array.iter().enumerate() {
@ -536,6 +625,10 @@ mod array_functions {
array: &mut Array, array: &mut Array,
reducer: FnPtr, reducer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(Dynamic::UNIT);
}
let mut result = Dynamic::UNIT; let mut result = Dynamic::UNIT;
for (i, item) in array.iter().enumerate().rev() { for (i, item) in array.iter().enumerate().rev() {
@ -568,6 +661,10 @@ mod array_functions {
reducer: FnPtr, reducer: FnPtr,
initial: Dynamic, initial: Dynamic,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(initial);
}
let mut result = initial; let mut result = initial;
for (i, item) in array.iter().enumerate().rev() { for (i, item) in array.iter().enumerate().rev() {
@ -599,6 +696,10 @@ mod array_functions {
array: &mut Array, array: &mut Array,
comparer: FnPtr, comparer: FnPtr,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
if array.is_empty() {
return Ok(());
}
array.sort_by(|x, y| { array.sort_by(|x, y| {
comparer comparer
.call_dynamic(&ctx, None, [x.clone(), y.clone()]) .call_dynamic(&ctx, None, [x.clone(), y.clone()])
@ -621,6 +722,10 @@ mod array_functions {
array: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Array, Box<EvalAltResult>> { ) -> Result<Array, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(Array::new());
}
let mut drained = Array::with_capacity(array.len()); let mut drained = Array::with_capacity(array.len());
let mut i = 0; let mut i = 0;
@ -660,18 +765,22 @@ mod array_functions {
} }
#[rhai_fn(name = "drain")] #[rhai_fn(name = "drain")]
pub fn drain_range(array: &mut Array, start: INT, len: INT) -> Array { pub fn drain_range(array: &mut Array, start: INT, len: INT) -> Array {
if array.is_empty() || len <= 0 {
return Array::new();
}
let start = if start < 0 { let start = if start < 0 {
let arr_len = array.len(); let arr_len = array.len();
start start
.checked_abs() .checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len)) .map_or(0, |n| arr_len - (n as usize).min(arr_len))
} else if start as usize >= array.len() { } else if start as usize >= array.len() {
return Default::default(); return Array::new();
} else { } else {
start as usize start as usize
}; };
let len = if len < 0 { let len = if len <= 0 {
0 0
} else if len as usize > array.len() - start { } else if len as usize > array.len() - start {
array.len() - start array.len() - start
@ -687,6 +796,10 @@ mod array_functions {
array: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Array, Box<EvalAltResult>> { ) -> Result<Array, Box<EvalAltResult>> {
if array.is_empty() {
return Ok(Array::new());
}
let mut drained = Array::new(); let mut drained = Array::new();
let mut i = 0; let mut i = 0;
@ -726,6 +839,10 @@ mod array_functions {
} }
#[rhai_fn(name = "retain")] #[rhai_fn(name = "retain")]
pub fn retain_range(array: &mut Array, start: INT, len: INT) -> Array { pub fn retain_range(array: &mut Array, start: INT, len: INT) -> Array {
if array.is_empty() || len <= 0 {
return Array::new();
}
let start = if start < 0 { let start = if start < 0 {
let arr_len = array.len(); let arr_len = array.len();
start start

View File

@ -17,33 +17,57 @@ def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, {
mod map_functions { mod map_functions {
#[rhai_fn(name = "has", pure)] #[rhai_fn(name = "has", pure)]
pub fn contains(map: &mut Map, prop: ImmutableString) -> bool { pub fn contains(map: &mut Map, prop: ImmutableString) -> bool {
if map.is_empty() {
false
} else {
map.contains_key(prop.as_str()) map.contains_key(prop.as_str())
} }
}
#[rhai_fn(pure)] #[rhai_fn(pure)]
pub fn len(map: &mut Map) -> INT { pub fn len(map: &mut Map) -> INT {
map.len() as INT map.len() as INT
} }
pub fn clear(map: &mut Map) { pub fn clear(map: &mut Map) {
if !map.is_empty() {
map.clear(); map.clear();
} }
}
pub fn remove(map: &mut Map, name: ImmutableString) -> Dynamic { pub fn remove(map: &mut Map, name: ImmutableString) -> Dynamic {
map.remove(name.as_str()).unwrap_or_else(|| ().into()) if !map.is_empty() {
map.remove(name.as_str()).unwrap_or_else(|| Dynamic::UNIT)
} else {
Dynamic::UNIT
}
} }
#[rhai_fn(name = "mixin", name = "+=")] #[rhai_fn(name = "mixin", name = "+=")]
pub fn mixin(map: &mut Map, map2: Map) { pub fn mixin(map: &mut Map, map2: Map) {
if !map2.is_empty() {
map.extend(map2.into_iter()); map.extend(map2.into_iter());
} }
}
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn merge(map1: Map, map2: Map) -> Map { pub fn merge(map1: Map, map2: Map) -> Map {
if map2.is_empty() {
map1
} else if map1.is_empty() {
map2
} else {
let mut map1 = map1; let mut map1 = map1;
map1.extend(map2.into_iter()); map1.extend(map2.into_iter());
map1 map1
} }
}
pub fn fill_with(map: &mut Map, map2: Map) { pub fn fill_with(map: &mut Map, map2: Map) {
if !map2.is_empty() {
if map.is_empty() {
*map = map2;
} else {
map2.into_iter().for_each(|(key, value)| { map2.into_iter().for_each(|(key, value)| {
map.entry(key).or_insert(value); map.entry(key).or_insert(value);
}); });
} }
}
}
#[rhai_fn(name = "==", return_raw, pure)] #[rhai_fn(name = "==", return_raw, pure)]
pub fn equals( pub fn equals(
ctx: NativeCallContext, ctx: NativeCallContext,
@ -53,10 +77,8 @@ mod map_functions {
if map1.len() != map2.len() { if map1.len() != map2.len() {
return Ok(false); return Ok(false);
} }
if map1.is_empty() {
return Ok(true);
}
if !map1.is_empty() {
let mut map2 = map2; let mut map2 = map2;
for (m1, v1) in map1.iter_mut() { for (m1, v1) in map1.iter_mut() {
@ -72,6 +94,7 @@ mod map_functions {
return Ok(false); return Ok(false);
} }
} }
}
Ok(true) Ok(true)
} }
@ -88,11 +111,19 @@ mod map_functions {
pub mod indexing { pub mod indexing {
#[rhai_fn(pure)] #[rhai_fn(pure)]
pub fn keys(map: &mut Map) -> Array { pub fn keys(map: &mut Map) -> Array {
if map.is_empty() {
Array::new()
} else {
map.keys().cloned().map(Into::<Dynamic>::into).collect() map.keys().cloned().map(Into::<Dynamic>::into).collect()
} }
}
#[rhai_fn(pure)] #[rhai_fn(pure)]
pub fn values(map: &mut Map) -> Array { pub fn values(map: &mut Map) -> Array {
if map.is_empty() {
Array::new()
} else {
map.values().cloned().collect() map.values().cloned().collect()
} }
} }
}
} }

View File

@ -66,12 +66,20 @@ mod string_functions {
#[rhai_fn(name = "len", get = "len")] #[rhai_fn(name = "len", get = "len")]
pub fn len(string: &str) -> INT { pub fn len(string: &str) -> INT {
if string.is_empty() {
0
} else {
string.chars().count() as INT string.chars().count() as INT
} }
}
#[rhai_fn(name = "bytes", get = "bytes")] #[rhai_fn(name = "bytes", get = "bytes")]
pub fn bytes(string: &str) -> INT { pub fn bytes(string: &str) -> INT {
if string.is_empty() {
0
} else {
string.len() as INT string.len() as INT
} }
}
pub fn remove(string: &mut ImmutableString, sub_string: ImmutableString) { pub fn remove(string: &mut ImmutableString, sub_string: ImmutableString) {
*string -= sub_string; *string -= sub_string;
} }
@ -80,15 +88,17 @@ mod string_functions {
*string -= character; *string -= character;
} }
pub fn clear(string: &mut ImmutableString) { pub fn clear(string: &mut ImmutableString) {
if !string.is_empty() {
string.make_mut().clear(); string.make_mut().clear();
} }
}
pub fn truncate(string: &mut ImmutableString, len: INT) { pub fn truncate(string: &mut ImmutableString, len: INT) {
if len > 0 { if len > 0 {
let chars: StaticVec<_> = string.chars().collect(); let chars: StaticVec<_> = string.chars().collect();
let copy = string.make_mut(); let copy = string.make_mut();
copy.clear(); copy.clear();
copy.extend(chars.into_iter().take(len as usize)); copy.extend(chars.into_iter().take(len as usize));
} else { } else if !string.is_empty() {
string.make_mut().clear(); string.make_mut().clear();
} }
} }
@ -99,11 +109,14 @@ mod string_functions {
*string = trimmed.to_string().into(); *string = trimmed.to_string().into();
} }
} }
#[rhai_fn(return_raw)] pub fn pop(string: &mut ImmutableString) -> Dynamic {
pub fn pop(string: &mut ImmutableString) -> Result<char, Box<EvalAltResult>> { if string.is_empty() {
Dynamic::UNIT
} else {
match string.make_mut().pop() { match string.make_mut().pop() {
Some(c) => Ok(c.into()), Some(c) => c.into(),
None => EvalAltResult::ErrorStringBounds(0, 0, Position::NONE).into(), None => Dynamic::UNIT,
}
} }
} }
#[rhai_fn(name = "pop")] #[rhai_fn(name = "pop")]
@ -112,7 +125,7 @@ mod string_functions {
string: &mut ImmutableString, string: &mut ImmutableString,
len: INT, len: INT,
) -> ImmutableString { ) -> ImmutableString {
if len <= 0 { if string.is_empty() || len <= 0 {
return ctx.engine().empty_string.clone(); return ctx.engine().empty_string.clone();
} }
@ -128,17 +141,29 @@ mod string_functions {
chars.into_iter().rev().collect::<SmartString>().into() chars.into_iter().rev().collect::<SmartString>().into()
} }
pub fn to_upper(string: &str) -> ImmutableString { pub fn to_upper(string: ImmutableString) -> ImmutableString {
if string.is_empty() {
string
} else {
string.to_uppercase().into() string.to_uppercase().into()
} }
pub fn make_upper(string: &mut ImmutableString) {
*string = to_upper(string);
} }
pub fn to_lower(string: &str) -> ImmutableString { pub fn make_upper(string: &mut ImmutableString) {
if !string.is_empty() {
*string = string.to_uppercase().into();
}
}
pub fn to_lower(string: ImmutableString) -> ImmutableString {
if string.is_empty() {
string
} else {
string.to_lowercase().into() string.to_lowercase().into()
} }
}
pub fn make_lower(string: &mut ImmutableString) { pub fn make_lower(string: &mut ImmutableString) {
*string = to_lower(string); if !string.is_empty() {
*string = string.to_lowercase().into();
}
} }
#[rhai_fn(name = "to_upper")] #[rhai_fn(name = "to_upper")]
@ -174,6 +199,10 @@ mod string_functions {
#[rhai_fn(name = "index_of")] #[rhai_fn(name = "index_of")]
pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT { pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT {
if string.is_empty() {
return -1;
}
let start = if start < 0 { let start = if start < 0 {
if let Some(n) = start.checked_abs() { if let Some(n) = start.checked_abs() {
let chars: Vec<_> = string.chars().collect(); let chars: Vec<_> = string.chars().collect();
@ -209,13 +238,21 @@ mod string_functions {
} }
#[rhai_fn(name = "index_of")] #[rhai_fn(name = "index_of")]
pub fn index_of_char(string: &str, character: char) -> INT { pub fn index_of_char(string: &str, character: char) -> INT {
if string.is_empty() {
-1
} else {
string string
.find(character) .find(character)
.map(|index| string[0..index].chars().count() as INT) .map(|index| string[0..index].chars().count() as INT)
.unwrap_or(-1 as INT) .unwrap_or(-1 as INT)
} }
}
#[rhai_fn(name = "index_of")] #[rhai_fn(name = "index_of")]
pub fn index_of_string_starting_from(string: &str, find_string: &str, start: INT) -> INT { pub fn index_of_string_starting_from(string: &str, find_string: &str, start: INT) -> INT {
if string.is_empty() {
return -1;
}
let start = if start < 0 { let start = if start < 0 {
if let Some(n) = start.checked_abs() { if let Some(n) = start.checked_abs() {
let chars = string.chars().collect::<Vec<_>>(); let chars = string.chars().collect::<Vec<_>>();
@ -251,11 +288,15 @@ mod string_functions {
} }
#[rhai_fn(name = "index_of")] #[rhai_fn(name = "index_of")]
pub fn index_of(string: &str, find_string: &str) -> INT { pub fn index_of(string: &str, find_string: &str) -> INT {
if string.is_empty() {
-1
} else {
string string
.find(find_string) .find(find_string)
.map(|index| string[0..index].chars().count() as INT) .map(|index| string[0..index].chars().count() as INT)
.unwrap_or(-1 as INT) .unwrap_or(-1 as INT)
} }
}
pub fn sub_string( pub fn sub_string(
ctx: NativeCallContext, ctx: NativeCallContext,
@ -263,6 +304,10 @@ mod string_functions {
start: INT, start: INT,
len: INT, len: INT,
) -> ImmutableString { ) -> ImmutableString {
if string.is_empty() {
return ctx.engine().empty_string.clone();
}
let mut chars = StaticVec::with_capacity(string.len()); let mut chars = StaticVec::with_capacity(string.len());
let offset = if string.is_empty() || len <= 0 { let offset = if string.is_empty() || len <= 0 {
@ -308,12 +353,20 @@ mod string_functions {
string: &str, string: &str,
start: INT, start: INT,
) -> ImmutableString { ) -> ImmutableString {
if string.is_empty() {
ctx.engine().empty_string.clone()
} else {
let len = string.len() as INT; let len = string.len() as INT;
sub_string(ctx, string, start, len) sub_string(ctx, string, start, len)
} }
}
#[rhai_fn(name = "crop")] #[rhai_fn(name = "crop")]
pub fn crop(string: &mut ImmutableString, start: INT, len: INT) { pub fn crop(string: &mut ImmutableString, start: INT, len: INT) {
if string.is_empty() {
return;
}
let mut chars = StaticVec::with_capacity(string.len()); let mut chars = StaticVec::with_capacity(string.len());
let offset = if string.is_empty() || len <= 0 { let offset = if string.is_empty() || len <= 0 {
@ -358,34 +411,41 @@ mod string_functions {
#[rhai_fn(name = "replace")] #[rhai_fn(name = "replace")]
pub fn replace(string: &mut ImmutableString, find_string: &str, substitute_string: &str) { pub fn replace(string: &mut ImmutableString, find_string: &str, substitute_string: &str) {
if !string.is_empty() {
*string = string.replace(find_string, substitute_string).into(); *string = string.replace(find_string, substitute_string).into();
} }
}
#[rhai_fn(name = "replace")] #[rhai_fn(name = "replace")]
pub fn replace_string_with_char( pub fn replace_string_with_char(
string: &mut ImmutableString, string: &mut ImmutableString,
find_string: &str, find_string: &str,
substitute_character: char, substitute_character: char,
) { ) {
if !string.is_empty() {
*string = string *string = string
.replace(find_string, &substitute_character.to_string()) .replace(find_string, &substitute_character.to_string())
.into(); .into();
} }
}
#[rhai_fn(name = "replace")] #[rhai_fn(name = "replace")]
pub fn replace_char_with_string( pub fn replace_char_with_string(
string: &mut ImmutableString, string: &mut ImmutableString,
find_character: char, find_character: char,
substitute_string: &str, substitute_string: &str,
) { ) {
if !string.is_empty() {
*string = string *string = string
.replace(&find_character.to_string(), substitute_string) .replace(&find_character.to_string(), substitute_string)
.into(); .into();
} }
}
#[rhai_fn(name = "replace")] #[rhai_fn(name = "replace")]
pub fn replace_char( pub fn replace_char(
string: &mut ImmutableString, string: &mut ImmutableString,
find_character: char, find_character: char,
substitute_character: char, substitute_character: char,
) { ) {
if !string.is_empty() {
*string = string *string = string
.replace( .replace(
&find_character.to_string(), &find_character.to_string(),
@ -393,6 +453,7 @@ mod string_functions {
) )
.into(); .into();
} }
}
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn pad( pub fn pad(
@ -401,6 +462,10 @@ mod string_functions {
len: INT, len: INT,
character: char, character: char,
) -> Result<(), Box<crate::EvalAltResult>> { ) -> Result<(), Box<crate::EvalAltResult>> {
if len <= 0 {
return Ok(());
}
let _ctx = ctx; let _ctx = ctx;
// Check if string will be over max size limit // Check if string will be over max size limit
@ -413,7 +478,6 @@ mod string_functions {
.into(); .into();
} }
if len > 0 {
let orig_len = string.chars().count(); let orig_len = string.chars().count();
if len as usize > orig_len { if len as usize > orig_len {
@ -424,8 +488,7 @@ mod string_functions {
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_string_size() > 0 if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
&& string.len() > _ctx.engine().max_string_size()
{ {
return crate::EvalAltResult::ErrorDataTooLarge( return crate::EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
@ -434,7 +497,6 @@ mod string_functions {
.into(); .into();
} }
} }
}
Ok(()) Ok(())
} }
@ -445,6 +507,10 @@ mod string_functions {
len: INT, len: INT,
padding: &str, padding: &str,
) -> Result<(), Box<crate::EvalAltResult>> { ) -> Result<(), Box<crate::EvalAltResult>> {
if len <= 0 {
return Ok(());
}
let _ctx = ctx; let _ctx = ctx;
// Check if string will be over max size limit // Check if string will be over max size limit
@ -457,7 +523,6 @@ mod string_functions {
.into(); .into();
} }
if len > 0 {
let mut str_len = string.chars().count(); let mut str_len = string.chars().count();
let padding_len = padding.chars().count(); let padding_len = padding.chars().count();
@ -475,8 +540,7 @@ mod string_functions {
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_string_size() > 0 if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
&& string.len() > _ctx.engine().max_string_size()
{ {
return crate::EvalAltResult::ErrorDataTooLarge( return crate::EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
@ -485,7 +549,6 @@ mod string_functions {
.into(); .into();
} }
} }
}
Ok(()) Ok(())
} }
@ -496,8 +559,12 @@ mod string_functions {
#[rhai_fn(name = "split")] #[rhai_fn(name = "split")]
pub fn chars(string: &str) -> Array { pub fn chars(string: &str) -> Array {
if string.is_empty() {
Array::new()
} else {
string.chars().map(Into::<Dynamic>::into).collect() string.chars().map(Into::<Dynamic>::into).collect()
} }
}
#[rhai_fn(name = "split")] #[rhai_fn(name = "split")]
pub fn split_at(ctx: NativeCallContext, string: ImmutableString, start: INT) -> Array { pub fn split_at(ctx: NativeCallContext, string: ImmutableString, start: INT) -> Array {
if start <= 0 { if start <= 0 {