Implement SQL Object as a newtype tuple struct

This commit is contained in:
Tobie Morgan Hitchcock 2022-05-04 14:09:57 +01:00
parent 5182359813
commit 3ee1ddb5b1
12 changed files with 88 additions and 91 deletions

View file

@ -15,65 +15,73 @@ use nom::combinator::opt;
use nom::multi::separated_list0; use nom::multi::separated_list0;
use nom::sequence::delimited; use nom::sequence::delimited;
use serde::ser::SerializeMap; use serde::ser::SerializeMap;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::ops::Deref;
use std::ops::DerefMut;
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Deserialize)] #[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Deserialize)]
pub struct Object { pub struct Object(pub BTreeMap<String, Value>);
pub value: BTreeMap<String, Value>,
}
impl From<BTreeMap<String, Value>> for Object { impl From<BTreeMap<String, Value>> for Object {
fn from(v: BTreeMap<String, Value>) -> Self { fn from(v: BTreeMap<String, Value>) -> Self {
Object { Object(v)
value: v,
}
} }
} }
impl From<HashMap<String, Value>> for Object { impl From<HashMap<String, Value>> for Object {
fn from(v: HashMap<String, Value>) -> Self { fn from(v: HashMap<String, Value>) -> Self {
Object { Object(v.into_iter().collect())
value: v.into_iter().collect(),
}
} }
} }
impl From<Operation> for Object { impl From<Operation> for Object {
fn from(v: Operation) -> Self { fn from(v: Operation) -> Self {
Object { Object(map! {
value: map! { String::from("op") => match v.op {
String::from("op") => match v.op { Op::None => Value::from("none"),
Op::None => Value::from("none"), Op::Add => Value::from("add"),
Op::Add => Value::from("add"), Op::Remove => Value::from("remove"),
Op::Remove => Value::from("remove"), Op::Replace => Value::from("replace"),
Op::Replace => Value::from("replace"), Op::Change => Value::from("change"),
Op::Change => Value::from("change"),
},
String::from("path") => v.path.to_path().into(),
String::from("value") => v.value,
}, },
} String::from("path") => v.path.to_path().into(),
String::from("value") => v.value,
})
}
}
impl Deref for Object {
type Target = BTreeMap<String, Value>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Object {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
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 { impl Object {
pub fn remove(&mut self, key: &str) {
self.value.remove(key);
}
pub fn insert(&mut self, key: &str, val: Value) {
self.value.insert(key.to_owned(), val);
}
pub fn to_operation(&self) -> Result<Operation, Error> { pub fn to_operation(&self) -> Result<Operation, Error> {
match self.value.get("op") { match self.get("op") {
Some(o) => match self.value.get("path") { Some(o) => match self.get("path") {
Some(p) => Ok(Operation { Some(p) => Ok(Operation {
op: o.into(), op: o.into(),
path: p.to_idiom(), path: p.to_idiom(),
value: match self.value.get("value") { value: match self.get("value") {
Some(v) => v.clone(), Some(v) => v.clone(),
None => Value::Null, None => Value::Null,
}, },
@ -98,15 +106,13 @@ impl Object {
doc: Option<&Value>, doc: Option<&Value>,
) -> Result<Value, Error> { ) -> Result<Value, Error> {
let mut x = BTreeMap::new(); let mut x = BTreeMap::new();
for (k, v) in &self.value { for (k, v) in self.iter() {
match v.compute(ctx, opt, txn, doc).await { match v.compute(ctx, opt, txn, doc).await {
Ok(v) => x.insert(k.clone(), v), Ok(v) => x.insert(k.clone(), v),
Err(e) => return Err(e), Err(e) => return Err(e),
}; };
} }
Ok(Value::Object(Object { Ok(Value::Object(Object(x)))
value: x,
}))
} }
} }
@ -115,8 +121,7 @@ impl fmt::Display for Object {
write!( write!(
f, f,
"{{ {} }}", "{{ {} }}",
self.value self.iter()
.iter()
.map(|(k, v)| format!("{}: {}", escape(k, &val_char, "\""), v)) .map(|(k, v)| format!("{}: {}", escape(k, &val_char, "\""), v))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
@ -130,16 +135,14 @@ impl Serialize for Object {
S: serde::Serializer, S: serde::Serializer,
{ {
if serializer.is_human_readable() { if serializer.is_human_readable() {
let mut map = serializer.serialize_map(Some(self.value.len()))?; let mut map = serializer.serialize_map(Some(self.len()))?;
for (ref k, ref v) in &self.value { for (ref k, ref v) in &self.0 {
map.serialize_key(k)?; map.serialize_key(k)?;
map.serialize_value(v)?; map.serialize_value(v)?;
} }
map.end() map.end()
} else { } else {
let mut val = serializer.serialize_struct("Object", 1)?; serializer.serialize_newtype_struct("Object", &self.0)
val.serialize_field("value", &self.value)?;
val.end()
} }
} }
} }
@ -152,12 +155,7 @@ pub fn object(i: &str) -> IResult<&str, Object> {
let (i, _) = opt(char(','))(i)?; let (i, _) = opt(char(','))(i)?;
let (i, _) = mightbespace(i)?; let (i, _) = mightbespace(i)?;
let (i, _) = char('}')(i)?; let (i, _) = char('}')(i)?;
Ok(( Ok((i, Object(v.into_iter().collect())))
i,
Object {
value: v.into_iter().collect(),
},
))
} }
fn item(i: &str) -> IResult<&str, (String, Value)> { fn item(i: &str) -> IResult<&str, (String, Value)> {
@ -197,7 +195,7 @@ mod tests {
assert!(res.is_ok()); assert!(res.is_ok());
let out = res.unwrap().1; let out = res.unwrap().1;
assert_eq!("{ one: 1, tre: 3, two: 2 }", format!("{}", out)); assert_eq!("{ one: 1, tre: 3, two: 2 }", format!("{}", out));
assert_eq!(out.value.len(), 3); assert_eq!(out.0.len(), 3);
} }
#[test] #[test]
@ -207,7 +205,7 @@ mod tests {
assert!(res.is_ok()); assert!(res.is_ok());
let out = res.unwrap().1; let out = res.unwrap().1;
assert_eq!("{ one: 1, tre: 3, two: 2 }", format!("{}", out)); assert_eq!("{ one: 1, tre: 3, two: 2 }", format!("{}", out));
assert_eq!(out.value.len(), 3); assert_eq!(out.0.len(), 3);
} }
#[test] #[test]
@ -217,6 +215,6 @@ mod tests {
assert!(res.is_ok()); assert!(res.is_ok());
let out = res.unwrap().1; let out = res.unwrap().1;
assert_eq!("{ one: 1, tre: 3 + 1, two: 2 }", format!("{}", out)); assert_eq!("{ one: 1, tre: 3 + 1, two: 2 }", format!("{}", out));
assert_eq!(out.value.len(), 3); assert_eq!(out.0.len(), 3);
} }
} }

View file

@ -45,9 +45,9 @@ impl InfoStatement {
// Process the statement // Process the statement
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_ns().await? { for v in run.all_ns().await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("ns", tmp.into()); res.insert("ns".to_owned(), tmp.into());
// Ok all good // Ok all good
Value::from(res).ok() Value::from(res).ok()
} }
@ -63,21 +63,21 @@ impl InfoStatement {
// Process the databases // Process the databases
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_db(opt.ns()).await? { for v in run.all_db(opt.ns()).await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("db", tmp.into()); res.insert("db".to_owned(), tmp.into());
// Process the tokens // Process the tokens
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_nt(opt.ns()).await? { for v in run.all_nt(opt.ns()).await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("nt", tmp.into()); res.insert("nt".to_owned(), tmp.into());
// Process the logins // Process the logins
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_nl(opt.ns()).await? { for v in run.all_nl(opt.ns()).await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("nl", tmp.into()); res.insert("nl".to_owned(), tmp.into());
// Ok all good // Ok all good
Value::from(res).ok() Value::from(res).ok()
} }
@ -93,27 +93,27 @@ impl InfoStatement {
// Process the tables // Process the tables
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_tb(opt.ns(), opt.db()).await? { for v in run.all_tb(opt.ns(), opt.db()).await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("tb", tmp.into()); res.insert("tb".to_owned(), tmp.into());
// Process the scopes // Process the scopes
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_sc(opt.ns(), opt.db()).await? { for v in run.all_sc(opt.ns(), opt.db()).await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("sc", tmp.into()); res.insert("sc".to_owned(), tmp.into());
// Process the tokens // Process the tokens
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_nt(opt.ns()).await? { for v in run.all_nt(opt.ns()).await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("nt", tmp.into()); res.insert("nt".to_owned(), tmp.into());
// Process the logins // Process the logins
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_nl(opt.ns()).await? { for v in run.all_nl(opt.ns()).await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("nl", tmp.into()); res.insert("nl".to_owned(), tmp.into());
// Ok all good // Ok all good
Value::from(res).ok() Value::from(res).ok()
} }
@ -129,9 +129,9 @@ impl InfoStatement {
// Process the tokens // Process the tokens
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_st(opt.ns(), opt.db(), sc).await? { for v in run.all_st(opt.ns(), opt.db(), sc).await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("st", tmp.into()); res.insert("st".to_owned(), tmp.into());
// Ok all good // Ok all good
Value::from(res).ok() Value::from(res).ok()
} }
@ -147,27 +147,27 @@ impl InfoStatement {
// Process the events // Process the events
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_ev(opt.ns(), opt.db(), tb).await? { for v in run.all_ev(opt.ns(), opt.db(), tb).await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("ev", tmp.into()); res.insert("ev".to_owned(), tmp.into());
// Process the fields // Process the fields
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_fd(opt.ns(), opt.db(), tb).await? { for v in run.all_fd(opt.ns(), opt.db(), tb).await? {
tmp.insert(&v.name.to_string(), v.to_string().into()); tmp.insert(v.name.to_string(), v.to_string().into());
} }
res.insert("fd", tmp.into()); res.insert("fd".to_owned(), tmp.into());
// Process the indexs // Process the indexs
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_ix(opt.ns(), opt.db(), tb).await? { for v in run.all_ix(opt.ns(), opt.db(), tb).await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("ix", tmp.into()); res.insert("ix".to_owned(), tmp.into());
// Process the tables // Process the tables
let mut tmp = Object::default(); let mut tmp = Object::default();
for v in run.all_ft(opt.ns(), opt.db(), tb).await? { for v in run.all_ft(opt.ns(), opt.db(), tb).await? {
tmp.insert(&v.name, v.to_string().into()); tmp.insert(v.name.to_owned(), v.to_string().into());
} }
res.insert("ft", tmp.into()); res.insert("ft".to_owned(), tmp.into());
// Ok all good // Ok all good
Value::from(res).ok() Value::from(res).ok()
} }

View file

@ -16,7 +16,7 @@ impl Value {
Some(p) => match (self, other) { Some(p) => match (self, other) {
// Current path part is an object // Current path part is an object
(Value::Object(a), Value::Object(b)) => match p { (Value::Object(a), Value::Object(b)) => match p {
Part::Field(f) => match (a.value.get(&f.name), b.value.get(&f.name)) { Part::Field(f) => match (a.get(&f.name), b.get(&f.name)) {
(Some(a), Some(b)) => a.compare(b, path.next(), collate, numeric), (Some(a), Some(b)) => a.compare(b, path.next(), collate, numeric),
(Some(_), None) => Some(Ordering::Greater), (Some(_), None) => Some(Ordering::Greater),
(None, Some(_)) => Some(Ordering::Less), (None, Some(_)) => Some(Ordering::Less),

View file

@ -30,7 +30,7 @@ impl Value {
v.remove(&f.name); v.remove(&f.name);
Ok(()) Ok(())
} }
_ => match v.value.get_mut(&f.name) { _ => match v.get_mut(&f.name) {
Some(v) if v.is_some() => v.del(ctx, opt, txn, path.next()).await, Some(v) if v.is_some() => v.del(ctx, opt, txn, path.next()).await,
_ => Ok(()), _ => Ok(()),
}, },

View file

@ -9,8 +9,8 @@ impl Value {
match (self, val) { match (self, val) {
(Value::Object(a), Value::Object(b)) if a != b => { (Value::Object(a), Value::Object(b)) if a != b => {
// Loop over old keys // Loop over old keys
for (key, _) in a.value.iter() { for (key, _) in a.iter() {
if !b.value.contains_key(key) { if !b.contains_key(key) {
ops.push(Operation { ops.push(Operation {
op: Op::Remove, op: Op::Remove,
path: path.clone().push(key.clone().into()), path: path.clone().push(key.clone().into()),
@ -19,8 +19,8 @@ impl Value {
} }
} }
// Loop over new keys // Loop over new keys
for (key, val) in b.value.iter() { for (key, val) in b.iter() {
match a.value.get(key) { match a.get(key) {
None => ops.push(Operation { None => ops.push(Operation {
op: Op::Add, op: Op::Add,
path: path.clone().push(key.clone().into()), path: path.clone().push(key.clone().into()),

View file

@ -13,7 +13,7 @@ impl Value {
Some(p) => match self { Some(p) => match self {
// Current path part is an object // Current path part is an object
Value::Object(v) => match p { Value::Object(v) => match p {
Part::Field(f) => match v.value.get(&f.name) { Part::Field(f) => match v.get(&f.name) {
Some(v) => v._each(path.next(), prev.push(p.clone())), Some(v) => v._each(path.next(), prev.push(p.clone())),
None => vec![], None => vec![],
}, },

View file

@ -10,7 +10,6 @@ impl Value {
match self { match self {
// Current path part is an object // Current path part is an object
Value::Object(v) => v Value::Object(v) => v
.value
.iter() .iter()
.flat_map(|(k, v)| v._every(prev.clone().push(Part::from(k)))) .flat_map(|(k, v)| v._every(prev.clone().push(Part::from(k))))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),

View file

@ -26,7 +26,7 @@ impl Value {
Some(p) => match self { Some(p) => match self {
// Current path part is an object // Current path part is an object
Value::Object(v) => match p { Value::Object(v) => match p {
Part::Field(f) => match v.value.get(&f.name) { Part::Field(f) => match v.get(&f.name) {
Some(v) => v.get(ctx, opt, txn, path.next()).await, Some(v) => v.get(ctx, opt, txn, path.next()).await,
None => Ok(Value::None), None => Ok(Value::None),
}, },

View file

@ -15,7 +15,7 @@ impl Value {
) -> Result<(), Error> { ) -> Result<(), Error> {
match val.compute(ctx, opt, txn, Some(self)).await? { match val.compute(ctx, opt, txn, Some(self)).await? {
Value::Object(v) => { Value::Object(v) => {
for (k, v) in v.value.into_iter() { for (k, v) in v {
self.set(ctx, opt, txn, &[Part::from(k)], v).await?; self.set(ctx, opt, txn, &[Part::from(k)], v).await?;
} }
Ok(()) Ok(())

View file

@ -9,7 +9,7 @@ impl Value {
Some(p) => match self { Some(p) => match self {
// Current path part is an object // Current path part is an object
Value::Object(v) => match p { Value::Object(v) => match p {
Part::Field(f) => match v.value.get(&f.name) { Part::Field(f) => match v.get(&f.name) {
Some(v) => v.pick(path.next()), Some(v) => v.pick(path.next()),
None => Value::None, None => Value::None,
}, },

View file

@ -24,12 +24,12 @@ impl Value {
Some(p) => match self { Some(p) => match self {
// Current path part is an object // Current path part is an object
Value::Object(v) => match p { Value::Object(v) => match p {
Part::Field(f) => match v.value.get_mut(&f.name) { Part::Field(f) => match v.get_mut(&f.name) {
Some(v) if v.is_some() => v.set(ctx, opt, txn, path.next(), val).await, Some(v) if v.is_some() => v.set(ctx, opt, txn, path.next(), val).await,
_ => { _ => {
let mut obj = Value::base(); let mut obj = Value::base();
obj.set(ctx, opt, txn, path.next(), val).await?; obj.set(ctx, opt, txn, path.next(), val).await?;
v.insert(&f.name, obj); v.insert(f.name.to_owned(), obj);
Ok(()) Ok(())
} }
}, },

View file

@ -499,7 +499,7 @@ impl Value {
Value::Thing(_) => true, Value::Thing(_) => true,
Value::Geometry(_) => true, Value::Geometry(_) => true,
Value::Array(v) => !v.value.is_empty(), Value::Array(v) => !v.value.is_empty(),
Value::Object(v) => !v.value.is_empty(), Value::Object(v) => !v.is_empty(),
Value::Strand(v) => !v.value.is_empty() && v.value.to_ascii_lowercase() != "false", Value::Strand(v) => !v.value.is_empty() && v.value.to_ascii_lowercase() != "false",
Value::Number(v) => v.is_truthy(), Value::Number(v) => v.is_truthy(),
Value::Duration(v) => v.value.as_nanos() > 0, Value::Duration(v) => v.value.as_nanos() > 0,