From 6d2f4a983337ba70f2dca58505d36458be0a7565 Mon Sep 17 00:00:00 2001 From: Micha de Vries Date: Mon, 5 Aug 2024 16:46:08 +0200 Subject: [PATCH] Improve `string::is::datetime` and `string::is::uuid`, Add `string::is::record` (#4462) --- core/src/fnc/mod.rs | 1 + .../modules/surrealdb/functions/string/is.rs | 3 +- core/src/fnc/string.rs | 36 ++++++++++++++----- core/src/syn/parser/builtin.rs | 1 + lib/fuzz/fuzz_targets/fuzz_executor.dict | 1 + lib/fuzz/fuzz_targets/fuzz_sql_parser.dict | 1 + lib/tests/function.rs | 21 ++++++++++- 7 files changed, 54 insertions(+), 10 deletions(-) diff --git a/core/src/fnc/mod.rs b/core/src/fnc/mod.rs index 2c77381c..5fb40efb 100644 --- a/core/src/fnc/mod.rs +++ b/core/src/fnc/mod.rs @@ -300,6 +300,7 @@ pub fn synchronous( "string::is::semver" => string::is::semver, "string::is::url" => string::is::url, "string::is::uuid" => string::is::uuid, + "string::is::record" => string::is::record, "string::similarity::fuzzy" => string::similarity::fuzzy, "string::similarity::jaro" => string::similarity::jaro, "string::similarity::smithwaterman" => string::similarity::smithwaterman, diff --git a/core/src/fnc/script/modules/surrealdb/functions/string/is.rs b/core/src/fnc/script/modules/surrealdb/functions/string/is.rs index 2759e944..54a4d129 100644 --- a/core/src/fnc/script/modules/surrealdb/functions/string/is.rs +++ b/core/src/fnc/script/modules/surrealdb/functions/string/is.rs @@ -23,5 +23,6 @@ impl_module_def!( "numeric" => run, "semver" => run, "url" => run, - "uuid" => run + "uuid" => run, + "record" => run ); diff --git a/core/src/fnc/string.rs b/core/src/fnc/string.rs index ea44fdca..e56d2779 100644 --- a/core/src/fnc/string.rs +++ b/core/src/fnc/string.rs @@ -183,6 +183,7 @@ pub mod html { pub mod is { use crate::err::Error; use crate::sql::value::Value; + use crate::sql::{Datetime, Thing}; use chrono::NaiveDateTime; use once_cell::sync::Lazy; use regex::Regex; @@ -207,8 +208,11 @@ pub mod is { Ok(arg.is_ascii().into()) } - pub fn datetime((arg, fmt): (String, String)) -> Result { - Ok(NaiveDateTime::parse_from_str(&arg, &fmt).is_ok().into()) + pub fn datetime((arg, fmt): (String, Option)) -> Result { + Ok(match fmt { + Some(fmt) => NaiveDateTime::parse_from_str(&arg, &fmt).is_ok().into(), + None => Datetime::try_from(arg.as_ref()).is_ok().into(), + }) } pub fn domain((arg,): (String,)) -> Result { @@ -255,13 +259,29 @@ pub mod is { Ok(Url::parse(&arg).is_ok().into()) } - pub fn uuid((arg,): (Value,)) -> Result { - Ok(match arg { - Value::Strand(v) => Uuid::parse_str(v.as_string().as_str()).is_ok(), - Value::Uuid(_) => true, + pub fn uuid((arg,): (String,)) -> Result { + Ok(Uuid::parse_str(arg.as_ref()).is_ok().into()) + } + + pub fn record((arg, tb): (String, Option)) -> Result { + let res = match Thing::try_from(arg) { + Ok(t) => match tb { + Some(Value::Strand(tb)) => t.tb == *tb, + Some(Value::Table(tb)) => t.tb == tb.0, + Some(_) => { + return Err(Error::InvalidArguments { + name: "string::is::record()".into(), + message: + "Expected an optional string or table type for the second argument" + .into(), + }) + } + None => true, + }, _ => false, - } - .into()) + }; + + Ok(res.into()) } } diff --git a/core/src/syn/parser/builtin.rs b/core/src/syn/parser/builtin.rs index 987d85d6..c7de8ace 100644 --- a/core/src/syn/parser/builtin.rs +++ b/core/src/syn/parser/builtin.rs @@ -288,6 +288,7 @@ pub(crate) static PATHS: phf::Map, PathKind> = phf_map! { UniCase::ascii("string::is::semver") => PathKind::Function, UniCase::ascii("string::is::url") => PathKind::Function, UniCase::ascii("string::is::uuid") => PathKind::Function, + UniCase::ascii("string::is::record") => PathKind::Function, UniCase::ascii("string::semver::compare") => PathKind::Function, UniCase::ascii("string::semver::major") => PathKind::Function, UniCase::ascii("string::semver::minor") => PathKind::Function, diff --git a/lib/fuzz/fuzz_targets/fuzz_executor.dict b/lib/fuzz/fuzz_targets/fuzz_executor.dict index fa4c9c24..f39bddab 100644 --- a/lib/fuzz/fuzz_targets/fuzz_executor.dict +++ b/lib/fuzz/fuzz_targets/fuzz_executor.dict @@ -312,6 +312,7 @@ "string::is::semver(" "string::is::url(" "string::is::uuid(" +"string::is::record(" "string::join(" "string::len(" "string::lowercase(" diff --git a/lib/fuzz/fuzz_targets/fuzz_sql_parser.dict b/lib/fuzz/fuzz_targets/fuzz_sql_parser.dict index 12cca8bc..540e93cc 100644 --- a/lib/fuzz/fuzz_targets/fuzz_sql_parser.dict +++ b/lib/fuzz/fuzz_targets/fuzz_sql_parser.dict @@ -310,6 +310,7 @@ "string::is::semver(" "string::is::url(" "string::is::uuid(" +"string::is::record(" "string::join(" "string::len(" "string::lowercase(" diff --git a/lib/tests/function.rs b/lib/tests/function.rs index 5c24fe36..6dc242b6 100644 --- a/lib/tests/function.rs +++ b/lib/tests/function.rs @@ -3670,7 +3670,7 @@ async fn function_parse_is_url() -> Result<(), Error> { #[tokio::test] async fn function_parse_is_uuid() -> Result<(), Error> { let sql = r#" - RETURN string::is::uuid(u"e72bee20-f49b-11ec-b939-0242ac120002"); + RETURN string::is::uuid("e72bee20-f49b-11ec-b939-0242ac120002"); RETURN string::is::uuid("this is a test!"); "#; let mut test = Test::new(sql).await?; @@ -3686,6 +3686,25 @@ async fn function_parse_is_uuid() -> Result<(), Error> { Ok(()) } +#[tokio::test] +async fn function_parse_is_record() -> Result<(), Error> { + let sql = r#" + RETURN string::is::record("test:123"); + RETURN string::is::record("invalid record id!"); + "#; + let mut test = Test::new(sql).await?; + // + let tmp = test.next()?.result?; + let val = Value::Bool(true); + assert_eq!(tmp, val); + // + let tmp = test.next()?.result?; + let val = Value::Bool(false); + assert_eq!(tmp, val); + // + Ok(()) +} + #[tokio::test] async fn function_string_join() -> Result<(), Error> { let sql = r#"