diff --git a/src/sql/idiom.rs b/src/sql/idiom.rs index 88e59ae9..7cf272bb 100644 --- a/src/sql/idiom.rs +++ b/src/sql/idiom.rs @@ -32,6 +32,14 @@ pub struct Idiom { pub parts: Vec, } +impl From for Idiom { + fn from(v: String) -> Self { + Idiom { + parts: vec![Part::from(v)], + } + } +} + impl From> for Idiom { fn from(v: Vec) -> Self { Idiom { @@ -59,7 +67,7 @@ impl Idiom { ) -> Result { 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), } diff --git a/src/sql/param.rs b/src/sql/param.rs index 850c8afa..86f86063 100644 --- a/src/sql/param.rs +++ b/src/sql/param.rs @@ -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), diff --git a/src/sql/subquery.rs b/src/sql/subquery.rs index 9da43b80..03eea3cf 100644 --- a/src/sql/subquery.rs +++ b/src/sql/subquery.rs @@ -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(), }, } diff --git a/src/sql/value/array.rs b/src/sql/value/array.rs index 56873814..0ad7eedf 100644 --- a/src/sql/value/array.rs +++ b/src/sql/value/array.rs @@ -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); } } diff --git a/src/sql/value/clear.rs b/src/sql/value/clear.rs new file mode 100644 index 00000000..d4c7601d --- /dev/null +++ b/src/sql/value/clear.rs @@ -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); + } +} diff --git a/src/sql/value/decrement.rs b/src/sql/value/decrement.rs index dd6eb624..c704fc83 100644 --- a/src/sql/value/decrement.rs +++ b/src/sql/value/decrement.rs @@ -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); } } diff --git a/src/sql/value/del.rs b/src/sql/value/del.rs index 5c638295..9a219aed 100644 --- a/src/sql/value/del.rs +++ b/src/sql/value/del.rs @@ -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); } } diff --git a/src/sql/value/first.rs b/src/sql/value/first.rs index 19eea0c3..4b1039a9 100644 --- a/src/sql/value/first.rs +++ b/src/sql/value/first.rs @@ -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.get(ctx, opt, exe, &Idiom::from(vec![Part::First])).await } } diff --git a/src/sql/value/get.rs b/src/sql/value/get.rs index 8dba84c9..3137bed9 100644 --- a/src/sql/value/get.rs +++ b/src/sql/value/get.rs @@ -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 { 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! { diff --git a/src/sql/value/increment.rs b/src/sql/value/increment.rs index af2851b5..fe06e818 100644 --- a/src/sql/value/increment.rs +++ b/src/sql/value/increment.rs @@ -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); } } diff --git a/src/sql/value/last.rs b/src/sql/value/last.rs index c3de90af..9aeac4ed 100644 --- a/src/sql/value/last.rs +++ b/src/sql/value/last.rs @@ -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.get(ctx, opt, exe, &Idiom::from(vec![Part::Last])).await } } diff --git a/src/sql/value/merge.rs b/src/sql/value/merge.rs new file mode 100644 index 00000000..55aa77c7 --- /dev/null +++ b/src/sql/value/merge.rs @@ -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!(), + } + } +} diff --git a/src/sql/value/mod.rs b/src/sql/value/mod.rs index a97ac0d9..523fedba 100644 --- a/src/sql/value/mod.rs +++ b/src/sql/value/mod.rs @@ -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; diff --git a/src/sql/value/object.rs b/src/sql/value/object.rs index c4cca4c3..fc50d44f 100644 --- a/src/sql/value/object.rs +++ b/src/sql/value/object.rs @@ -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); + } +} diff --git a/src/sql/value/patch.rs b/src/sql/value/patch.rs index 6ab28066..40ce891f 100644 --- a/src/sql/value/patch.rs +++ b/src/sql/value/patch.rs @@ -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(()) } } diff --git a/src/sql/value/replace.rs b/src/sql/value/replace.rs new file mode 100644 index 00000000..57557fa5 --- /dev/null +++ b/src/sql/value/replace.rs @@ -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); + } +} diff --git a/src/sql/value/set.rs b/src/sql/value/set.rs index e1b95388..6db93995 100644 --- a/src/sql/value/set.rs +++ b/src/sql/value/set.rs @@ -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); } }