use crate::dbs::Executor; use crate::dbs::Options; use crate::dbs::Process; use crate::dbs::Runtime; use crate::sql::field::{Field, Fields}; use crate::sql::idiom::Idiom; use crate::sql::part::Part; use crate::sql::statements::select::SelectStatement; use crate::sql::value::{Value, Values}; impl Value { pub fn get(&self, ctx: &Runtime, opt: &Options, exe: &mut Executor, path: &Idiom) -> Self { 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 v.value.get(&p.name) { Some(v) => v.get(ctx, opt, exe, &path.next()), None => Value::None, }, _ => Value::None, }, // Current path part is an array Value::Array(v) => match p { Part::All => v .value .iter() .map(|v| v.get(ctx, opt, exe, &path.next())) .collect::>() .into(), Part::First => match v.value.first() { Some(v) => v.get(ctx, opt, exe, &path.next()), None => Value::None, }, Part::Last => match v.value.last() { Some(v) => v.get(ctx, opt, exe, &path.next()), None => Value::None, }, Part::Index(i) => match v.value.get(i.to_usize()) { Some(v) => v.get(ctx, opt, exe, &path.next()), None => Value::None, }, Part::Where(w) => v .value .iter() .filter_map(|v| match w.process(ctx, opt, exe, Some(v)) { Ok(v) if v.is_truthy() => Some(v.get(ctx, opt, exe, &path.next())), _ => None, }) .collect::>() .into(), _ => 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()), // Remote embedded field, so fetch the thing _ => { let stm = SelectStatement { expr: Fields(vec![Field::All]), what: Values(vec![Value::Thing(v.clone())]), ..SelectStatement::default() }; match stm.process(ctx, opt, exe, None) { Ok(v) => v.get(ctx, opt, exe, &path.next()), Err(_) => Value::None, } } }, // Ignore everything else _ => Value::None, }, // No more parts so get the value None => self.clone(), } } } #[cfg(test)] mod tests { use super::*; use crate::dbs::test::mock; use crate::sql::test::Parse; use crate::sql::thing::Thing; #[test] fn get_none() { let (ctx, opt, mut exe) = mock(); let idi = Idiom { parts: vec![], }; let val = Value::parse("{ test: { other: null, something: 123 } }"); let res = val.get(&ctx, &opt, &mut exe, &idi); assert_eq!(res, val); } #[test] fn get_basic() { 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); assert_eq!(res, Value::from(123)); } #[test] fn get_thing() { 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); assert_eq!( res, Value::from(Thing { tb: String::from("test"), id: String::from("tobie") }) ); } #[test] fn get_array() { 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); assert_eq!(res, Value::from(456)); } #[test] fn get_array_thing() { 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); assert_eq!( res, Value::from(Thing { tb: String::from("test"), id: String::from("jaime") }) ); } #[test] fn get_array_field() { 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); assert_eq!(res, Value::from(36)); } #[test] fn get_array_fields() { 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); assert_eq!(res, Value::from(vec![34, 36])); } }