Return a Result from async methods on Value

Instead of silently failing, the async methods on a Value for getting, setting, and updating the value, now return a Result. This means we can chain the awaited result using the ? operator.
This commit is contained in:
Tobie Morgan Hitchcock 2022-01-20 23:00:57 +00:00
parent c86a53502d
commit 63747612e3
17 changed files with 343 additions and 169 deletions

View file

@ -32,6 +32,14 @@ pub struct Idiom {
pub parts: Vec<Part>,
}
impl From<String> for Idiom {
fn from(v: String) -> Self {
Idiom {
parts: vec![Part::from(v)],
}
}
}
impl From<Vec<Part>> for Idiom {
fn from(v: Vec<Part>) -> Self {
Idiom {
@ -59,7 +67,7 @@ impl Idiom {
) -> Result<Value, Error> {
match doc {
// There is a current document
Some(v) => v.get(ctx, opt, exe, self).await.ok(),
Some(v) => v.get(ctx, opt, exe, self).await,
// There isn't any document
None => Ok(Value::None),
}

View file

@ -42,7 +42,7 @@ impl Param {
// Process the paramater value
let res = v.compute(ctx, opt, exe, doc).await?;
// Return the desired field
res.get(ctx, opt, exe, &self.name.next()).await.ok()
res.get(ctx, opt, exe, &self.name.next()).await
}
// The base variable does not exist
None => Ok(Value::None),

View file

@ -66,13 +66,11 @@ impl Subquery {
// Process result
match v.limit() {
1 => match v.expr.single() {
Some(v) => {
res.first(&ctx, &opt, exe).await.get(&ctx, &opt, exe, &v).await.ok()
}
None => res.first(&ctx, &opt, exe).await.ok(),
Some(v) => res.first(&ctx, &opt, exe).await?.get(&ctx, &opt, exe, &v).await,
None => res.first(&ctx, &opt, exe).await,
},
_ => match v.expr.single() {
Some(v) => res.get(&ctx, &opt, exe, &v).await.ok(),
Some(v) => res.get(&ctx, &opt, exe, &v).await,
None => res.ok(),
},
}

View file

@ -1,6 +1,7 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::array::Array;
use crate::sql::idiom::Idiom;
use crate::sql::value::Value;
@ -12,8 +13,36 @@ impl Value {
opt: &Options<'_>,
exe: &mut Executor,
path: &Idiom,
) {
) -> Result<(), Error> {
let val = Value::from(Array::default());
self.set(ctx, opt, exe, path, Value::from(val)).await
self.set(ctx, opt, exe, path, val).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dbs::test::mock;
use crate::sql::test::Parse;
#[tokio::test]
async fn array_none() {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::default();
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("[]");
val.array(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
#[tokio::test]
async fn array_path() {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: [] }");
val.array(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
}

44
src/sql/value/clear.rs Normal file
View file

@ -0,0 +1,44 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::object::Object;
use crate::sql::value::Value;
impl Value {
pub async fn clear(
&mut self,
_ctx: &Runtime,
_opt: &Options<'_>,
_exe: &mut Executor,
) -> Result<(), Error> {
*self = Value::from(Object::default());
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dbs::test::mock;
use crate::sql::test::Parse;
#[tokio::test]
async fn clear_none() {
let (ctx, opt, mut exe) = mock();
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{}");
val.clear(&ctx, &opt, &mut exe).await.unwrap();
assert_eq!(res, val);
}
#[tokio::test]
async fn clear_path() {
let (ctx, opt, mut exe) = mock();
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{}");
val.clear(&ctx, &opt, &mut exe).await.unwrap();
assert_eq!(res, val);
}
}

View file

@ -1,6 +1,7 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::number::Number;
use crate::sql::value::Value;
@ -13,33 +14,23 @@ impl Value {
exe: &mut Executor,
path: &Idiom,
val: Value,
) {
match self.get(ctx, opt, exe, path).await {
) -> Result<(), Error> {
match self.get(ctx, opt, exe, path).await? {
Value::Number(v) => match val {
Value::Number(x) => {
self.set(ctx, opt, exe, path, Value::from(v - x)).await;
()
}
_ => (),
Value::Number(x) => self.set(ctx, opt, exe, path, Value::from(v - x)).await,
_ => Ok(()),
},
Value::Array(v) => match val {
Value::Array(x) => {
self.set(ctx, opt, exe, path, Value::from(v - x)).await;
()
}
x => {
self.set(ctx, opt, exe, path, Value::from(v - x)).await;
()
}
Value::Array(x) => self.set(ctx, opt, exe, path, Value::from(v - x)).await,
x => self.set(ctx, opt, exe, path, Value::from(v - x)).await,
},
Value::None => match val {
Value::Number(x) => {
self.set(ctx, opt, exe, path, Value::from(Number::from(0) - x)).await;
()
self.set(ctx, opt, exe, path, Value::from(Number::from(0) - x)).await
}
_ => (),
_ => Ok(()),
},
_ => (),
_ => Ok(()),
}
}
}
@ -57,7 +48,7 @@ mod tests {
let idi = Idiom::parse("other");
let mut val = Value::parse("{ test: 100 }");
let res = Value::parse("{ test: 100, other: -10 }");
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(10)).await;
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(10)).await.unwrap();
assert_eq!(res, val);
}
@ -67,7 +58,7 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: 100 }");
let res = Value::parse("{ test: 90 }");
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(10)).await;
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(10)).await.unwrap();
assert_eq!(res, val);
}
@ -77,7 +68,7 @@ mod tests {
let idi = Idiom::parse("test[1]");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 190, 300] }");
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(10)).await;
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(10)).await.unwrap();
assert_eq!(res, val);
}
@ -87,7 +78,7 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 300] }");
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(200)).await;
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(200)).await.unwrap();
assert_eq!(res, val);
}
@ -97,7 +88,7 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [200] }");
val.decrement(&ctx, &opt, &mut exe, &idi, Value::parse("[100, 300]")).await;
val.decrement(&ctx, &opt, &mut exe, &idi, Value::parse("[100, 300]")).await.unwrap();
assert_eq!(res, val);
}
}

