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

View file

@ -16,7 +16,7 @@ impl Value {
Some(p) => match (self, other) {
// Current path part is an object
(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(_), None) => Some(Ordering::Greater),
(None, Some(_)) => Some(Ordering::Less),

View file

@ -30,7 +30,7 @@ impl Value {
v.remove(&f.name);
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,
_ => Ok(()),
},

View file

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

View file

@ -13,7 +13,7 @@ impl Value {
Some(p) => match self {
// Current path part is an object
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())),
None => vec![],
},

View file

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

View file

@ -26,7 +26,7 @@ impl Value {
Some(p) => match self {
// Current path part is an object
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,
None => Ok(Value::None),
},

View file

@ -15,7 +15,7 @@ impl Value {
) -> Result<(), Error> {
match val.compute(ctx, opt, txn, Some(self)).await? {
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?;
}
Ok(())

View file

@ -9,7 +9,7 @@ impl Value {
Some(p) => match self {
// Current path part is an object
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()),
None => Value::None,
},

View file

@ -24,12 +24,12 @@ impl Value {
Some(p) => match self {
// Current path part is an object
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,
_ => {
let mut obj = Value::base();
obj.set(ctx, opt, txn, path.next(), val).await?;
v.insert(&f.name, obj);
v.insert(f.name.to_owned(), obj);
Ok(())
}
},

View file

@ -499,7 +499,7 @@ impl Value {
Value::Thing(_) => true,
Value::Geometry(_) => true,
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::Number(v) => v.is_truthy(),
Value::Duration(v) => v.value.as_nanos() > 0,