Feature: Add function string::contains
(#1906)
This commit is contained in:
parent
c3c6613e7a
commit
1cd00cf3fc
7 changed files with 117 additions and 1 deletions
|
@ -274,6 +274,7 @@
|
||||||
# "sleep("
|
# "sleep("
|
||||||
"string"
|
"string"
|
||||||
"string::concat("
|
"string::concat("
|
||||||
|
"string::contains("
|
||||||
"string::endsWith("
|
"string::endsWith("
|
||||||
"string::join("
|
"string::join("
|
||||||
"string::len("
|
"string::len("
|
||||||
|
|
|
@ -273,6 +273,7 @@
|
||||||
"sleep("
|
"sleep("
|
||||||
"string"
|
"string"
|
||||||
"string::concat("
|
"string::concat("
|
||||||
|
"string::contains("
|
||||||
"string::endsWith("
|
"string::endsWith("
|
||||||
"string::join("
|
"string::join("
|
||||||
"string::len("
|
"string::len("
|
||||||
|
|
|
@ -201,6 +201,7 @@ pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec<Value>) -> Result<Va
|
||||||
"session::token" => session::token(ctx),
|
"session::token" => session::token(ctx),
|
||||||
//
|
//
|
||||||
"string::concat" => string::concat,
|
"string::concat" => string::concat,
|
||||||
|
"string::contains" => string::contains,
|
||||||
"string::endsWith" => string::ends_with,
|
"string::endsWith" => string::ends_with,
|
||||||
"string::join" => string::join,
|
"string::join" => string::join,
|
||||||
"string::len" => string::len,
|
"string::len" => string::len,
|
||||||
|
|
|
@ -19,6 +19,7 @@ impl ModuleDef for Package {
|
||||||
fn load<'js>(_ctx: Ctx<'js>, module: &Module<'js, Created>) -> Result<()> {
|
fn load<'js>(_ctx: Ctx<'js>, module: &Module<'js, Created>) -> Result<()> {
|
||||||
module.add("default")?;
|
module.add("default")?;
|
||||||
module.add("concat")?;
|
module.add("concat")?;
|
||||||
|
module.add("contains")?;
|
||||||
module.add("endsWith")?;
|
module.add("endsWith")?;
|
||||||
module.add("join")?;
|
module.add("join")?;
|
||||||
module.add("len")?;
|
module.add("len")?;
|
||||||
|
@ -39,6 +40,7 @@ impl ModuleDef for Package {
|
||||||
fn eval<'js>(ctx: Ctx<'js>, module: &Module<'js, Loaded<Native>>) -> Result<()> {
|
fn eval<'js>(ctx: Ctx<'js>, module: &Module<'js, Loaded<Native>>) -> Result<()> {
|
||||||
// Set specific exports
|
// Set specific exports
|
||||||
module.set("concat", Func::from(|v: Any| run("string::concat", v.0)))?;
|
module.set("concat", Func::from(|v: Any| run("string::concat", v.0)))?;
|
||||||
|
module.set("contains", Func::from(|v: Any| run("string::contains", v.0)))?;
|
||||||
module.set("endsWith", Func::from(|v: Any| run("string::endsWith", v.0)))?;
|
module.set("endsWith", Func::from(|v: Any| run("string::endsWith", v.0)))?;
|
||||||
module.set("join", Func::from(|v: Any| run("string::join", v.0)))?;
|
module.set("join", Func::from(|v: Any| run("string::join", v.0)))?;
|
||||||
module.set("len", Func::from(|v: Any| run("string::len", v.0)))?;
|
module.set("len", Func::from(|v: Any| run("string::len", v.0)))?;
|
||||||
|
@ -56,6 +58,7 @@ impl ModuleDef for Package {
|
||||||
// Set default export
|
// Set default export
|
||||||
let default = Object::new(ctx)?;
|
let default = Object::new(ctx)?;
|
||||||
default.set("concat", Func::from(|v: Any| run("string::concat", v.0)))?;
|
default.set("concat", Func::from(|v: Any| run("string::concat", v.0)))?;
|
||||||
|
default.set("contains", Func::from(|v: Any| run("string::contains", v.0)))?;
|
||||||
default.set("endsWith", Func::from(|v: Any| run("string::endsWith", v.0)))?;
|
default.set("endsWith", Func::from(|v: Any| run("string::endsWith", v.0)))?;
|
||||||
default.set("join", Func::from(|v: Any| run("string::join", v.0)))?;
|
default.set("join", Func::from(|v: Any| run("string::join", v.0)))?;
|
||||||
default.set("len", Func::from(|v: Any| run("string::len", v.0)))?;
|
default.set("len", Func::from(|v: Any| run("string::len", v.0)))?;
|
||||||
|
|
|
@ -6,6 +6,10 @@ pub fn concat(args: Vec<Value>) -> Result<Value, Error> {
|
||||||
Ok(args.into_iter().map(|x| x.as_string()).collect::<Vec<_>>().concat().into())
|
Ok(args.into_iter().map(|x| x.as_string()).collect::<Vec<_>>().concat().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains((val, check): (String, String)) -> Result<Value, Error> {
|
||||||
|
Ok(val.contains(&check).into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ends_with((val, chr): (String, String)) -> Result<Value, Error> {
|
pub fn ends_with((val, chr): (String, String)) -> Result<Value, Error> {
|
||||||
Ok(val.ends_with(&chr).into())
|
Ok(val.ends_with(&chr).into())
|
||||||
}
|
}
|
||||||
|
@ -107,7 +111,7 @@ pub fn words((string,): (String,)) -> Result<Value, Error> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::slice;
|
use super::{contains, slice};
|
||||||
use crate::sql::Value;
|
use crate::sql::Value;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -131,4 +135,22 @@ mod tests {
|
||||||
test(string, Some(-1), None, "界");
|
test(string, Some(-1), None, "界");
|
||||||
test(string, Some(-2), Some(1), "世");
|
test(string, Some(-2), Some(1), "世");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_contains() {
|
||||||
|
fn test(base: &str, contained: &str, expected: bool) {
|
||||||
|
assert_eq!(
|
||||||
|
contains((base.to_string(), contained.to_string())).unwrap(),
|
||||||
|
Value::from(expected)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
test("", "", true);
|
||||||
|
test("", "a", false);
|
||||||
|
test("a", "", true);
|
||||||
|
test("abcde", "bcd", true);
|
||||||
|
test("abcde", "cbcd", false);
|
||||||
|
test("好世界", "世", true);
|
||||||
|
test("好世界", "你好", false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -485,6 +485,7 @@ fn function_session(i: &str) -> IResult<&str, &str> {
|
||||||
fn function_string(i: &str) -> IResult<&str, &str> {
|
fn function_string(i: &str) -> IResult<&str, &str> {
|
||||||
alt((
|
alt((
|
||||||
tag("concat"),
|
tag("concat"),
|
||||||
|
tag("contains"),
|
||||||
tag("endsWith"),
|
tag("endsWith"),
|
||||||
tag("join"),
|
tag("join"),
|
||||||
tag("len"),
|
tag("len"),
|
||||||
|
|
|
@ -2947,6 +2947,93 @@ async fn function_string_concat() -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn function_string_contains() -> Result<(), Error> {
|
||||||
|
let sql = r#"
|
||||||
|
RETURN string::contains("", "");
|
||||||
|
RETURN string::contains("a", "");
|
||||||
|
RETURN string::contains("abcdefg", "");
|
||||||
|
RETURN string::contains("abcdefg", "bcd");
|
||||||
|
RETURN string::contains("abcdefg", "abcd");
|
||||||
|
RETURN string::contains("abcdefg", "xxabcd");
|
||||||
|
RETURN string::contains("abcdefg", "hij");
|
||||||
|
RETURN string::contains("ประเทศไทย中华", "ประเ");
|
||||||
|
RETURN string::contains("ประเทศไทย中华", "ะเ");
|
||||||
|
RETURN string::contains("ประเทศไทย中华", "ไท华");
|
||||||
|
RETURN string::contains("1234567ah012345678901ah", "hah");
|
||||||
|
RETURN string::contains("00abc01234567890123456789abc", "bcabc");
|
||||||
|
RETURN string::contains("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaba");
|
||||||
|
RETURN string::contains("* \t", " ");
|
||||||
|
RETURN string::contains("* \t", "?");
|
||||||
|
"#;
|
||||||
|
let dbs = Datastore::new("memory").await?;
|
||||||
|
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||||
|
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
|
||||||
|
assert_eq!(res.len(), 15);
|
||||||
|
// 1
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(true);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 2
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(true);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 3
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(true);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 4
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(true);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 5
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(true);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 6
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(false);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 7
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(false);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 8
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(true);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 9
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(true);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 10
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(false);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 11
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(false);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 12
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(false);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 13
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(false);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 14
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(true);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
// 15
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::Bool(false);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn function_string_ends_with() -> Result<(), Error> {
|
async fn function_string_ends_with() -> Result<(), Error> {
|
||||||
let sql = r#"
|
let sql = r#"
|
||||||
|
|
Loading…
Reference in a new issue