View file

@ -1,6 +1,7 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::array::Abolish;
use crate::sql::idiom::Idiom;
use crate::sql::part::Part;
@ -16,66 +17,73 @@ impl Value {
opt: &Options<'_>,
exe: &mut Executor,
path: &Idiom,
) {
) -> Result<(), Error> {
match path.parts.first() {
// Get the current path part
Some(p) => match self {
// Current path part is an object
Value::Object(v) => match p {
Part::Field(p) => match path.parts.len() {
1 => v.remove(&p.name),
1 => {
v.remove(&p.name);
Ok(())
}
_ => match v.value.get_mut(&p.name) {
Some(v) if v.is_some() => v.del(ctx, opt, exe, &path.next()).await,
_ => (),
_ => Ok(()),
},
},
_ => (),
_ => Ok(()),
},
// Current path part is an array
Value::Array(v) => match p {
Part::All => match path.parts.len() {
1 => v.value.clear(),
1 => {
v.value.clear();
Ok(())
}
_ => {
for v in &mut v.value {
v.del(ctx, opt, exe, &path.next()).await
v.del(ctx, opt, exe, &path.next()).await?;
}
Ok(())
}
},
Part::First => match path.parts.len() {
1 => {
if v.value.len().gt(&0) {
v.value.remove(0);
()
}
Ok(())
}
_ => match v.value.first_mut() {
Some(v) => v.del(ctx, opt, exe, &path.next()).await,
None => (),
None => Ok(()),
},
},
Part::Last => match path.parts.len() {
1 => {
if v.value.len().gt(&0) {
v.value.remove(v.value.len() - 1);
()
}
Ok(())
}
_ => match v.value.last_mut() {
Some(v) => v.del(ctx, opt, exe, &path.next()).await,
None => (),
None => Ok(()),
},
},
Part::Index(i) => match path.parts.len() {
1 => {
if v.value.len().gt(&i.to_usize()) {
v.value.remove(i.to_usize());
()
}
Ok(())
}
_ => match path.parts.len() {
_ => match v.value.get_mut(i.to_usize()) {
Some(v) => v.del(ctx, opt, exe, &path.next()).await,
None => (),
None => Ok(()),
},
},
},
@ -83,31 +91,29 @@ impl Value {
1 => {
let mut m = HashMap::new();
for (i, v) in v.value.iter().enumerate() {
match w.compute(ctx, opt, exe, Some(&v)).await {
Ok(o) if o.is_truthy() => m.insert(i, ()),
_ => None,
if w.compute(ctx, opt, exe, Some(&v)).await?.is_truthy() {
m.insert(i, ());
};
}
v.value.abolish(|i| m.contains_key(&i))
v.value.abolish(|i| m.contains_key(&i));
Ok(())
}
_ => {
for v in &mut v.value {
match w.compute(ctx, opt, exe, Some(&v)).await {
Ok(o) if o.is_truthy() => {
v.del(ctx, opt, exe, &path.next()).await
if w.compute(ctx, opt, exe, Some(&v)).await?.is_truthy() {
v.del(ctx, opt, exe, &path.next()).await?;
}
_ => (),
};
}
Ok(())
}
},
_ => (),
_ => Ok(()),
},
// Ignore everything else
_ => (),
_ => Ok(()),
},
// We are done
None => (),
None => Ok(()),
}
}
}
@ -122,12 +128,10 @@ mod tests {
#[tokio::test]
async fn del_none() {
let (ctx, opt, mut exe) = mock();
let idi = Idiom {
parts: vec![],
};
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, &mut exe, &idi).await;
val.del(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
@ -137,7 +141,7 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ }");
val.del(&ctx, &opt, &mut exe, &idi).await;
val.del(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
@ -147,7 +151,7 @@ mod tests {
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, &mut exe, &idi).await;
val.del(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
@ -157,7 +161,7 @@ mod tests {
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, &mut exe, &idi).await;
val.del(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
@ -167,7 +171,7 @@ mod tests {
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, &mut exe, &idi).await;
val.del(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
@ -177,7 +181,7 @@ mod tests {
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, &mut exe, &idi).await;
val.del(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
@ -187,7 +191,7 @@ mod tests {
let idi = Idiom::parse("test.something[1].age");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 34 }, { }] } }");
val.del(&ctx, &opt, &mut exe, &idi).await;
val.del(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
@ -197,7 +201,7 @@ mod tests {
let idi = Idiom::parse("test.something[*].age");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ }, { }] } }");
val.del(&ctx, &opt, &mut exe, &idi).await;
val.del(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
@ -207,7 +211,7 @@ mod tests {
let idi = Idiom::parse("test.something[WHERE age > 35].age");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 34 }, { }] } }");
val.del(&ctx, &opt, &mut exe, &idi).await;
val.del(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
@ -217,7 +221,7 @@ mod tests {
let idi = Idiom::parse("test.something[WHERE age > 35]");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 34 }] } }");
val.del(&ctx, &opt, &mut exe, &idi).await;
val.del(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
}

