2022-05-14 12:35:08 +00:00
|
|
|
use crate::ctx::Context;
|
2023-07-06 14:57:42 +00:00
|
|
|
use crate::dbs::{Options, Transaction};
|
|
|
|
use crate::doc::CursorDoc;
|
2021-03-29 15:43:37 +00:00
|
|
|
use crate::err::Error;
|
2020-06-29 15:36:01 +00:00
|
|
|
use crate::sql::comment::mightbespace;
|
2022-05-15 08:34:29 +00:00
|
|
|
use crate::sql::common::{commas, val_char};
|
2022-01-16 20:31:50 +00:00
|
|
|
use crate::sql::error::IResult;
|
2022-05-15 08:34:29 +00:00
|
|
|
use crate::sql::escape::escape_key;
|
2023-01-19 09:53:33 +00:00
|
|
|
use crate::sql::fmt::{is_pretty, pretty_indent, Fmt, Pretty};
|
2022-01-22 21:16:13 +00:00
|
|
|
use crate::sql::operation::{Op, Operation};
|
2022-05-30 15:32:26 +00:00
|
|
|
use crate::sql::thing::Thing;
|
2022-01-13 17:36:41 +00:00
|
|
|
use crate::sql::value::{value, Value};
|
2020-06-29 15:36:01 +00:00
|
|
|
use nom::branch::alt;
|
|
|
|
use nom::bytes::complete::is_not;
|
|
|
|
use nom::bytes::complete::take_while1;
|
2022-03-16 23:52:25 +00:00
|
|
|
use nom::character::complete::char;
|
2020-06-29 15:36:01 +00:00
|
|
|
use nom::combinator::opt;
|
2021-03-29 15:43:37 +00:00
|
|
|
use nom::multi::separated_list0;
|
2020-06-29 15:36:01 +00:00
|
|
|
use nom::sequence::delimited;
|
|
|
|
use serde::{Deserialize, Serialize};
|
2022-01-13 17:36:41 +00:00
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use std::collections::HashMap;
|
2023-01-19 09:53:33 +00:00
|
|
|
use std::fmt::{self, Display, Formatter, Write};
|
2022-05-04 13:09:57 +00:00
|
|
|
use std::ops::Deref;
|
|
|
|
use std::ops::DerefMut;
|
2020-06-29 15:36:01 +00:00
|
|
|
|
2023-03-30 10:41:44 +00:00
|
|
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Object";
|
|
|
|
|
2023-05-09 17:48:14 +00:00
|
|
|
/// Invariant: Keys never contain NUL bytes.
|
2023-04-29 15:58:22 +00:00
|
|
|
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
|
|
|
#[serde(rename = "$surrealdb::private::sql::Object")]
|
2023-05-09 17:48:14 +00:00
|
|
|
pub struct Object(#[serde(with = "no_nul_bytes_in_keys")] pub BTreeMap<String, Value>);
|
2022-01-13 17:36:41 +00:00
|
|
|
|
|
|
|
impl From<BTreeMap<String, Value>> for Object {
|
|
|
|
fn from(v: BTreeMap<String, Value>) -> Self {
|
2022-10-04 21:51:18 +00:00
|
|
|
Self(v)
|
2022-01-13 17:36:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-19 18:41:13 +00:00
|
|
|
impl From<HashMap<&str, Value>> for Object {
|
|
|
|
fn from(v: HashMap<&str, Value>) -> Self {
|
|
|
|
Self(v.into_iter().map(|(key, val)| (key.to_string(), val)).collect())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-13 17:36:41 +00:00
|
|
|
impl From<HashMap<String, Value>> for Object {
|
|
|
|
fn from(v: HashMap<String, Value>) -> Self {
|
2022-10-04 21:51:18 +00:00
|
|
|
Self(v.into_iter().collect())
|
2022-01-13 17:36:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-04 21:51:18 +00:00
|
|
|
impl From<Option<Self>> for Object {
|
|
|
|
fn from(v: Option<Self>) -> Self {
|
2022-07-27 08:15:10 +00:00
|
|
|
v.unwrap_or_default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-22 21:16:13 +00:00
|
|
|
impl From<Operation> for Object {
|
|
|
|
fn from(v: Operation) -> Self {
|
2022-10-04 21:51:18 +00:00
|
|
|
Self(map! {
|
2023-05-09 17:48:14 +00:00
|
|
|
String::from("op") => Value::from(match v.op {
|
|
|
|
Op::None => "none",
|
|
|
|
Op::Add => "add",
|
|
|
|
Op::Remove => "remove",
|
|
|
|
Op::Replace => "replace",
|
|
|
|
Op::Change => "change",
|
|
|
|
}),
|
2022-05-04 13:09:57 +00:00
|
|
|
String::from("path") => v.path.to_path().into(),
|
|
|
|
String::from("value") => v.value,
|
|
|
|
})
|
2022-01-22 21:16:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-04 13:09:57 +00:00
|
|
|
impl Deref for Object {
|
|
|
|
type Target = BTreeMap<String, Value>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
2022-01-13 17:36:41 +00:00
|
|
|
}
|
2022-05-04 13:09:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DerefMut for Object {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
&mut self.0
|
2022-01-13 17:36:41 +00:00
|
|
|
}
|
2022-05-04 13:09:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl IntoIterator for Object {
|
|
|
|
type Item = (String, Value);
|
|
|
|
type IntoIter = std::collections::btree_map::IntoIter<String, Value>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
|
|
self.0.into_iter()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Object {
|
2022-11-23 09:42:59 +00:00
|
|
|
/// Fetch the record id if there is one
|
2022-05-30 15:32:26 +00:00
|
|
|
pub fn rid(&self) -> Option<Thing> {
|
|
|
|
match self.get("id") {
|
|
|
|
Some(Value::Thing(v)) => Some(v.clone()),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2022-11-23 09:42:59 +00:00
|
|
|
/// Convert this object to a diff-match-patch operation
|
2022-01-22 21:16:13 +00:00
|
|
|
pub fn to_operation(&self) -> Result<Operation, Error> {
|
2022-05-04 13:09:57 +00:00
|
|
|
match self.get("op") {
|
|
|
|
Some(o) => match self.get("path") {
|
2022-01-22 21:16:13 +00:00
|
|
|
Some(p) => Ok(Operation {
|
|
|
|
op: o.into(),
|
2022-05-31 19:44:27 +00:00
|
|
|
path: p.jsonpath(),
|
2022-05-04 13:09:57 +00:00
|
|
|
value: match self.get("value") {
|
2022-01-22 21:16:13 +00:00
|
|
|
Some(v) => v.clone(),
|
|
|
|
None => Value::Null,
|
|
|
|
},
|
|
|
|
}),
|
2022-03-06 10:58:59 +00:00
|
|
|
_ => Err(Error::InvalidPatch {
|
2022-01-22 21:16:13 +00:00
|
|
|
message: String::from("'path' key missing"),
|
|
|
|
}),
|
|
|
|
},
|
2022-03-06 10:58:59 +00:00
|
|
|
_ => Err(Error::InvalidPatch {
|
2022-01-22 21:16:13 +00:00
|
|
|
message: String::from("'op' key missing"),
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
2021-03-29 15:43:37 +00:00
|
|
|
}
|
2020-06-29 15:36:01 +00:00
|
|
|
|
2022-01-14 08:12:56 +00:00
|
|
|
impl Object {
|
2023-05-09 22:17:29 +00:00
|
|
|
/// Process this type returning a computed simple Value
|
2023-07-06 14:57:42 +00:00
|
|
|
pub(crate) async fn compute(
|
|
|
|
&self,
|
|
|
|
ctx: &Context<'_>,
|
|
|
|
opt: &Options,
|
|
|
|
txn: &Transaction,
|
|
|
|
doc: Option<&CursorDoc<'_>>,
|
|
|
|
) -> Result<Value, Error> {
|
2022-01-14 08:12:56 +00:00
|
|
|
let mut x = BTreeMap::new();
|
2022-05-04 13:09:57 +00:00
|
|
|
for (k, v) in self.iter() {
|
2023-07-06 14:57:42 +00:00
|
|
|
match v.compute(ctx, opt, txn, doc).await {
|
2022-01-14 08:12:56 +00:00
|
|
|
Ok(v) => x.insert(k.clone(), v),
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
};
|
|
|
|
}
|
2022-05-04 13:09:57 +00:00
|
|
|
Ok(Value::Object(Object(x)))
|
2022-01-14 08:12:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-19 09:53:33 +00:00
|
|
|
impl Display for Object {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
|
|
let mut f = Pretty::from(f);
|
|
|
|
if is_pretty() {
|
|
|
|
f.write_char('{')?;
|
|
|
|
} else {
|
|
|
|
f.write_str("{ ")?;
|
|
|
|
}
|
2023-04-15 15:59:55 +00:00
|
|
|
if !self.is_empty() {
|
|
|
|
let indent = pretty_indent();
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"{}",
|
|
|
|
Fmt::pretty_comma_separated(
|
|
|
|
self.0.iter().map(|args| Fmt::new(args, |(k, v), f| write!(
|
|
|
|
f,
|
|
|
|
"{}: {}",
|
|
|
|
escape_key(k),
|
|
|
|
v
|
|
|
|
))),
|
|
|
|
)
|
|
|
|
)?;
|
|
|
|
drop(indent);
|
|
|
|
}
|
2023-01-19 09:53:33 +00:00
|
|
|
if is_pretty() {
|
|
|
|
f.write_char('}')
|
|
|
|
} else {
|
|
|
|
f.write_str(" }")
|
|
|
|
}
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-09 17:48:14 +00:00
|
|
|
mod no_nul_bytes_in_keys {
|
|
|
|
use serde::{
|
|
|
|
de::{self, Visitor},
|
|
|
|
ser::SerializeMap,
|
|
|
|
Deserializer, Serializer,
|
|
|
|
};
|
|
|
|
use std::{collections::BTreeMap, fmt};
|
|
|
|
|
|
|
|
use crate::sql::Value;
|
|
|
|
|
|
|
|
pub(crate) fn serialize<S>(
|
|
|
|
m: &BTreeMap<String, Value>,
|
|
|
|
serializer: S,
|
|
|
|
) -> Result<S::Ok, S::Error>
|
|
|
|
where
|
|
|
|
S: Serializer,
|
|
|
|
{
|
|
|
|
let mut s = serializer.serialize_map(Some(m.len()))?;
|
|
|
|
for (k, v) in m {
|
|
|
|
debug_assert!(!k.contains('\0'));
|
|
|
|
s.serialize_entry(k, v)?;
|
|
|
|
}
|
|
|
|
s.end()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<BTreeMap<String, Value>, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
struct NoNulBytesInKeysVisitor;
|
|
|
|
|
|
|
|
impl<'de> Visitor<'de> for NoNulBytesInKeysVisitor {
|
|
|
|
type Value = BTreeMap<String, Value>;
|
|
|
|
|
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
formatter.write_str("a map without any NUL bytes in its keys")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
|
|
|
where
|
|
|
|
A: de::MapAccess<'de>,
|
|
|
|
{
|
|
|
|
let mut ret = BTreeMap::new();
|
|
|
|
while let Some((k, v)) = map.next_entry()? {
|
|
|
|
ret.insert(k, v);
|
|
|
|
}
|
|
|
|
Ok(ret)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
deserializer.deserialize_map(NoNulBytesInKeysVisitor)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-29 15:36:01 +00:00
|
|
|
pub fn object(i: &str) -> IResult<&str, Object> {
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('{')(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, _) = mightbespace(i)?;
|
2023-04-17 14:39:37 +00:00
|
|
|
let (i, v) = separated_list0(commas, |i| {
|
|
|
|
let (i, k) = key(i)?;
|
|
|
|
let (i, _) = mightbespace(i)?;
|
|
|
|
let (i, _) = char(':')(i)?;
|
|
|
|
let (i, _) = mightbespace(i)?;
|
|
|
|
let (i, v) = value(i)?;
|
|
|
|
Ok((i, (String::from(k), v)))
|
|
|
|
})(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, _) = mightbespace(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = opt(char(','))(i)?;
|
2020-06-29 15:36:01 +00:00
|
|
|
let (i, _) = mightbespace(i)?;
|
2022-03-16 23:52:25 +00:00
|
|
|
let (i, _) = char('}')(i)?;
|
2022-05-04 13:09:57 +00:00
|
|
|
Ok((i, Object(v.into_iter().collect())))
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
2023-04-17 14:39:37 +00:00
|
|
|
pub fn key(i: &str) -> IResult<&str, &str> {
|
2020-06-29 15:36:01 +00:00
|
|
|
alt((key_none, key_single, key_double))(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn key_none(i: &str) -> IResult<&str, &str> {
|
|
|
|
take_while1(val_char)(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn key_single(i: &str) -> IResult<&str, &str> {
|
2023-05-09 17:48:14 +00:00
|
|
|
delimited(char('\''), is_not("\'\0"), char('\''))(i)
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn key_double(i: &str) -> IResult<&str, &str> {
|
2023-05-09 17:48:14 +00:00
|
|
|
delimited(char('\"'), is_not("\"\0"), char('\"'))(i)
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn object_normal() {
|
|
|
|
let sql = "{one:1,two:2,tre:3}";
|
|
|
|
let res = object(sql);
|
|
|
|
assert!(res.is_ok());
|
|
|
|
let out = res.unwrap().1;
|
2022-01-13 17:36:41 +00:00
|
|
|
assert_eq!("{ one: 1, tre: 3, two: 2 }", format!("{}", out));
|
2022-05-04 13:09:57 +00:00
|
|
|
assert_eq!(out.0.len(), 3);
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn object_commas() {
|
|
|
|
let sql = "{one:1,two:2,tre:3,}";
|
|
|
|
let res = object(sql);
|
|
|
|
assert!(res.is_ok());
|
|
|
|
let out = res.unwrap().1;
|
2022-01-13 17:36:41 +00:00
|
|
|
assert_eq!("{ one: 1, tre: 3, two: 2 }", format!("{}", out));
|
2022-05-04 13:09:57 +00:00
|
|
|
assert_eq!(out.0.len(), 3);
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn object_expression() {
|
|
|
|
let sql = "{one:1,two:2,tre:3+1}";
|
|
|
|
let res = object(sql);
|
|
|
|
assert!(res.is_ok());
|
|
|
|
let out = res.unwrap().1;
|
2022-01-13 17:36:41 +00:00
|
|
|
assert_eq!("{ one: 1, tre: 3 + 1, two: 2 }", format!("{}", out));
|
2022-05-04 13:09:57 +00:00
|
|
|
assert_eq!(out.0.len(), 3);
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
}
|