Don’t use asynchronous functions when not necessary ()

This commit is contained in:
Tobie Morgan Hitchcock 2023-05-06 01:15:03 +01:00 committed by GitHub
parent e725b3e8e6
commit 53212b23be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 434 additions and 109 deletions

View file

@ -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(())
}

View file

@ -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(())

View file

@ -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(())
}

View file

@ -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
View 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
View 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);
}
}

View file

@ -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<'_>,

View file

@ -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())
}
}

View file

@ -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);
}

View file

@ -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
View 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);
}
}

View file

@ -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<'_>,

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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

View file

@ -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

View file

@ -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);
}
}

View file

@ -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(