Don’t use asynchronous functions when not necessary (#1936)
This commit is contained in:
parent
e725b3e8e6
commit
53212b23be
19 changed files with 434 additions and 109 deletions
|
@ -20,25 +20,25 @@ impl<'a> Document<'a> {
|
|||
// Get the record id
|
||||
let rid = self.id.as_ref().unwrap();
|
||||
// Set default field values
|
||||
self.current.to_mut().def(ctx, opt, txn, rid).await?;
|
||||
self.current.to_mut().def(rid);
|
||||
// The statement has a data clause
|
||||
if let Some(v) = stm.data() {
|
||||
match v {
|
||||
Data::PatchExpression(data) => {
|
||||
let data = data.compute(ctx, opt, txn, Some(&self.current)).await?;
|
||||
self.current.to_mut().patch(ctx, opt, txn, data).await?
|
||||
self.current.to_mut().patch(data)?
|
||||
}
|
||||
Data::MergeExpression(data) => {
|
||||
let data = data.compute(ctx, opt, txn, Some(&self.current)).await?;
|
||||
self.current.to_mut().merge(ctx, opt, txn, data).await?
|
||||
self.current.to_mut().merge(data)?
|
||||
}
|
||||
Data::ReplaceExpression(data) => {
|
||||
let data = data.compute(ctx, opt, txn, Some(&self.current)).await?;
|
||||
self.current.to_mut().replace(ctx, opt, txn, data).await?
|
||||
self.current.to_mut().replace(data)?
|
||||
}
|
||||
Data::ContentExpression(data) => {
|
||||
let data = data.compute(ctx, opt, txn, Some(&self.current)).await?;
|
||||
self.current.to_mut().replace(ctx, opt, txn, data).await?
|
||||
self.current.to_mut().replace(data)?
|
||||
}
|
||||
Data::SetExpression(x) => {
|
||||
for x in x.iter() {
|
||||
|
@ -86,6 +86,9 @@ impl<'a> Document<'a> {
|
|||
Operator::Dec => {
|
||||
self.current.to_mut().decrement(&ctx, opt, txn, &x.0, v).await?
|
||||
}
|
||||
Operator::Ext => {
|
||||
self.current.to_mut().extend(&ctx, opt, txn, &x.0, v).await?
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +97,7 @@ impl<'a> Document<'a> {
|
|||
};
|
||||
};
|
||||
// Set default field values
|
||||
self.current.to_mut().def(ctx, opt, txn, rid).await?;
|
||||
self.current.to_mut().def(rid);
|
||||
// Carry on
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::sql::Dir;
|
|||
impl<'a> Document<'a> {
|
||||
pub async fn edges(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
_ctx: &Context<'_>,
|
||||
opt: &Options,
|
||||
txn: &Transaction,
|
||||
_stm: &Statement<'_>,
|
||||
|
@ -46,9 +46,9 @@ impl<'a> Document<'a> {
|
|||
let key = crate::key::graph::new(opt.ns(), opt.db(), &r.tb, &r.id, i, rid);
|
||||
run.set(key, vec![]).await?;
|
||||
// Store the edges on the record
|
||||
self.current.to_mut().set(ctx, opt, txn, &*EDGE, Value::Bool(true)).await?;
|
||||
self.current.to_mut().set(ctx, opt, txn, &*IN, l.clone().into()).await?;
|
||||
self.current.to_mut().set(ctx, opt, txn, &*OUT, r.clone().into()).await?;
|
||||
self.current.to_mut().put(&*EDGE, Value::Bool(true));
|
||||
self.current.to_mut().put(&*IN, l.clone().into());
|
||||
self.current.to_mut().put(&*OUT, r.clone().into());
|
||||
}
|
||||
// Carry on
|
||||
Ok(())
|
||||
|
|
|
@ -17,14 +17,14 @@ impl<'a> Document<'a> {
|
|||
// Get the record id
|
||||
let rid = self.id.as_ref().unwrap();
|
||||
// Set default field values
|
||||
self.current.to_mut().def(ctx, opt, txn, rid).await?;
|
||||
self.current.to_mut().def(rid);
|
||||
// This is an INSERT statement
|
||||
if let Workable::Insert(v) = &self.extras {
|
||||
let v = v.compute(ctx, opt, txn, Some(&self.current)).await?;
|
||||
self.current.to_mut().merge(ctx, opt, txn, v).await?;
|
||||
self.current.to_mut().merge(v)?;
|
||||
}
|
||||
// Set default field values
|
||||
self.current.to_mut().def(ctx, opt, txn, rid).await?;
|
||||
self.current.to_mut().def(rid);
|
||||
// Carry on
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -12,15 +12,15 @@ use crate::sql::value::Value;
|
|||
impl<'a> Document<'a> {
|
||||
pub async fn reset(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
opt: &Options,
|
||||
txn: &Transaction,
|
||||
_ctx: &Context<'_>,
|
||||
_opt: &Options,
|
||||
_txn: &Transaction,
|
||||
_stm: &Statement<'_>,
|
||||
) -> Result<(), Error> {
|
||||
// Get the record id
|
||||
let rid = self.id.as_ref().unwrap();
|
||||
// Set default field values
|
||||
self.current.to_mut().def(ctx, opt, txn, rid).await?;
|
||||
self.current.to_mut().def(rid);
|
||||
// Ensure edge fields are reset
|
||||
if self.initial.pick(&*EDGE).is_true() {
|
||||
self.current.to_mut().put(&*EDGE, Value::Bool(true));
|
||||
|
|
213
lib/src/sql/value/cut.rs
Normal file
213
lib/src/sql/value/cut.rs
Normal file
|
@ -0,0 +1,213 @@
|
|||
use crate::sql::part::Next;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
/// Synchronous method for deleting a field from a `Value`
|
||||
pub(crate) fn cut(&mut self, path: &[Part]) {
|
||||
if let Some(p) = path.first() {
|
||||
// Get the current path part
|
||||
match self {
|
||||
// Current path part is an object
|
||||
Value::Object(v) => {
|
||||
if let Part::Field(f) = p {
|
||||
match path.len() {
|
||||
1 => {
|
||||
v.remove(f as &str);
|
||||
}
|
||||
_ => {
|
||||
if let Some(v) = v.get_mut(f as &str) {
|
||||
v.cut(path.next())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Current path part is an array
|
||||
Value::Array(v) => match p {
|
||||
Part::All => match path.len() {
|
||||
1 => {
|
||||
v.clear();
|
||||
}
|
||||
_ => {
|
||||
let path = path.next();
|
||||
v.iter_mut().for_each(|v| v.cut(path));
|
||||
}
|
||||
},
|
||||
Part::First => match path.len() {
|
||||
1 => {
|
||||
if !v.is_empty() {
|
||||
let i = 0;
|
||||
v.remove(i);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if let Some(v) = v.first_mut() {
|
||||
v.cut(path.next())
|
||||
}
|
||||
}
|
||||
},
|
||||
Part::Last => match path.len() {
|
||||
1 => {
|
||||
if !v.is_empty() {
|
||||
let i = v.len() - 1;
|
||||
v.remove(i);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if let Some(v) = v.last_mut() {
|
||||
v.cut(path.next())
|
||||
}
|
||||
}
|
||||
},
|
||||
Part::Index(i) => match path.len() {
|
||||
1 => {
|
||||
if v.len().gt(&i.to_usize()) {
|
||||
v.remove(i.to_usize());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if let Some(v) = v.get_mut(i.to_usize()) {
|
||||
v.cut(path.next())
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
v.iter_mut().for_each(|v| v.cut(path));
|
||||
}
|
||||
},
|
||||
// Ignore everything else
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::dbs::test::mock;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[tokio::test]
|
||||
async fn del_none() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let idi = Idiom::default();
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn del_reset() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ }");
|
||||
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn del_basic() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let idi = Idiom::parse("test.something");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: { other: null } }");
|
||||
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn del_wrong() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let idi = Idiom::parse("test.something.wrong");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn del_other() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let idi = Idiom::parse("test.other.something");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn del_array() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let idi = Idiom::parse("test.something[1]");
|
||||
let mut val = Value::parse("{ test: { something: [123, 456, 789] } }");
|
||||
let res = Value::parse("{ test: { something: [123, 789] } }");
|
||||
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn del_array_field() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let idi = Idiom::parse("test.something[1].age");
|
||||
let mut val = Value::parse(
|
||||
"{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }",
|
||||
);
|
||||
let res = Value::parse("{ test: { something: [{ name: 'A', age: 34 }, { name: 'B' }] } }");
|
||||
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn del_array_fields() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let idi = Idiom::parse("test.something[*].age");
|
||||
let mut val = Value::parse(
|
||||
"{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }",
|
||||
);
|
||||
let res = Value::parse("{ test: { something: [{ name: 'A' }, { name: 'B' }] } }");
|
||||
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn del_array_fields_flat() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let idi = Idiom::parse("test.something.age");
|
||||
let mut val = Value::parse(
|
||||
"{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }",
|
||||
);
|
||||
let res = Value::parse("{ test: { something: [{ name: 'A' }, { name: 'B' }] } }");
|
||||
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn del_array_where_field() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let idi = Idiom::parse("test.something[WHERE age > 35].age");
|
||||
let mut val = Value::parse(
|
||||
"{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }",
|
||||
);
|
||||
let res = Value::parse("{ test: { something: [{ name: 'A', age: 34 }, { name: 'B' }] } }");
|
||||
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn del_array_where_fields() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let idi = Idiom::parse("test.something[WHERE age > 35]");
|
||||
let mut val = Value::parse(
|
||||
"{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }",
|
||||
);
|
||||
let res = Value::parse("{ test: { something: [{ name: 'A', age: 34 }] } }");
|
||||
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
}
|
79
lib/src/sql/value/dec.rs
Normal file
79
lib/src/sql/value/dec.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
use crate::sql::number::Number;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
/// Synchronous method for decrementing a field in a `Value`
|
||||
pub(crate) fn dec(&mut self, path: &[Part], val: Value) {
|
||||
match self.pick(path) {
|
||||
Value::Number(v) => {
|
||||
if let Value::Number(x) = val {
|
||||
self.put(path, Value::from(v - x))
|
||||
}
|
||||
}
|
||||
Value::Array(v) => match val {
|
||||
Value::Array(x) => self.put(path, Value::from(v - x)),
|
||||
x => self.put(path, Value::from(v - x)),
|
||||
},
|
||||
Value::None => {
|
||||
if let Value::Number(x) = val {
|
||||
self.put(path, Value::from(Number::from(0) - x))
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[tokio::test]
|
||||
async fn decrement_none() {
|
||||
let idi = Idiom::parse("other");
|
||||
let mut val = Value::parse("{ test: 100 }");
|
||||
let res = Value::parse("{ test: 100, other: -10 }");
|
||||
val.dec(&idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn decrement_number() {
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: 100 }");
|
||||
let res = Value::parse("{ test: 90 }");
|
||||
val.dec(&idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn decrement_array_number() {
|
||||
let idi = Idiom::parse("test[1]");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [100, 190, 300] }");
|
||||
val.dec(&idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn decrement_array_value() {
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [100, 300] }");
|
||||
val.dec(&idi, Value::from(200));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn decrement_array_array() {
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [200] }");
|
||||
val.dec(&idi, Value::parse("[100, 300]"));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ use crate::sql::part::Part;
|
|||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
/// Asynchronous method for decrementing a field in a `Value`
|
||||
pub(crate) async fn decrement(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
|
|
|
@ -1,19 +1,9 @@
|
|||
use crate::ctx::Context;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Transaction;
|
||||
use crate::err::Error;
|
||||
use crate::sql::paths::ID;
|
||||
use crate::sql::thing::Thing;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub(crate) async fn def(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
opt: &Options,
|
||||
txn: &Transaction,
|
||||
val: &Thing,
|
||||
) -> Result<(), Error> {
|
||||
self.set(ctx, opt, txn, ID.as_ref(), val.clone().into()).await
|
||||
pub(crate) fn def(&mut self, val: &Thing) {
|
||||
self.put(ID.as_ref(), val.clone().into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use async_recursion::async_recursion;
|
|||
use std::collections::HashSet;
|
||||
|
||||
impl Value {
|
||||
/// Asynchronous method for deleting a field from a `Value`
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
|
||||
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
|
||||
pub(crate) async fn del(
|
||||
|
@ -53,7 +54,7 @@ impl Value {
|
|||
},
|
||||
Part::First => match path.len() {
|
||||
1 => {
|
||||
if v.len().gt(&0) {
|
||||
if !v.is_empty() {
|
||||
let i = 0;
|
||||
v.remove(i);
|
||||
}
|
||||
|
@ -66,7 +67,7 @@ impl Value {
|
|||
},
|
||||
Part::Last => match path.len() {
|
||||
1 => {
|
||||
if v.len().gt(&0) {
|
||||
if !v.is_empty() {
|
||||
let i = v.len() - 1;
|
||||
v.remove(i);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ use crate::sql::value::{Value, Values};
|
|||
use async_recursion::async_recursion;
|
||||
|
||||
impl Value {
|
||||
/// Asynchronous method for getting a local or remote field from a `Value`
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
|
||||
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
|
||||
pub(crate) async fn get(
|
||||
|
|
79
lib/src/sql/value/inc.rs
Normal file
79
lib/src/sql/value/inc.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
use crate::sql::number::Number;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
/// Synchronous method for incrementing a field in a `Value`
|
||||
pub(crate) fn inc(&mut self, path: &[Part], val: Value) {
|
||||
match self.pick(path) {
|
||||
Value::Number(v) => {
|
||||
if let Value::Number(x) = val {
|
||||
self.put(path, Value::from(v + x))
|
||||
}
|
||||
}
|
||||
Value::Array(v) => match val {
|
||||
Value::Array(x) => self.put(path, Value::from(v + x)),
|
||||
x => self.put(path, Value::from(v + x)),
|
||||
},
|
||||
Value::None => match val {
|
||||
Value::Number(x) => self.put(path, Value::from(Number::from(0) + x)),
|
||||
Value::Array(x) => self.put(path, Value::from(x)),
|
||||
x => self.put(path, Value::from(vec![x])),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[tokio::test]
|
||||
async fn increment_none() {
|
||||
let idi = Idiom::parse("other");
|
||||
let mut val = Value::parse("{ test: 100 }");
|
||||
let res = Value::parse("{ test: 100, other: +10 }");
|
||||
val.inc(&idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn increment_number() {
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: 100 }");
|
||||
let res = Value::parse("{ test: 110 }");
|
||||
val.inc(&idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn increment_array_number() {
|
||||
let idi = Idiom::parse("test[1]");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [100, 210, 300] }");
|
||||
val.inc(&idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn increment_array_value() {
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [100, 200, 300, 200] }");
|
||||
val.inc(&idi, Value::from(200));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn increment_array_array() {
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [100, 200, 300, 100, 300, 400, 500] }");
|
||||
val.inc(&idi, Value::parse("[100, 300, 400, 500]"));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ use crate::sql::part::Part;
|
|||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
/// Asynchronous method for incrementing a field in a `Value`
|
||||
pub(crate) async fn increment(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
|
|
|
@ -1,29 +1,17 @@
|
|||
use crate::ctx::Context;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Transaction;
|
||||
use crate::err::Error;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub(crate) async fn merge(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
opt: &Options,
|
||||
txn: &Transaction,
|
||||
val: Value,
|
||||
) -> Result<(), Error> {
|
||||
match val {
|
||||
v if v.is_object() => {
|
||||
for k in v.every(None, false, false).iter() {
|
||||
match v.get(ctx, opt, txn, &k.0).await? {
|
||||
Value::None => self.del(ctx, opt, txn, &k.0).await?,
|
||||
v => self.set(ctx, opt, txn, &k.0, v).await?,
|
||||
}
|
||||
pub(crate) fn merge(&mut self, val: Value) -> Result<(), Error> {
|
||||
if val.is_object() {
|
||||
for k in val.every(None, false, false).iter() {
|
||||
match val.pick(&k.0) {
|
||||
Value::None => self.cut(&k.0),
|
||||
v => self.put(&k.0, v),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,12 +19,10 @@ impl Value {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::dbs::test::mock;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[tokio::test]
|
||||
async fn merge_none() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let mut res = Value::parse(
|
||||
"{
|
||||
name: {
|
||||
|
@ -56,13 +42,12 @@ mod tests {
|
|||
},
|
||||
}",
|
||||
);
|
||||
res.merge(&ctx, &opt, &txn, mrg).await.unwrap();
|
||||
res.merge(mrg).unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn merge_basic() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let mut res = Value::parse(
|
||||
"{
|
||||
name: {
|
||||
|
@ -91,7 +76,7 @@ mod tests {
|
|||
tags: ['Rust', 'Golang', 'JavaScript'],
|
||||
}",
|
||||
);
|
||||
res.merge(&ctx, &opt, &txn, mrg).await.unwrap();
|
||||
res.merge(mrg).unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ mod value;
|
|||
mod all;
|
||||
mod clear;
|
||||
mod compare;
|
||||
mod cut;
|
||||
mod decrement;
|
||||
mod def;
|
||||
mod del;
|
||||
|
@ -20,6 +21,7 @@ mod first;
|
|||
mod flatten;
|
||||
mod generate;
|
||||
mod get;
|
||||
mod inc;
|
||||
mod increment;
|
||||
mod last;
|
||||
mod merge;
|
||||
|
|
|
@ -1,34 +1,25 @@
|
|||
use crate::ctx::Context;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Transaction;
|
||||
use crate::err::Error;
|
||||
use crate::sql::operation::Op;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub(crate) async fn patch(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
opt: &Options,
|
||||
txn: &Transaction,
|
||||
val: Value,
|
||||
) -> Result<(), Error> {
|
||||
pub(crate) fn patch(&mut self, val: Value) -> Result<(), Error> {
|
||||
for o in val.to_operations()?.into_iter() {
|
||||
match o.op {
|
||||
Op::Add => match self.get(ctx, opt, txn, &o.path).await? {
|
||||
Value::Array(_) => self.increment(ctx, opt, txn, &o.path, o.value).await?,
|
||||
_ => self.set(ctx, opt, txn, &o.path, o.value).await?,
|
||||
Op::Add => match self.pick(&o.path) {
|
||||
Value::Array(_) => self.inc(&o.path, o.value),
|
||||
_ => self.put(&o.path, o.value),
|
||||
},
|
||||
Op::Remove => self.del(ctx, opt, txn, &o.path).await?,
|
||||
Op::Replace => self.set(ctx, opt, txn, &o.path, o.value).await?,
|
||||
Op::Remove => self.cut(&o.path),
|
||||
Op::Replace => self.put(&o.path, o.value),
|
||||
Op::Change => {
|
||||
if let Value::Strand(p) = o.value {
|
||||
if let Value::Strand(v) = self.get(ctx, opt, txn, &o.path).await? {
|
||||
if let Value::Strand(v) = self.pick(&o.path) {
|
||||
let mut dmp = dmp::new();
|
||||
let mut pch = dmp.patch_from_text(p.as_string());
|
||||
let (txt, _) = dmp.patch_apply(&mut pch, v.as_str());
|
||||
let txt = txt.into_iter().collect::<String>();
|
||||
self.set(ctx, opt, txn, &o.path, Value::from(txt)).await?;
|
||||
self.put(&o.path, Value::from(txt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,90 +34,81 @@ impl Value {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::dbs::test::mock;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[tokio::test]
|
||||
async fn patch_add_simple() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let ops = Value::parse("[{ op: 'add', path: '/temp', value: true }]");
|
||||
let res = Value::parse("{ test: { other: null, something: 123 }, temp: true }");
|
||||
val.patch(&ctx, &opt, &txn, ops).await.unwrap();
|
||||
val.patch(ops).unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn patch_remove_simple() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }");
|
||||
let ops = Value::parse("[{ op: 'remove', path: '/temp' }]");
|
||||
let res = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
val.patch(&ctx, &opt, &txn, ops).await.unwrap();
|
||||
val.patch(ops).unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn patch_replace_simple() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }");
|
||||
let ops = Value::parse("[{ op: 'replace', path: '/temp', value: 'text' }]");
|
||||
let res = Value::parse("{ test: { other: null, something: 123 }, temp: 'text' }");
|
||||
val.patch(&ctx, &opt, &txn, ops).await.unwrap();
|
||||
val.patch(ops).unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn patch_change_simple() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: 'test' }");
|
||||
let ops = Value::parse(
|
||||
"[{ op: 'change', path: '/temp', value: '@@ -1,4 +1,4 @@\n te\n-s\n+x\n t\n' }]",
|
||||
);
|
||||
let res = Value::parse("{ test: { other: null, something: 123 }, temp: 'text' }");
|
||||
val.patch(&ctx, &opt, &txn, ops).await.unwrap();
|
||||
val.patch(ops).unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn patch_add_embedded() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let ops = Value::parse("[{ op: 'add', path: '/temp/test', value: true }]");
|
||||
let res = Value::parse("{ test: { other: null, something: 123 }, temp: { test: true } }");
|
||||
val.patch(&ctx, &opt, &txn, ops).await.unwrap();
|
||||
val.patch(ops).unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn patch_remove_embedded() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }");
|
||||
let ops = Value::parse("[{ op: 'remove', path: '/test/other' }]");
|
||||
let res = Value::parse("{ test: { something: 123 }, temp: true }");
|
||||
val.patch(&ctx, &opt, &txn, ops).await.unwrap();
|
||||
val.patch(ops).unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn patch_replace_embedded() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }");
|
||||
let ops = Value::parse("[{ op: 'replace', path: '/test/other', value: 'text' }]");
|
||||
let res = Value::parse("{ test: { other: 'text', something: 123 }, temp: true }");
|
||||
val.patch(&ctx, &opt, &txn, ops).await.unwrap();
|
||||
val.patch(ops).unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn patch_change_embedded() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let mut val = Value::parse("{ test: { other: 'test', something: 123 }, temp: true }");
|
||||
let ops = Value::parse(
|
||||
"[{ op: 'change', path: '/test/other', value: '@@ -1,4 +1,4 @@\n te\n-s\n+x\n t\n' }]",
|
||||
);
|
||||
let res = Value::parse("{ test: { other: 'text', something: 123 }, temp: true }");
|
||||
val.patch(&ctx, &opt, &txn, ops).await.unwrap();
|
||||
val.patch(ops).unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::sql::part::Part;
|
|||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
/// Synchronous method for getting a field from a `Value`
|
||||
pub fn pick(&self, path: &[Part]) -> Self {
|
||||
match path.first() {
|
||||
// Get the current path part
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::sql::part::Part;
|
|||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
/// Synchronous method for setting a field on a `Value`
|
||||
pub fn put(&mut self, path: &[Part], val: Value) {
|
||||
match path.first() {
|
||||
// Get the current path part
|
||||
|
|
|
@ -1,25 +1,12 @@
|
|||
use crate::ctx::Context;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Transaction;
|
||||
use crate::err::Error;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub(crate) async fn replace(
|
||||
&mut self,
|
||||
_ctx: &Context<'_>,
|
||||
_opt: &Options,
|
||||
_txn: &Transaction,
|
||||
val: Value,
|
||||
) -> Result<(), Error> {
|
||||
// Clear all entries
|
||||
match val {
|
||||
Value::Object(v) => {
|
||||
*self = Value::from(v);
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
pub(crate) fn replace(&mut self, val: Value) -> Result<(), Error> {
|
||||
if val.is_object() {
|
||||
*self = val;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,16 +14,14 @@ impl Value {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::dbs::test::mock;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[tokio::test]
|
||||
async fn replace() {
|
||||
let (ctx, opt, txn) = mock().await;
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ other: true }");
|
||||
let obj = Value::parse("{ other: true }");
|
||||
val.replace(&ctx, &opt, &txn, obj).await.unwrap();
|
||||
val.replace(obj).unwrap();
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use crate::sql::value::Value;
|
|||
use async_recursion::async_recursion;
|
||||
|
||||
impl Value {
|
||||
/// Asynchronous method for setting a field on a `Value`
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
|
||||
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
|
||||
pub(crate) async fn set(
|
||||
|
|
Loading…
Reference in a new issue