diff --git a/Cargo.lock b/Cargo.lock index b4c94f83..4176c330 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2623,9 +2623,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "storekey" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c486bc18d702687cb3265288657cfe89df0f2f1a88900f104a2cc86889bddd7" +checksum = "3c05ac79ed7dcd90b469801242eb9f348cd5749404a567bf36d2d6c3c5ff6698" dependencies = [ "byteorder", "serde", diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 4d5a95c5..be6d0700 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -43,7 +43,7 @@ regex = "1.5.6" msgpack = { version = "1.1.0", package = "rmp-serde" } scrypt = "0.10.0" serde = { version = "1.0.137", features = ["derive"] } -storekey = "0.2.0" +storekey = "0.3.0" sha-1 = "0.10.0" sha2 = "0.10.2" slug = "0.1.4" diff --git a/lib/src/fnc/rand.rs b/lib/src/fnc/rand.rs index 930f2181..26c7612e 100644 --- a/lib/src/fnc/rand.rs +++ b/lib/src/fnc/rand.rs @@ -2,11 +2,11 @@ use crate::cnf::ID_CHARS; use crate::ctx::Context; use crate::err::Error; use crate::sql::datetime::Datetime; +use crate::sql::uuid::Uuid; use crate::sql::value::Value; use nanoid::nanoid; use rand::distributions::Alphanumeric; use rand::Rng; -use uuid::Uuid; pub fn rand(_: &Context, _: Vec) -> Result { Ok(rand::random::().into()) @@ -147,5 +147,5 @@ pub fn time(_: &Context, mut args: Vec) -> Result { } pub fn uuid(_: &Context, _: Vec) -> Result { - Ok(Uuid::new_v4().to_string().into()) + Ok(Uuid::new().into()) } diff --git a/lib/src/sql/common.rs b/lib/src/sql/common.rs index 1ffa0b4d..231dbf2d 100644 --- a/lib/src/sql/common.rs +++ b/lib/src/sql/common.rs @@ -23,6 +23,11 @@ pub fn commas(i: &str) -> IResult<&str, ()> { Ok((i, ())) } +#[inline] +pub fn is_hex(chr: char) -> bool { + chr.is_ascii_hexdigit() +} + #[inline] pub fn is_digit(chr: char) -> bool { (0x30..=0x39).contains(&(chr as u8)) diff --git a/lib/src/sql/mod.rs b/lib/src/sql/mod.rs index 430475b9..006fdbf8 100644 --- a/lib/src/sql/mod.rs +++ b/lib/src/sql/mod.rs @@ -48,6 +48,7 @@ pub(crate) mod subquery; pub(crate) mod table; pub(crate) mod thing; pub(crate) mod timeout; +pub(crate) mod uuid; pub(crate) mod value; pub(crate) mod version; pub(crate) mod view; @@ -112,6 +113,7 @@ pub use self::table::Table; pub use self::table::Tables; pub use self::thing::Thing; pub use self::timeout::Timeout; +pub use self::uuid::Uuid; pub use self::value::Value; pub use self::value::Values; pub use self::version::Version; diff --git a/lib/src/sql/uuid.rs b/lib/src/sql/uuid.rs new file mode 100644 index 00000000..1c1f4872 --- /dev/null +++ b/lib/src/sql/uuid.rs @@ -0,0 +1,121 @@ +use crate::sql::common::is_hex; +use crate::sql::error::IResult; +use crate::sql::serde::is_internal_serialization; +use nom::branch::alt; +use nom::bytes::complete::take_while_m_n; +use nom::character::complete::char; +use nom::combinator::recognize; +use nom::sequence::delimited; +use nom::sequence::tuple; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::ops::Deref; +use std::str; + +const SINGLE: char = '\''; +const DOUBLE: char = '"'; + +#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Deserialize)] +pub struct Uuid(pub uuid::Uuid); + +impl From<&str> for Uuid { + fn from(s: &str) -> Self { + match uuid::Uuid::try_parse(s) { + Ok(v) => Uuid(v), + _ => Uuid::default(), + } + } +} + +impl From for Uuid { + fn from(s: String) -> Self { + match uuid::Uuid::try_parse(&s) { + Ok(v) => Uuid(v), + _ => Uuid::default(), + } + } +} + +impl Deref for Uuid { + type Target = uuid::Uuid; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Uuid { + pub fn new() -> Self { + Uuid(uuid::Uuid::new_v4()) + } + pub fn to_raw(&self) -> String { + self.0.to_string() + } +} + +impl fmt::Display for Uuid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "\"{}\"", self.0) + } +} + +impl Serialize for Uuid { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if is_internal_serialization() { + serializer.serialize_newtype_struct("Uuid", &self.0) + } else { + serializer.serialize_some(&self.0) + } + } +} + +pub fn uuid(i: &str) -> IResult<&str, Uuid> { + alt(( + delimited(char(DOUBLE), uuid_raw, char(DOUBLE)), + delimited(char(SINGLE), uuid_raw, char(SINGLE)), + ))(i) +} + +fn uuid_raw(i: &str) -> IResult<&str, Uuid> { + let (i, v) = recognize(tuple(( + take_while_m_n(8, 8, is_hex), + char('-'), + take_while_m_n(4, 4, is_hex), + char('-'), + alt((char('1'), char('2'), char('3'), char('4'))), + take_while_m_n(3, 3, is_hex), + char('-'), + take_while_m_n(4, 4, is_hex), + char('-'), + take_while_m_n(12, 12, is_hex), + )))(i)?; + Ok((i, Uuid::from(v))) +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn uuid_v1() { + let sql = "e72bee20-f49b-11ec-b939-0242ac120002"; + let res = uuid_raw(sql); + assert!(res.is_ok()); + let out = res.unwrap().1; + assert_eq!("\"e72bee20-f49b-11ec-b939-0242ac120002\"", format!("{}", out)); + assert_eq!(out, Uuid::from("e72bee20-f49b-11ec-b939-0242ac120002")); + } + + #[test] + fn uuid_v4() { + let sql = "b19bc00b-aa98-486c-ae37-c8e1c54295b1"; + let res = uuid_raw(sql); + assert!(res.is_ok()); + let out = res.unwrap().1; + assert_eq!("\"b19bc00b-aa98-486c-ae37-c8e1c54295b1\"", format!("{}", out)); + assert_eq!(out, Uuid::from("b19bc00b-aa98-486c-ae37-c8e1c54295b1")); + } +} diff --git a/lib/src/sql/value/value.rs b/lib/src/sql/value/value.rs index e0f15bb5..e6affddc 100644 --- a/lib/src/sql/value/value.rs +++ b/lib/src/sql/value/value.rs @@ -28,6 +28,7 @@ use crate::sql::strand::{strand, Strand}; use crate::sql::subquery::{subquery, Subquery}; use crate::sql::table::{table, Table}; use crate::sql::thing::{thing, Thing}; +use crate::sql::uuid::{uuid as unique, Uuid}; use async_recursion::async_recursion; use bigdecimal::BigDecimal; use chrono::{DateTime, Utc}; @@ -101,6 +102,7 @@ pub enum Value { Strand(Strand), Duration(Duration), Datetime(Datetime), + Uuid(Uuid), Array(Array), Object(Object), Geometry(Geometry), @@ -140,6 +142,12 @@ impl From for Value { } } +impl From for Value { + fn from(v: Uuid) -> Self { + Value::Uuid(v) + } +} + impl From for Value { fn from(v: Param) -> Self { Value::Param(v) @@ -823,6 +831,10 @@ impl Value { }, _ => false, }, + Value::Uuid(v) => match other { + Value::Uuid(w) => v == w, + _ => false, + }, Value::Array(v) => match other { Value::Array(w) => v == w, _ => false, @@ -989,6 +1001,7 @@ impl fmt::Display for Value { Value::Strand(v) => write!(f, "{}", v), Value::Duration(v) => write!(f, "{}", v), Value::Datetime(v) => write!(f, "{}", v), + Value::Uuid(v) => write!(f, "{}", v), Value::Array(v) => write!(f, "{}", v), Value::Object(v) => write!(f, "{}", v), Value::Geometry(v) => write!(f, "{}", v), @@ -1061,19 +1074,20 @@ impl Serialize for Value { Value::Strand(v) => s.serialize_newtype_variant("Value", 6, "Strand", v), Value::Duration(v) => s.serialize_newtype_variant("Value", 7, "Duration", v), Value::Datetime(v) => s.serialize_newtype_variant("Value", 8, "Datetime", v), - Value::Array(v) => s.serialize_newtype_variant("Value", 9, "Array", v), - Value::Object(v) => s.serialize_newtype_variant("Value", 10, "Object", v), - Value::Geometry(v) => s.serialize_newtype_variant("Value", 11, "Geometry", v), - Value::Param(v) => s.serialize_newtype_variant("Value", 12, "Param", v), - Value::Idiom(v) => s.serialize_newtype_variant("Value", 13, "Idiom", v), - Value::Table(v) => s.serialize_newtype_variant("Value", 14, "Table", v), - Value::Thing(v) => s.serialize_newtype_variant("Value", 15, "Thing", v), - Value::Model(v) => s.serialize_newtype_variant("Value", 16, "Model", v), - Value::Regex(v) => s.serialize_newtype_variant("Value", 17, "Regex", v), - Value::Edges(v) => s.serialize_newtype_variant("Value", 18, "Edges", v), - Value::Function(v) => s.serialize_newtype_variant("Value", 19, "Function", v), - Value::Subquery(v) => s.serialize_newtype_variant("Value", 20, "Subquery", v), - Value::Expression(v) => s.serialize_newtype_variant("Value", 21, "Expression", v), + Value::Uuid(v) => s.serialize_newtype_variant("Value", 9, "Uuid", v), + Value::Array(v) => s.serialize_newtype_variant("Value", 10, "Array", v), + Value::Object(v) => s.serialize_newtype_variant("Value", 11, "Object", v), + Value::Geometry(v) => s.serialize_newtype_variant("Value", 12, "Geometry", v), + Value::Param(v) => s.serialize_newtype_variant("Value", 13, "Param", v), + Value::Idiom(v) => s.serialize_newtype_variant("Value", 14, "Idiom", v), + Value::Table(v) => s.serialize_newtype_variant("Value", 15, "Table", v), + Value::Thing(v) => s.serialize_newtype_variant("Value", 16, "Thing", v), + Value::Model(v) => s.serialize_newtype_variant("Value", 17, "Model", v), + Value::Regex(v) => s.serialize_newtype_variant("Value", 18, "Regex", v), + Value::Edges(v) => s.serialize_newtype_variant("Value", 19, "Edges", v), + Value::Function(v) => s.serialize_newtype_variant("Value", 20, "Function", v), + Value::Subquery(v) => s.serialize_newtype_variant("Value", 21, "Subquery", v), + Value::Expression(v) => s.serialize_newtype_variant("Value", 22, "Expression", v), } } else { match self { @@ -1083,6 +1097,7 @@ impl Serialize for Value { Value::True => s.serialize_bool(true), Value::False => s.serialize_bool(false), Value::Thing(v) => s.serialize_some(v), + Value::Uuid(v) => s.serialize_some(v), Value::Array(v) => s.serialize_some(v), Value::Object(v) => s.serialize_some(v), Value::Number(v) => s.serialize_some(v), @@ -1164,6 +1179,7 @@ pub fn single(i: &str) -> IResult<&str, Value> { map(datetime, Value::from), map(duration, Value::from), map(geometry, Value::from), + map(unique, Value::from), map(number, Value::from), map(strand, Value::from), map(object, Value::from), @@ -1188,6 +1204,7 @@ pub fn select(i: &str) -> IResult<&str, Value> { map(datetime, Value::from), map(duration, Value::from), map(geometry, Value::from), + map(unique, Value::from), map(number, Value::from), map(strand, Value::from), map(object, Value::from), @@ -1221,6 +1238,7 @@ pub fn json(i: &str) -> IResult<&str, Value> { map(datetime, Value::from), map(duration, Value::from), map(geometry, Value::from), + map(unique, Value::from), map(number, Value::from), map(object, Value::from), map(array, Value::from),