Feature: Add function string::contains (#1906)

This commit is contained in:
Jan Tebernum 2023-05-03 12:40:17 +02:00 committed by GitHub
parent c3c6613e7a
commit 1cd00cf3fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 117 additions and 1 deletions

View file

@ -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("

View file

@ -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("

View file

@ -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,

View file

@ -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)))?;

View file

@ -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);
}
} }

View file

@ -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"),

View file

@ -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#"