View file

@ -1,12 +1,18 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::part::Part;
use crate::sql::value::Value;
impl Value {
pub async fn first(&self, ctx: &Runtime, opt: &Options<'_>, exe: &mut Executor) -> Self {
pub async fn first(
&self,
ctx: &Runtime,
opt: &Options<'_>,
exe: &mut Executor,
) -> Result<Self, Error> {
self.get(ctx, opt, exe, &Idiom::from(vec![Part::First])).await
}
}

View file

@ -1,6 +1,7 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::field::{Field, Fields};
use crate::sql::idiom::Idiom;
use crate::sql::part::Part;
@ -16,7 +17,7 @@ impl Value {
opt: &Options<'_>,
exe: &mut Executor,
path: &Idiom,
) -> Self {
) -> Result<Self, Error> {
match path.parts.first() {
// Get the current path part
Some(p) => match self {
@ -24,49 +25,46 @@ impl Value {
Value::Object(v) => match p {
Part::Field(p) => match v.value.get(&p.name) {
Some(v) => v.get(ctx, opt, exe, &path.next()).await,
None => Value::None,
None => Ok(Value::None),
},
_ => Value::None,
_ => Ok(Value::None),
},
// Current path part is an array
Value::Array(v) => match p {
Part::All => {
let mut a = Vec::new();
for v in &v.value {
a.push(v.get(ctx, opt, exe, &path.next()).await)
a.push(v.get(ctx, opt, exe, &path.next()).await?)
}
a.into()
Ok(a.into())
}
Part::First => match v.value.first() {
Some(v) => v.get(ctx, opt, exe, &path.next()).await,
None => Value::None,
None => Ok(Value::None),
},
Part::Last => match v.value.last() {
Some(v) => v.get(ctx, opt, exe, &path.next()).await,
None => Value::None,
None => Ok(Value::None),
},
Part::Index(i) => match v.value.get(i.to_usize()) {
Some(v) => v.get(ctx, opt, exe, &path.next()).await,
None => Value::None,
None => Ok(Value::None),
},
Part::Where(w) => {
let mut a = Vec::new();
for v in &v.value {
match w.compute(ctx, opt, exe, Some(&v)).await {
Ok(x) if x.is_truthy() => {
a.push(v.get(ctx, opt, exe, &path.next()).await)
if w.compute(ctx, opt, exe, Some(&v)).await?.is_truthy() {
a.push(v.get(ctx, opt, exe, &path.next()).await?)
}
_ => (),
};
}
a.into()
Ok(a.into())
}
_ => Value::None,
_ => Ok(Value::None),
},
// Current path part is a thing
Value::Thing(v) => match path.parts.len() {
// No remote embedded fields, so just return this
1 => Value::Thing(v.clone()),
1 => Ok(Value::Thing(v.clone())),
// Remote embedded field, so fetch the thing
_ => {
let stm = SelectStatement {
@ -76,15 +74,15 @@ impl Value {
};
match stm.compute(ctx, opt, exe, None).await {
Ok(v) => v.get(ctx, opt, exe, &path.next()).await,
Err(_) => Value::None,
Err(_) => Ok(Value::None),
}
}
},
// Ignore everything else
_ => Value::None,
_ => Ok(Value::None),
},
// No more parts so get the value
None => self.clone(),
None => Ok(self.clone()),
}
}
}
@ -100,11 +98,9 @@ mod tests {
#[tokio::test]
async fn get_none() {
let (ctx, opt, mut exe) = mock();
let idi = Idiom {
parts: vec![],
};
let idi = Idiom::default();
let val = Value::parse("{ test: { other: null, something: 123 } }");
let res = val.get(&ctx, &opt, &mut exe, &idi).await;
let res = val.get(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
@ -113,7 +109,7 @@ mod tests {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::parse("test.something");
let val = Value::parse("{ test: { other: null, something: 123 } }");
let res = val.get(&ctx, &opt, &mut exe, &idi).await;
let res = val.get(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, Value::from(123));
}
@ -122,7 +118,7 @@ mod tests {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::parse("test.other");
let val = Value::parse("{ test: { other: test:tobie, something: 123 } }");
let res = val.get(&ctx, &opt, &mut exe, &idi).await;
let res = val.get(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(
res,
Value::from(Thing {
@ -137,7 +133,7 @@ mod tests {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::parse("test.something[1]");
let val = Value::parse("{ test: { something: [123, 456, 789] } }");
let res = val.get(&ctx, &opt, &mut exe, &idi).await;
let res = val.get(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, Value::from(456));
}
@ -146,7 +142,7 @@ mod tests {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::parse("test.something[1]");
let val = Value::parse("{ test: { something: [test:tobie, test:jaime] } }");
let res = val.get(&ctx, &opt, &mut exe, &idi).await;
let res = val.get(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(
res,
Value::from(Thing {
@ -161,7 +157,7 @@ mod tests {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::parse("test.something[1].age");
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = val.get(&ctx, &opt, &mut exe, &idi).await;
let res = val.get(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, Value::from(36));
}
@ -170,7 +166,7 @@ mod tests {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::parse("test.something[*].age");
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = val.get(&ctx, &opt, &mut exe, &idi).await;
let res = val.get(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, Value::from(vec![34, 36]));
}
@ -179,7 +175,7 @@ mod tests {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::parse("test.something[WHERE age > 35].age");
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = val.get(&ctx, &opt, &mut exe, &idi).await;
let res = val.get(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, Value::from(vec![36]));
}
@ -188,7 +184,7 @@ mod tests {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::parse("test.something[WHERE age > 35]");
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = val.get(&ctx, &opt, &mut exe, &idi).await;
let res = val.get(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(
res,
Value::from(vec![Value::from(map! {

View file

@ -1,6 +1,7 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::number::Number;
use crate::sql::value::Value;
@ -13,40 +14,24 @@ impl Value {
exe: &mut Executor,
path: &Idiom,
val: Value,
) {
match self.get(ctx, opt, exe, path).await {
) -> Result<(), Error> {
match self.get(ctx, opt, exe, path).await? {
Value::Number(v) => match val {
Value::Number(x) => {
self.set(ctx, opt, exe, path, Value::from(v + x)).await;
()
}
_ => (),
Value::Number(x) => self.set(ctx, opt, exe, path, Value::from(v + x)).await,
_ => Ok(()),
},
Value::Array(v) => match val {
Value::Array(x) => {
self.set(ctx, opt, exe, path, Value::from(v + x)).await;
()
}
x => {
self.set(ctx, opt, exe, path, Value::from(v + x)).await;
()
}
Value::Array(x) => self.set(ctx, opt, exe, path, Value::from(v + x)).await,
x => self.set(ctx, opt, exe, path, Value::from(v + x)).await,
},
Value::None => match val {
Value::Number(x) => {
self.set(ctx, opt, exe, path, Value::from(Number::from(0) + x)).await;
()
}
Value::Array(x) => {
self.set(ctx, opt, exe, path, Value::from(x)).await;
()
}
x => {
self.set(ctx, opt, exe, path, Value::from(vec![x])).await;
()
self.set(ctx, opt, exe, path, Value::from(Number::from(0) + x)).await
}
Value::Array(x) => self.set(ctx, opt, exe, path, Value::from(x)).await,
x => self.set(ctx, opt, exe, path, Value::from(vec![x])).await,
},
_ => (),
_ => Ok(()),
}
}
}
@ -64,7 +49,7 @@ mod tests {
let idi = Idiom::parse("other");
let mut val = Value::parse("{ test: 100 }");
let res = Value::parse("{ test: 100, other: +10 }");
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(10)).await;
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(10)).await.unwrap();
assert_eq!(res, val);
}
@ -74,7 +59,7 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: 100 }");
let res = Value::parse("{ test: 110 }");
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(10)).await;
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(10)).await.unwrap();
assert_eq!(res, val);
}
@ -84,7 +69,7 @@ mod tests {
let idi = Idiom::parse("test[1]");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 210, 300] }");
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(10)).await;
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(10)).await.unwrap();
assert_eq!(res, val);
}
@ -94,7 +79,7 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 200, 300] }");
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(200)).await;
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(200)).await.unwrap();
assert_eq!(res, val);
}
@ -104,7 +89,9 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 200, 300, 400, 500] }");
val.increment(&ctx, &opt, &mut exe, &idi, Value::parse("[100, 300, 400, 500]")).await;
val.increment(&ctx, &opt, &mut exe, &idi, Value::parse("[100, 300, 400, 500]"))
.await
.unwrap();
assert_eq!(res, val);
}
}

View file

@ -1,12 +1,18 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::part::Part;
use crate::sql::value::Value;
impl Value {
pub async fn last(&self, ctx: &Runtime, opt: &Options<'_>, exe: &mut Executor) -> Self {
pub async fn last(
&self,
ctx: &Runtime,
opt: &Options<'_>,
exe: &mut Executor,
) -> Result<Self, Error> {
self.get(ctx, opt, exe, &Idiom::from(vec![Part::Last])).await
}
}

26
src/sql/value/merge.rs Normal file
View file

@ -0,0 +1,26 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::object::Object;
use crate::sql::value::Value;
impl Value {
pub async fn merge(
&mut self,
ctx: &Runtime,
opt: &Options<'_>,
exe: &mut Executor,
val: &Object,
) -> Result<(), Error> {
match val.compute(ctx, opt, exe, Some(self)).await? {
Value::Object(v) => {
for (k, v) in v.value.into_iter() {
self.set(ctx, opt, exe, &k.into(), v).await?;
}
Ok(())
}
_ => unreachable!(),
}
}
}

View file

@ -1,6 +1,7 @@
pub use self::value::*;
pub mod array;
pub mod clear;
pub mod decrement;
pub mod del;
pub mod diff;
@ -9,7 +10,9 @@ pub mod first;
pub mod get;
pub mod increment;
pub mod last;
pub mod merge;
pub mod object;
pub mod patch;
pub mod replace;
pub mod set;
pub mod value;

View file

@ -1,6 +1,7 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::object::Object;
use crate::sql::value::Value;
@ -12,8 +13,36 @@ impl Value {
opt: &Options<'_>,
exe: &mut Executor,
path: &Idiom,
) {
) -> Result<(), Error> {
let val = Value::from(Object::default());
self.set(ctx, opt, exe, path, val).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dbs::test::mock;
use crate::sql::test::Parse;
#[tokio::test]
async fn object_none() {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::default();
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{}");
val.object(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
#[tokio::test]
async fn object_path() {
let (ctx, opt, mut exe) = mock();
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: {} }");
val.object(&ctx, &opt, &mut exe, &idi).await.unwrap();
assert_eq!(res, val);
}
}

View file

@ -1,11 +1,18 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::operation::Operations;
use crate::sql::value::Value;
impl Value {
pub fn patch(self, _: &Runtime, _: &Options, _: &mut Executor, ops: Operations) -> Self {
self
pub async fn patch(
&self,
_ctx: &Runtime,
_opt: &Options<'_>,
_exe: &mut Executor,
ops: Operations,
) -> Result<(), Error> {
Ok(())
}
}

38
src/sql/value/replace.rs Normal file
View file

@ -0,0 +1,38 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::object::Object;
use crate::sql::value::Value;
impl Value {
pub async fn replace(
&mut self,
_ctx: &Runtime,
_opt: &Options<'_>,
_exe: &mut Executor,
val: &Object,
) -> Result<(), Error> {
// Clear all entries
*self = Value::from(val.clone());
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dbs::test::mock;
use crate::sql::test::Parse;
#[tokio::test]
async fn replace() {
let (ctx, opt, mut exe) = mock();
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ other: true }");
let obj = Object::from(map! {String::from("other") => Value::from(true) });
val.replace(&ctx, &opt, &mut exe, &obj).await.unwrap();
assert_eq!(res, val);
}
}

View file

@ -1,6 +1,7 @@
use crate::dbs::Executor;
use crate::dbs::Options;
use crate::dbs::Runtime;
use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::object::Object;
use crate::sql::part::Part;
@ -16,7 +17,7 @@ impl Value {
exe: &mut Executor,
path: &Idiom,
val: Value,
) {
) -> Result<(), Error> {
match path.parts.first() {
// Get the current path part
Some(p) => match self {
@ -26,48 +27,51 @@ impl Value {
Some(v) if v.is_some() => v.set(ctx, opt, exe, &path.next(), val).await,
_ => {
let mut obj = Value::from(Object::default());
obj.set(ctx, opt, exe, &path.next(), val).await;
v.insert(&p.name, obj)
obj.set(ctx, opt, exe, &path.next(), val).await?;
v.insert(&p.name, obj);
Ok(())
}
},
_ => (),
_ => Ok(()),
},
// Current path part is an array
Value::Array(v) => match p {
Part::All => {
for v in &mut v.value {
v.set(ctx, opt, exe, &path.next(), val.clone()).await
v.set(ctx, opt, exe, &path.next(), val.clone()).await?;
}
Ok(())
}
Part::First => match v.value.first_mut() {
Some(v) => v.set(ctx, opt, exe, &path.next(), val).await,
None => (),
None => Ok(()),
},
Part::Last => match v.value.last_mut() {
Some(v) => v.set(ctx, opt, exe, &path.next(), val).await,
None => (),
None => Ok(()),
},
Part::Index(i) => match v.value.get_mut(i.to_usize()) {
Some(v) => v.set(ctx, opt, exe, &path.next(), val).await,
None => (),
None => Ok(()),
},
Part::Where(w) => {
for v in &mut v.value {
match w.compute(ctx, opt, exe, Some(&v)).await {
Ok(x) if x.is_truthy() => {
v.set(ctx, opt, exe, &path.next(), val.clone()).await
}
_ => (),
};
if w.compute(ctx, opt, exe, Some(&v)).await?.is_truthy() {
v.set(ctx, opt, exe, &path.next(), val.clone()).await?;
}
}
_ => (),
Ok(())
}
_ => Ok(()),
},
// Ignore everything else
_ => (),
_ => Ok(()),
},
// No more parts so set the value
None => *self = val.clone(),
None => {
*self = val.clone();
Ok(())
}
}
}
}
@ -82,12 +86,10 @@ mod tests {
#[tokio::test]
async fn set_none() {
let (ctx, opt, mut exe) = mock();
let idi = Idiom {
parts: vec![],
};
let idi = Idiom::default();
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("999");
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await;
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await.unwrap();
assert_eq!(res, val);
}
@ -97,7 +99,7 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: 999 }");
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await;
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await.unwrap();
assert_eq!(res, val);
}
@ -107,7 +109,7 @@ mod tests {
let idi = Idiom::parse("test.something");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: { other: null, something: 999 } }");
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await;
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await.unwrap();
assert_eq!(res, val);
}
@ -117,7 +119,7 @@ mod tests {
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.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await;
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await.unwrap();
assert_eq!(res, val);
}
@ -127,7 +129,7 @@ mod tests {
let idi = Idiom::parse("test.other.something");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: { other: { something: 999 }, something: 123 } }");
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await;
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await.unwrap();
assert_eq!(res, val);
}
@ -137,7 +139,7 @@ mod tests {
let idi = Idiom::parse("test.something[1]");
let mut val = Value::parse("{ test: { something: [123, 456, 789] } }");
let res = Value::parse("{ test: { something: [123, 999, 789] } }");
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await;
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999)).await.unwrap();
assert_eq!(res, val);
}
@ -147,7 +149,7 @@ mod tests {
let idi = Idiom::parse("test.something[1].age");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 34 }, { age: 21 }] } }");
val.set(&ctx, &opt, &mut exe, &idi, Value::from(21)).await;
val.set(&ctx, &opt, &mut exe, &idi, Value::from(21)).await.unwrap();
assert_eq!(res, val);
}
@ -157,7 +159,7 @@ mod tests {
let idi = Idiom::parse("test.something[*].age");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 21 }, { age: 21 }] } }");
val.set(&ctx, &opt, &mut exe, &idi, Value::from(21)).await;
val.set(&ctx, &opt, &mut exe, &idi, Value::from(21)).await.unwrap();
assert_eq!(res, val);
}
@ -167,7 +169,7 @@ mod tests {
let idi = Idiom::parse("test.something[WHERE age > 35].age");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 34 }, { age: 21 }] } }");
val.set(&ctx, &opt, &mut exe, &idi, Value::from(21)).await;
val.set(&ctx, &opt, &mut exe, &idi, Value::from(21)).await.unwrap();
assert_eq!(res, val);
}
@ -177,7 +179,7 @@ mod tests {
let idi = Idiom::parse("test.something[WHERE age > 35]");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 34 }, 21] } }");
val.set(&ctx, &opt, &mut exe, &idi, Value::from(21)).await;
val.set(&ctx, &opt, &mut exe, &idi, Value::from(21)).await.unwrap();
assert_eq!(res, val);
}
}