use crate::pointer::{Pointer, Step};
use serde_value::Value;
pub enum ValuePointer<'a> {
Existing(&'a Value),
NewUnder(&'a Value, usize),
}
pub enum ValuePointerMut<'a> {
Existing(&'a mut Value),
NewUnder(&'a mut Value, usize),
}
pub(crate) fn traverse<'a>(val: &'a Value, pointer: &Pointer) -> Option<ValuePointer<'a>> {
let mut it = pointer.clone().into_iter();
match it.next() {
Some(step) => _traverse(val, &step, &mut it),
None => Some(ValuePointer::Existing(val)),
}
}
pub(crate) fn traverse_mut<'a>(
val: &'a mut Value,
pointer: &Pointer,
) -> Option<ValuePointerMut<'a>> {
let mut it = pointer.clone().into_iter();
match it.next() {
Some(step) => _traverse_mut(val, &step, &mut it),
None => Some(ValuePointerMut::Existing(val)),
}
}
fn _traverse_mut<'a>(
parent: &'a mut Value,
step: &Step,
steps: &mut dyn Iterator<Item = Step>,
) -> Option<ValuePointerMut<'a>> {
let child = match step {
Step::Name(name) => match parent {
Value::Map(ref mut map) => map.get_mut(&Value::String(name.clone())),
_ => None,
},
Step::Index(index) => match parent {
Value::Seq(ref mut seq) => seq.get_mut(*index),
_ => None,
},
Step::NewElement => {
let seq_len = match parent {
Value::Seq(seq) => {
if let Some(_) = steps.next() {
Some(seq.len())
} else {
None
}
}
_ => None,
};
return if let Some(len) = seq_len {
Some(ValuePointerMut::NewUnder(parent, len))
} else {
None
};
}
};
match steps.next() {
Some(child_step) => _traverse_mut(child.unwrap(), &child_step, steps),
None => Some(ValuePointerMut::Existing(child.unwrap())),
}
}
fn _traverse<'a>(
parent: &'a Value,
step: &Step,
steps: &mut dyn Iterator<Item = Step>,
) -> Option<ValuePointer<'a>> {
let child = match step {
Step::Name(name) => match parent {
Value::Map(ref map) => map.get(&Value::String(name.clone())),
_ => None,
},
Step::Index(index) => match parent {
Value::Seq(ref seq) => seq.get(*index),
_ => None,
},
Step::NewElement => {
return match parent {
Value::Seq(ref seq) => match steps.next() {
None => Some(ValuePointer::NewUnder(parent, seq.len())),
_ => None,
},
_ => None,
}
}
};
match steps.next() {
Some(child_step) => _traverse(child.unwrap(), &child_step, steps),
None => Some(ValuePointer::Existing(child.unwrap())),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
use std::mem;
use std::str::FromStr;
#[test]
fn finds_all_data() {
let val = Value::Map(BTreeMap::default());
assert_eq!(&val, Pointer::from_str("#").unwrap().find(&val).unwrap());
}
#[test]
fn finds_concrete_data() {
let val = Value::Map(
vec![
(Value::String("k1".into()), Value::String("v1".into())),
(
Value::String("k2".into()),
Value::Seq(vec![Value::I32(42), Value::Bool(true)]),
),
]
.into_iter()
.collect(),
);
assert_eq!(
Value::Bool(true),
*Pointer::from_str("/k2/1").unwrap().find(&val).unwrap()
);
}
#[test]
fn find_new_element() {
let val = Value::Map(
vec![
(Value::String("k1".into()), Value::String("v1".into())),
(
Value::String("k2".into()),
Value::Seq(vec![Value::Bool(true)]),
),
]
.into_iter()
.collect(),
);
let found = Pointer::from_str("/k2/-").unwrap().traverse(&val).unwrap();
match found {
ValuePointer::NewUnder(parent, idx) => {
assert_eq!(1, idx);
match parent {
Value::Seq(ar) => {
assert_eq!(1, ar.len());
}
_ => panic!("Should have found an array of size 1"),
}
}
_ => panic!("Should have found value pointer to a new value."),
}
}
#[test]
fn can_mutate() {
let mut val = Value::Map(
vec![
(Value::String("k1".into()), Value::String("v1".into())),
(
Value::String("k2".into()),
Value::Seq(vec![Value::Bool(true)]),
),
]
.into_iter()
.collect(),
);
let found = Pointer::from_str("/k2/0")
.unwrap()
.traverse_mut(&mut val)
.unwrap();
match found {
ValuePointerMut::Existing(v) => {
mem::replace(v, Value::String("boo".into()));
}
_ => {}
}
match val {
Value::Map(map) => {
match map.get(&Value::String("k2".into())).unwrap() {
Value::Seq(seq) => match seq.get(0).unwrap() {
Value::String(s) => {
assert_eq!(s.as_str(), "boo");
}
_ => panic!("Should have found a string"),
},
_ => panic!("Should have found a seq"),
};
}
_ => panic!("Should see the map"),
}
}
}