Build in operators between string and char.
This commit is contained in:
parent
f03983a9ca
commit
3f4dba9dbc
@ -22,7 +22,6 @@ New features
|
|||||||
|
|
||||||
* Functions are now allowed to have `Dynamic` arguments.
|
* Functions are now allowed to have `Dynamic` arguments.
|
||||||
* `#[rhai_fn(pure)]` attribute to mark a plugin function with `&mut` parameter as _pure_ so constants can be passed to it. Without it, passing a constant value into the `&mut` parameter will now raise an error.
|
* `#[rhai_fn(pure)]` attribute to mark a plugin function with `&mut` parameter as _pure_ so constants can be passed to it. Without it, passing a constant value into the `&mut` parameter will now raise an error.
|
||||||
* Comparisons between `FLOAT`/[`Decimal`](https://crates.io/crates/rust_decimal) and `INT` are now built in.
|
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
@ -31,8 +30,11 @@ Enhancements
|
|||||||
* Error position in `eval` statements is now wrapped in an `EvalAltResult::ErrorInFunctionCall`.
|
* Error position in `eval` statements is now wrapped in an `EvalAltResult::ErrorInFunctionCall`.
|
||||||
* `Position` now implements `Add` and `AddAssign`.
|
* `Position` now implements `Add` and `AddAssign`.
|
||||||
* `Scope` now implements `IntoIterator`.
|
* `Scope` now implements `IntoIterator`.
|
||||||
|
* Strings now have the `-`/`-=` operators and the `remove` method to delete a sub-string/character.
|
||||||
* Strings now have the `split_rev` method and variations of `split` with maximum number of segments.
|
* Strings now have the `split_rev` method and variations of `split` with maximum number of segments.
|
||||||
* Arrays now have the `split` method.
|
* Arrays now have the `split` method.
|
||||||
|
* Comparisons between `FLOAT`/[`Decimal`](https://crates.io/crates/rust_decimal) and `INT` are now built in.
|
||||||
|
* Comparisons between string and `char` are now built in.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.12
|
Version 0.19.12
|
||||||
|
114
src/fn_call.rs
114
src/fn_call.rs
@ -17,7 +17,6 @@ use crate::stdlib::{
|
|||||||
iter::{empty, once},
|
iter::{empty, once},
|
||||||
mem,
|
mem,
|
||||||
num::NonZeroU64,
|
num::NonZeroU64,
|
||||||
ops::Deref,
|
|
||||||
string::ToString,
|
string::ToString,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
@ -1470,28 +1469,60 @@ pub fn run_builtin_binary_op(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// char op string
|
||||||
|
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
|
let x = x.clone().cast::<char>();
|
||||||
|
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"+" => return Ok(Some(format!("{}{}", x, y).into())),
|
||||||
|
"==" | "!=" | ">" | ">=" | "<" | "<=" => {
|
||||||
|
let s1 = [x, '\0'];
|
||||||
|
let mut y = y.chars();
|
||||||
|
let s2 = [y.next().unwrap_or('\0'), y.next().unwrap_or('\0')];
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"==" => return Ok(Some((s1 == s2).into())),
|
||||||
|
"!=" => return Ok(Some((s1 != s2).into())),
|
||||||
|
">" => return Ok(Some((s1 > s2).into())),
|
||||||
|
">=" => return Ok(Some((s1 >= s2).into())),
|
||||||
|
"<" => return Ok(Some((s1 < s2).into())),
|
||||||
|
"<=" => return Ok(Some((s1 <= s2).into())),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// string op char
|
||||||
|
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
|
let x = &*x.read_lock::<ImmutableString>().unwrap();
|
||||||
|
let y = y.clone().cast::<char>();
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"+" => return Ok(Some((x + y).into())),
|
||||||
|
"-" => return Ok(Some((x - y).into())),
|
||||||
|
"==" | "!=" | ">" | ">=" | "<" | "<=" => {
|
||||||
|
let mut x = x.chars();
|
||||||
|
let s1 = [x.next().unwrap_or('\0'), x.next().unwrap_or('\0')];
|
||||||
|
let s2 = [y, '\0'];
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"==" => return Ok(Some((s1 == s2).into())),
|
||||||
|
"!=" => return Ok(Some((s1 != s2).into())),
|
||||||
|
">" => return Ok(Some((s1 > s2).into())),
|
||||||
|
">=" => return Ok(Some((s1 >= s2).into())),
|
||||||
|
"<" => return Ok(Some((s1 < s2).into())),
|
||||||
|
"<=" => return Ok(Some((s1 <= s2).into())),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default comparison operators for different types
|
||||||
if type2 != type1 {
|
if type2 != type1 {
|
||||||
// char op string
|
|
||||||
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
|
||||||
let x = x.clone().cast::<char>();
|
|
||||||
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+" => return Ok(Some(format!("{}{}", x, y).into())),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// string op char
|
|
||||||
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
|
||||||
let x = &*x.read_lock::<ImmutableString>().unwrap();
|
|
||||||
let y = y.clone().cast::<char>();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+" => return Ok(Some((x + y).into())),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Default comparison operators for different types
|
|
||||||
return Ok(match op {
|
return Ok(match op {
|
||||||
"!=" => Some(Dynamic::TRUE),
|
"!=" => Some(Dynamic::TRUE),
|
||||||
"==" | ">" | ">=" | "<" | "<=" => Some(Dynamic::FALSE),
|
"==" | ">" | ">=" | "<" | "<=" => Some(Dynamic::FALSE),
|
||||||
@ -1567,6 +1598,7 @@ pub fn run_builtin_binary_op(
|
|||||||
|
|
||||||
match op {
|
match op {
|
||||||
"+" => return Ok(Some((x + y).into())),
|
"+" => return Ok(Some((x + y).into())),
|
||||||
|
"-" => return Ok(Some((x - y).into())),
|
||||||
"==" => return Ok(Some((x == y).into())),
|
"==" => return Ok(Some((x == y).into())),
|
||||||
"!=" => return Ok(Some((x != y).into())),
|
"!=" => return Ok(Some((x != y).into())),
|
||||||
">" => return Ok(Some((x > y).into())),
|
">" => return Ok(Some((x > y).into())),
|
||||||
@ -1673,17 +1705,34 @@ pub fn run_builtin_op_assignment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if type2 != type1 {
|
// string op= char
|
||||||
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
let y = y.read_lock::<char>().unwrap().deref().clone();
|
let y = y.clone().cast::<char>();
|
||||||
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"+=" => return Ok(Some(*x += y)),
|
"+=" => return Ok(Some(*x += y)),
|
||||||
_ => return Ok(None),
|
"-=" => return Ok(Some(*x -= y)),
|
||||||
}
|
_ => return Ok(None),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// char op= string
|
||||||
|
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
|
let y = y.read_lock::<ImmutableString>().unwrap();
|
||||||
|
let mut ch = x.read_lock::<char>().unwrap().to_string();
|
||||||
|
let mut x = x.write_lock::<Dynamic>().unwrap();
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"+=" => {
|
||||||
|
ch.push_str(y.as_str());
|
||||||
|
return Ok(Some(*x = ch.into()));
|
||||||
|
}
|
||||||
|
_ => return Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No built-in op-assignments for different types.
|
||||||
|
if type2 != type1 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1741,7 +1790,7 @@ pub fn run_builtin_op_assignment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if type1 == TypeId::of::<char>() {
|
if type1 == TypeId::of::<char>() {
|
||||||
let y = y.read_lock::<char>().unwrap().deref().clone();
|
let y = y.clone().cast::<char>();
|
||||||
let mut x = x.write_lock::<Dynamic>().unwrap();
|
let mut x = x.write_lock::<Dynamic>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
@ -1751,11 +1800,12 @@ pub fn run_builtin_op_assignment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if type1 == TypeId::of::<ImmutableString>() {
|
if type1 == TypeId::of::<ImmutableString>() {
|
||||||
let y = y.read_lock::<ImmutableString>().unwrap().deref().clone();
|
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
||||||
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"+=" => return Ok(Some(*x += y)),
|
"+=" => return Ok(Some(*x += y)),
|
||||||
|
"-=" => return Ok(Some(*x -= y)),
|
||||||
_ => return Ok(None),
|
_ => return Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
mod string_functions {
|
mod string_functions {
|
||||||
use crate::ImmutableString;
|
use crate::ImmutableString;
|
||||||
|
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+", name = "append")]
|
||||||
pub fn add_append(string: &str, item: Dynamic) -> ImmutableString {
|
pub fn add_append(string: &str, item: Dynamic) -> ImmutableString {
|
||||||
format!("{}{}", string, item).into()
|
format!("{}{}", string, item).into()
|
||||||
}
|
}
|
||||||
@ -42,6 +42,13 @@ mod string_functions {
|
|||||||
pub fn len(string: &str) -> INT {
|
pub fn len(string: &str) -> INT {
|
||||||
string.chars().count() as INT
|
string.chars().count() as INT
|
||||||
}
|
}
|
||||||
|
pub fn remove(string: &mut ImmutableString, sub_string: ImmutableString) {
|
||||||
|
*string -= sub_string;
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "remove")]
|
||||||
|
pub fn remove_char(string: &mut ImmutableString, character: char) {
|
||||||
|
*string -= character;
|
||||||
|
}
|
||||||
pub fn clear(string: &mut ImmutableString) {
|
pub fn clear(string: &mut ImmutableString) {
|
||||||
string.make_mut().clear();
|
string.make_mut().clear();
|
||||||
}
|
}
|
||||||
@ -64,15 +71,15 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "contains")]
|
#[rhai_fn(name = "contains")]
|
||||||
pub fn contains_char(string: &str, ch: char) -> bool {
|
pub fn contains_char(string: &str, character: char) -> bool {
|
||||||
string.contains(ch)
|
string.contains(character)
|
||||||
}
|
}
|
||||||
pub fn contains(string: &str, find_string: &str) -> bool {
|
pub fn contains(string: &str, find_string: &str) -> bool {
|
||||||
string.contains(find_string)
|
string.contains(find_string)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_char_starting_from(string: &str, ch: char, start: INT) -> INT {
|
pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT {
|
||||||
let start = if start < 0 {
|
let start = if start < 0 {
|
||||||
0
|
0
|
||||||
} else if start as usize >= string.chars().count() {
|
} else if start as usize >= string.chars().count() {
|
||||||
@ -86,14 +93,14 @@ mod string_functions {
|
|||||||
};
|
};
|
||||||
|
|
||||||
string[start..]
|
string[start..]
|
||||||
.find(ch)
|
.find(character)
|
||||||
.map(|index| string[0..start + index].chars().count() as INT)
|
.map(|index| string[0..start + 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_char(string: &str, ch: char) -> INT {
|
pub fn index_of_char(string: &str, character: char) -> INT {
|
||||||
string
|
string
|
||||||
.find(ch)
|
.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)
|
||||||
}
|
}
|
||||||
@ -196,26 +203,33 @@ mod string_functions {
|
|||||||
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_char: char,
|
substitute_character: char,
|
||||||
) {
|
) {
|
||||||
*string = string
|
*string = string
|
||||||
.replace(find_string, &substitute_char.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_char: char,
|
find_character: char,
|
||||||
substitute_string: &str,
|
substitute_string: &str,
|
||||||
) {
|
) {
|
||||||
*string = string
|
*string = string
|
||||||
.replace(&find_char.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(string: &mut ImmutableString, find_char: char, substitute_char: char) {
|
pub fn replace_char(
|
||||||
|
string: &mut ImmutableString,
|
||||||
|
find_character: char,
|
||||||
|
substitute_character: char,
|
||||||
|
) {
|
||||||
*string = string
|
*string = string
|
||||||
.replace(&find_char.to_string(), &substitute_char.to_string())
|
.replace(
|
||||||
|
&find_character.to_string(),
|
||||||
|
&substitute_character.to_string(),
|
||||||
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +238,7 @@ mod string_functions {
|
|||||||
_ctx: NativeCallContext,
|
_ctx: NativeCallContext,
|
||||||
string: &mut ImmutableString,
|
string: &mut ImmutableString,
|
||||||
len: INT,
|
len: INT,
|
||||||
ch: char,
|
character: char,
|
||||||
) -> Result<Dynamic, Box<crate::EvalAltResult>> {
|
) -> Result<Dynamic, Box<crate::EvalAltResult>> {
|
||||||
// Check if string will be over max size limit
|
// Check if string will be over max size limit
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -243,7 +257,7 @@ mod string_functions {
|
|||||||
let p = string.make_mut();
|
let p = string.make_mut();
|
||||||
|
|
||||||
for _ in 0..(len as usize - orig_len) {
|
for _ in 0..(len as usize - orig_len) {
|
||||||
p.push(ch);
|
p.push(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
127
src/utils.rs
127
src/utils.rs
@ -12,7 +12,7 @@ use crate::stdlib::{
|
|||||||
hash::{BuildHasher, Hash, Hasher},
|
hash::{BuildHasher, Hash, Hasher},
|
||||||
iter::{empty, FromIterator},
|
iter::{empty, FromIterator},
|
||||||
num::NonZeroU64,
|
num::NonZeroU64,
|
||||||
ops::{Add, AddAssign, Deref, DerefMut},
|
ops::{Add, AddAssign, Deref, DerefMut, Sub, SubAssign},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
@ -468,6 +468,13 @@ impl Add<String> for &ImmutableString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AddAssign<String> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
|
fn add_assign(&mut self, rhs: String) {
|
||||||
|
self.make_mut().push_str(&rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Add<char> for ImmutableString {
|
impl Add<char> for ImmutableString {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
@ -496,6 +503,124 @@ impl AddAssign<char> for ImmutableString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Sub for ImmutableString {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs
|
||||||
|
} else {
|
||||||
|
self.replace(rhs.as_str(), "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for &ImmutableString {
|
||||||
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self.clone()
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs.clone()
|
||||||
|
} else {
|
||||||
|
self.replace(rhs.as_str(), "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign<&ImmutableString> for ImmutableString {
|
||||||
|
#[inline]
|
||||||
|
fn sub_assign(&mut self, rhs: &ImmutableString) {
|
||||||
|
if !rhs.is_empty() {
|
||||||
|
if self.is_empty() {
|
||||||
|
self.0 = rhs.0.clone();
|
||||||
|
} else {
|
||||||
|
self.0 = self.replace(rhs.as_str(), "").into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign<ImmutableString> for ImmutableString {
|
||||||
|
#[inline]
|
||||||
|
fn sub_assign(&mut self, rhs: ImmutableString) {
|
||||||
|
if !rhs.is_empty() {
|
||||||
|
if self.is_empty() {
|
||||||
|
self.0 = rhs.0;
|
||||||
|
} else {
|
||||||
|
self.0 = self.replace(rhs.as_str(), "").into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<String> for ImmutableString {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sub(self, rhs: String) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs.into()
|
||||||
|
} else {
|
||||||
|
self.replace(&rhs, "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<String> for &ImmutableString {
|
||||||
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sub(self, rhs: String) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self.clone()
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs.into()
|
||||||
|
} else {
|
||||||
|
self.replace(&rhs, "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign<String> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
|
fn sub_assign(&mut self, rhs: String) {
|
||||||
|
self.0 = self.replace(&rhs, "").into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<char> for ImmutableString {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn sub(self, rhs: char) -> Self::Output {
|
||||||
|
self.replace(rhs, "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<char> for &ImmutableString {
|
||||||
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn sub(self, rhs: char) -> Self::Output {
|
||||||
|
self.replace(rhs, "").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubAssign<char> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
|
fn sub_assign(&mut self, rhs: char) {
|
||||||
|
self.0 = self.replace(rhs, "").into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<S: AsRef<str>> PartialEq<S> for ImmutableString {
|
impl<S: AsRef<str>> PartialEq<S> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn eq(&self, other: &S) -> bool {
|
fn eq(&self, other: &S) -> bool {
|
||||||
|
@ -6,10 +6,10 @@ fn test_decrement() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("let x = 10; x -= 7; x")?, 3);
|
assert_eq!(engine.eval::<INT>("let x = 10; x -= 7; x")?, 3);
|
||||||
|
|
||||||
assert!(matches!(
|
assert_eq!(
|
||||||
*engine.eval::<String>(r#"let s = "test"; s -= "ing"; s"#).expect_err("expects error"),
|
engine.eval::<String>(r#"let s = "test"; s -= 's'; s"#)?,
|
||||||
EvalAltResult::ErrorFunctionNotFound(err, _) if err == "- (&str | ImmutableString | String, &str | ImmutableString | String)"
|
"tet"
|
||||||
));
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ fn test_increment() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(engine.eval::<INT>("let x = 1; x += 2; x")?, 3);
|
assert_eq!(engine.eval::<INT>("let x = 1; x += 2; x")?, 3);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<String>("let s = \"test\"; s += \"ing\"; s")?,
|
engine.eval::<String>(r#"let s = "test"; s += "ing"; s"#)?,
|
||||||
"testing"
|
"testing"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
14
tests/ops.rs
14
tests/ops.rs
@ -34,7 +34,19 @@ fn test_ops_numbers() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_op_precedence() -> Result<(), Box<EvalAltResult>> {
|
fn test_ops_strings() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>(r#""hello" > 'c'"#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#""" < 'c'"#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#"'x' > "hello""#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#""hello" > "foo""#)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ops_precedence() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -132,6 +132,19 @@ fn test_string_substring() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"❤❤ hello! ❤❤❤"
|
"❤❤ hello! ❤❤❤"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(
|
||||||
|
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x -= 'l'; x"#
|
||||||
|
)?,
|
||||||
|
"❤❤❤ heo! ❤❤❤"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(
|
||||||
|
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x -= "\u2764\u2764"; x"#
|
||||||
|
)?,
|
||||||
|
"❤ hello! ❤"
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.index_of('\u2764')"#
|
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.index_of('\u2764')"#
|
||||||
|
Loading…
Reference in New Issue
Block a user