Store parsed regex::Regex in Value. (#1837)
This commit is contained in:
parent
bc7471a6ad
commit
f159187dbe
4 changed files with 98 additions and 53 deletions
|
@ -66,10 +66,10 @@ pub fn point((arg1, arg2): (Value, Option<Value>)) -> Result<Value, Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn regex((arg,): (Value,)) -> Result<Value, Error> {
|
pub fn regex((arg,): (Value,)) -> Result<Value, Error> {
|
||||||
match arg {
|
Ok(match arg {
|
||||||
Value::Strand(v) => Ok(Value::Regex(v.as_str().into())),
|
Value::Strand(v) => v.parse().map(Value::Regex).unwrap_or(Value::None),
|
||||||
_ => Ok(Value::None),
|
_ => Value::None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn string((arg,): (Strand,)) -> Result<Value, Error> {
|
pub fn string((arg,): (Strand,)) -> Result<Value, Error> {
|
||||||
|
|
|
@ -4,59 +4,120 @@ use nom::bytes::complete::escaped;
|
||||||
use nom::bytes::complete::is_not;
|
use nom::bytes::complete::is_not;
|
||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{
|
||||||
use std::fmt;
|
de::{self, Visitor},
|
||||||
use std::ops::Deref;
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
|
};
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Regex";
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Regex";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Deserialize, Hash)]
|
#[derive(Clone)]
|
||||||
pub struct Regex(pub(super) String);
|
pub struct Regex(pub(super) regex::Regex);
|
||||||
|
|
||||||
impl From<&str> for Regex {
|
impl Regex {
|
||||||
fn from(r: &str) -> Self {
|
// Deref would expose `regex::Regex::as_str` which wouldn't have the '/' delimiters.
|
||||||
Self(r.replace("\\/", "/"))
|
pub fn regex(&self) -> ®ex::Regex {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Regex {
|
|
||||||
type Target = String;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Regex {
|
impl FromStr for Regex {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
type Err = <regex::Regex as FromStr>::Err;
|
||||||
write!(f, "/{}/", &self.0)
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
regex::Regex::new(&s.replace("\\/", "/")).map(Self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Regex {
|
impl PartialEq for Regex {
|
||||||
pub fn regex(&self) -> Option<regex::Regex> {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
regex::Regex::new(&self.0).ok()
|
self.0.as_str().eq(other.0.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Regex {}
|
||||||
|
|
||||||
|
impl Ord for Regex {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.0.as_str().cmp(other.0.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Regex {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Regex {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.0.as_str().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Regex {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Regex {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "/{}/", &self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Regex {
|
impl Serialize for Regex {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: serde::Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
if is_internal_serialization() {
|
if is_internal_serialization() {
|
||||||
serializer.serialize_newtype_struct(TOKEN, &self.0)
|
serializer.serialize_newtype_struct(TOKEN, self.0.as_str())
|
||||||
} else {
|
} else {
|
||||||
serializer.serialize_none()
|
serializer.serialize_none()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Regex {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct RegexVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for RegexVisitor {
|
||||||
|
type Value = Regex;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a regex str")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Regex::from_str(value).map_err(|_| de::Error::custom("invalid regex"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_str(RegexVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn regex(i: &str) -> IResult<&str, Regex> {
|
pub fn regex(i: &str) -> IResult<&str, Regex> {
|
||||||
let (i, _) = char('/')(i)?;
|
let (i, _) = char('/')(i)?;
|
||||||
let (i, v) = escaped(is_not("\\/"), '\\', anychar)(i)?;
|
let (i, v) = escaped(is_not("\\/"), '\\', anychar)(i)?;
|
||||||
let (i, _) = char('/')(i)?;
|
let (i, _) = char('/')(i)?;
|
||||||
Ok((i, Regex::from(v)))
|
let regex = v.parse().map_err(|_| nom::Err::Error(crate::sql::Error::Parser(v)))?;
|
||||||
|
Ok((i, regex))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -71,7 +132,7 @@ mod tests {
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let out = res.unwrap().1;
|
let out = res.unwrap().1;
|
||||||
assert_eq!("/test/", format!("{}", out));
|
assert_eq!("/test/", format!("{}", out));
|
||||||
assert_eq!(out, Regex::from("test"));
|
assert_eq!(out, "test".parse().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -81,6 +142,6 @@ mod tests {
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let out = res.unwrap().1;
|
let out = res.unwrap().1;
|
||||||
assert_eq!(r"/(?i)test/[a-z]+/\s\d\w{1}.*/", format!("{}", out));
|
assert_eq!(r"/(?i)test/[a-z]+/\s\d\w{1}.*/", format!("{}", out));
|
||||||
assert_eq!(out, Regex::from(r"(?i)test/[a-z]+/\s\d\w{1}.*"));
|
assert_eq!(out, r"(?i)test/[a-z]+/\s\d\w{1}.*".parse().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ use crate::sql::Future;
|
||||||
use crate::sql::Ident;
|
use crate::sql::Ident;
|
||||||
use crate::sql::Idiom;
|
use crate::sql::Idiom;
|
||||||
use crate::sql::Param;
|
use crate::sql::Param;
|
||||||
use crate::sql::Regex;
|
|
||||||
use crate::sql::Strand;
|
use crate::sql::Strand;
|
||||||
use crate::sql::Table;
|
use crate::sql::Table;
|
||||||
use crate::sql::Uuid;
|
use crate::sql::Uuid;
|
||||||
|
@ -211,7 +210,7 @@ impl ser::Serializer for Serializer {
|
||||||
value.serialize(ser::block::entry::vec::Serializer.wrap())?,
|
value.serialize(ser::block::entry::vec::Serializer.wrap())?,
|
||||||
))))),
|
))))),
|
||||||
sql::regex::TOKEN => {
|
sql::regex::TOKEN => {
|
||||||
Ok(Value::Regex(Regex(value.serialize(ser::string::Serializer.wrap())?)))
|
Ok(Value::Regex(value.serialize(ser::string::Serializer.wrap())?.parse().unwrap()))
|
||||||
}
|
}
|
||||||
sql::table::TOKEN => {
|
sql::table::TOKEN => {
|
||||||
Ok(Value::Table(Table(value.serialize(ser::string::Serializer.wrap())?)))
|
Ok(Value::Table(Table(value.serialize(ser::string::Serializer.wrap())?)))
|
||||||
|
@ -743,7 +742,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn regex() {
|
fn regex() {
|
||||||
let regex = Regex::default();
|
let regex = "abc".parse().unwrap();
|
||||||
let value = to_value(®ex).unwrap();
|
let value = to_value(®ex).unwrap();
|
||||||
let expected = Value::Regex(regex);
|
let expected = Value::Regex(regex);
|
||||||
assert_eq!(value, expected);
|
assert_eq!(value, expected);
|
||||||
|
|
|
@ -1179,22 +1179,13 @@ impl Value {
|
||||||
Value::False => other.is_false(),
|
Value::False => other.is_false(),
|
||||||
Value::Thing(v) => match other {
|
Value::Thing(v) => match other {
|
||||||
Value::Thing(w) => v == w,
|
Value::Thing(w) => v == w,
|
||||||
Value::Regex(w) => match w.regex() {
|
Value::Regex(w) => w.regex().is_match(v.to_string().as_str()),
|
||||||
Some(ref r) => r.is_match(v.to_string().as_str()),
|
|
||||||
None => false,
|
|
||||||
},
|
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
Value::Regex(v) => match other {
|
Value::Regex(v) => match other {
|
||||||
Value::Regex(w) => v == w,
|
Value::Regex(w) => v == w,
|
||||||
Value::Number(w) => match v.regex() {
|
Value::Number(w) => v.regex().is_match(w.to_string().as_str()),
|
||||||
Some(ref r) => r.is_match(w.to_string().as_str()),
|
Value::Strand(w) => v.regex().is_match(w.as_str()),
|
||||||
None => false,
|
|
||||||
},
|
|
||||||
Value::Strand(w) => match v.regex() {
|
|
||||||
Some(ref r) => r.is_match(w.as_str()),
|
|
||||||
None => false,
|
|
||||||
},
|
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
Value::Uuid(v) => match other {
|
Value::Uuid(v) => match other {
|
||||||
|
@ -1211,19 +1202,13 @@ impl Value {
|
||||||
},
|
},
|
||||||
Value::Strand(v) => match other {
|
Value::Strand(v) => match other {
|
||||||
Value::Strand(w) => v == w,
|
Value::Strand(w) => v == w,
|
||||||
Value::Regex(w) => match w.regex() {
|
Value::Regex(w) => w.regex().is_match(v.as_str()),
|
||||||
Some(ref r) => r.is_match(v.as_str()),
|
|
||||||
None => false,
|
|
||||||
},
|
|
||||||
_ => v == &other.to_strand(),
|
_ => v == &other.to_strand(),
|
||||||
},
|
},
|
||||||
Value::Number(v) => match other {
|
Value::Number(v) => match other {
|
||||||
Value::Number(w) => v == w,
|
Value::Number(w) => v == w,
|
||||||
Value::Strand(_) => v == &other.to_number(),
|
Value::Strand(_) => v == &other.to_number(),
|
||||||
Value::Regex(w) => match w.regex() {
|
Value::Regex(w) => w.regex().is_match(v.to_string().as_str()),
|
||||||
Some(ref r) => r.is_match(v.to_string().as_str()),
|
|
||||||
None => false,
|
|
||||||
},
|
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
Value::Geometry(v) => match other {
|
Value::Geometry(v) => match other {
|
||||||
|
@ -1886,7 +1871,7 @@ mod tests {
|
||||||
assert_eq!(24, std::mem::size_of::<crate::sql::table::Table>());
|
assert_eq!(24, std::mem::size_of::<crate::sql::table::Table>());
|
||||||
assert_eq!(56, std::mem::size_of::<crate::sql::thing::Thing>());
|
assert_eq!(56, std::mem::size_of::<crate::sql::thing::Thing>());
|
||||||
assert_eq!(48, std::mem::size_of::<crate::sql::model::Model>());
|
assert_eq!(48, std::mem::size_of::<crate::sql::model::Model>());
|
||||||
assert_eq!(24, std::mem::size_of::<crate::sql::regex::Regex>());
|
assert_eq!(16, std::mem::size_of::<crate::sql::regex::Regex>());
|
||||||
assert_eq!(8, std::mem::size_of::<Box<crate::sql::range::Range>>());
|
assert_eq!(8, std::mem::size_of::<Box<crate::sql::range::Range>>());
|
||||||
assert_eq!(8, std::mem::size_of::<Box<crate::sql::edges::Edges>>());
|
assert_eq!(8, std::mem::size_of::<Box<crate::sql::edges::Edges>>());
|
||||||
assert_eq!(8, std::mem::size_of::<Box<crate::sql::function::Function>>());
|
assert_eq!(8, std::mem::size_of::<Box<crate::sql::function::Function>>());
|
||||||
|
|
Loading…
Reference in a new issue