commit
7454ccdfe2
@ -0,0 +1,4 @@ |
||||
/target |
||||
**/*.rs.bk |
||||
Cargo.lock |
||||
.idea/ |
@ -0,0 +1,12 @@ |
||||
[package] |
||||
name = "json_dotpath" |
||||
version = "0.1.0" |
||||
authors = ["Ondřej Hruška <ondra@ondrovo.com>"] |
||||
edition = "2018" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
||||
serde = "1" |
||||
serde_derive = "1" |
||||
serde_json = "1" |
@ -0,0 +1,776 @@ |
||||
use serde::de::DeserializeOwned; |
||||
use serde::Serialize; |
||||
use serde_json::{Map, Value}; |
||||
use std::cmp::Ordering; |
||||
use std::mem; |
||||
|
||||
/// Access and mutate nested JSON elements by dotted paths
|
||||
///
|
||||
/// The path is composed of keys separated by dots, e.g. `foo.bar.1`.
|
||||
///
|
||||
/// Arrays are indexed by numeric strings or special keys (see `dot_get()` and `dot_set()`).
|
||||
///
|
||||
/// This trait is implemented for `serde_json::Value`, specifically the
|
||||
/// `Map`, `Array`, and `Null` variants. Empty path can also be used to access a scalar.
|
||||
pub trait DotPaths { |
||||
/// Get an item by path, if present.
|
||||
///
|
||||
/// JSON `null` becomes `None`, same as unpopulated path.
|
||||
///
|
||||
/// # Special keys
|
||||
/// Arrays can be indexed by special keys for reading:
|
||||
/// - `>` ... last element
|
||||
///
|
||||
/// # Panics
|
||||
/// - If the path attempts to index into a scalar (e.g. `"foo.bar"` in `{"foo": 123}`)
|
||||
/// - If the path uses invalid key in an array or map
|
||||
fn dot_get<T>(&self, path: &str) -> Option<T> |
||||
where |
||||
T: DeserializeOwned; |
||||
|
||||
/// Get an item, or a default value.
|
||||
///
|
||||
/// # Special keys
|
||||
/// see `dot_get()`
|
||||
///
|
||||
/// # Panics
|
||||
/// see `dot_get()`
|
||||
fn dot_get_or<T>(&self, path: &str, def: T) -> T |
||||
where |
||||
T: DeserializeOwned, |
||||
{ |
||||
self.dot_get(path).unwrap_or(def) |
||||
} |
||||
|
||||
/// Get an item, or a default value using the Default trait
|
||||
///
|
||||
/// # Special keys
|
||||
/// see `dot_get()`
|
||||
///
|
||||
/// # Panics
|
||||
/// see `dot_get()`
|
||||
fn dot_get_or_default<T>(&self, path: &str) -> T |
||||
where |
||||
T: DeserializeOwned + Default, |
||||
{ |
||||
self.dot_get(path).unwrap_or_default() |
||||
} |
||||
|
||||
/// Get a mutable reference to an item
|
||||
///
|
||||
/// # Special keys
|
||||
/// see `dot_get()`
|
||||
///
|
||||
/// # Panics
|
||||
/// see `dot_get()`
|
||||
fn dot_get_mut(&mut self, path: &str) -> Option<&mut Value>; |
||||
|
||||
/// Insert an item by path.
|
||||
///
|
||||
/// # Special keys
|
||||
/// Arrays can be indexed by special keys:
|
||||
/// - `+` or `>` ... append
|
||||
/// - `-` or `<` ... prepend
|
||||
/// - `>n` ... insert after an index `n`
|
||||
/// - `<n` ... insert before an index `n`
|
||||
///
|
||||
/// # Panics
|
||||
/// see `dot_get()`
|
||||
fn dot_set<T>(&mut self, path: &str, value: T) |
||||
where |
||||
T: Serialize; |
||||
|
||||
/// Replace a value by path with a new value.
|
||||
/// The value types do not have to match.
|
||||
///
|
||||
/// # Panics
|
||||
/// see `dot_get()`
|
||||
fn dot_replace<T, U>(&mut self, path: &str, value: T) -> Option<U> |
||||
where |
||||
T: Serialize, |
||||
U: DeserializeOwned; |
||||
|
||||
/// Get an item using a path, removing it from the store.
|
||||
/// If no item was stored under this path, then None is returned.
|
||||
///
|
||||
/// # Panics
|
||||
/// see `dot_get()`
|
||||
fn dot_take<T>(&mut self, path: &str) -> Option<T> |
||||
where |
||||
T: DeserializeOwned; |
||||
|
||||
/// Remove an item matching a key.
|
||||
/// Returns true if any item was removed.
|
||||
///
|
||||
/// # Panics
|
||||
/// see `dot_get()`
|
||||
fn dot_remove(&mut self, path: &str) -> bool { |
||||
self.dot_take::<Value>(path).is_some() |
||||
} |
||||
} |
||||
|
||||
/// Split the path string by dot, if present.
|
||||
///
|
||||
/// Returns a tuple of (before_dot, after_dot)
|
||||
fn path_split(path: &str) -> (&str, Option<&str>) { |
||||
let dot = path.find('.'); |
||||
match dot { |
||||
None => (path, None), |
||||
Some(pos) => (&path[0..pos], Some(&path[pos + 1..])), |
||||
} |
||||
} |
||||
|
||||
impl DotPaths for serde_json::Value { |
||||
fn dot_get<T>(&self, path: &str) -> Option<T> |
||||
where |
||||
T: DeserializeOwned, |
||||
{ |
||||
match self { |
||||
Value::Array(vec) => vec.dot_get(path), |
||||
Value::Object(map) => map.dot_get(path), |
||||
_ => { |
||||
if path.is_empty() { |
||||
serde_json::from_value(self.to_owned()).ok() |
||||
} else { |
||||
panic!("Node is not array or object!"); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn dot_get_mut(&mut self, path: &str) -> Option<&mut Value> { |
||||
match self { |
||||
Value::Array(vec) => vec.dot_get_mut(path), |
||||
Value::Object(map) => map.dot_get_mut(path), |
||||
_ => { |
||||
if path.is_empty() { |
||||
Some(self) |
||||
} else { |
||||
panic!("Node is not array or object!"); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn dot_set<T>(&mut self, path: &str, value: T) |
||||
where |
||||
T: Serialize, |
||||
{ |
||||
match self { |
||||
Value::Array(vec) => { |
||||
vec.dot_set(path, value); |
||||
} |
||||
Value::Object(map) => { |
||||
map.dot_set(path, value); |
||||
} |
||||
Value::Null => { |
||||
mem::replace(self, new_by_path_root(path, value)); |
||||
} |
||||
_ => { |
||||
if path.is_empty() { |
||||
mem::replace(self, serde_json::to_value(value).expect("Serialize error")); |
||||
} else { |
||||
panic!("Node is not an array, object, or null!"); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn dot_replace<T, U>(&mut self, path: &str, value: T) -> Option<U> |
||||
where |
||||
T: Serialize, |
||||
U: DeserializeOwned, |
||||
{ |
||||
match self { |
||||
Value::Array(vec) => vec.dot_replace(path, value), |
||||
Value::Object(map) => map.dot_replace(path, value), |
||||
Value::Null => { |
||||
self.dot_set(path, value); |
||||
None |
||||
} |
||||
_ => { |
||||
if path.is_empty() { |
||||
let new = serde_json::to_value(value).expect("Serialize error"); |
||||
let old = mem::replace(self, new); |
||||
Some(serde_json::from_value(old).expect("Unserialize error")) |
||||
} else { |
||||
panic!("Node is not an array, object, or null!") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn dot_take<T>(&mut self, path: &str) -> Option<T> |
||||
where |
||||
T: DeserializeOwned, |
||||
{ |
||||
match self { |
||||
Value::Array(vec) => vec.dot_take(path), |
||||
Value::Object(map) => map.dot_take(path), |
||||
Value::Null => None, |
||||
_ => { |
||||
if path.is_empty() { |
||||
let old = mem::replace(self, Value::Null); |
||||
Some(serde_json::from_value(old).expect("Unserialize error")) |
||||
} else { |
||||
panic!("Node is not an array, object, or null!") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Create a Value::Object or Value::Array based on a nested path.
|
||||
///
|
||||
/// Builds the parent path to a non-existent key in set-type operations.
|
||||
fn new_by_path_root<T>(path: &str, value: T) -> Value |
||||
where |
||||
T: Serialize, |
||||
{ |
||||
let (sub1, _) = path_split(path); |
||||
if sub1 == "0" || sub1 == "+" || sub1 == "<" || sub1 == ">" { |
||||
// new vec
|
||||
let mut new_vec = vec![]; |
||||
new_vec.dot_set(path, value); |
||||
Value::Array(new_vec) |
||||
} else { |
||||
// new map
|
||||
let mut new_map = Map::new(); |
||||
new_map.dot_set(path, value); |
||||
Value::Object(new_map) |
||||
} |
||||
} |
||||
|
||||
/// Check if a key is valid to use by dot paths in Value::Object.
|
||||
/// The key must start with an alpha character or underscore and must not contain period.
|
||||
fn validate_map_key(key: &str) { |
||||
if key.parse::<usize>().is_ok() { |
||||
panic!("Numeric keys are not allowed in maps: {}", key); |
||||
} |
||||
|
||||
if !key.starts_with(|p: char| p.is_ascii_alphabetic() || p == '_') || key.contains('.') { |
||||
panic!("Invalid map key: {}", key); |
||||
} |
||||
} |
||||
|
||||
impl DotPaths for serde_json::Map<String, serde_json::Value> { |
||||
fn dot_get<T>(&self, path: &str) -> Option<T> |
||||
where |
||||
T: DeserializeOwned, |
||||
{ |
||||
let (my, sub) = path_split(path); |
||||
validate_map_key(my); |
||||
|
||||
if let Some(sub_path) = sub { |
||||
self.get(my) |
||||
.map(|child| child.dot_get(sub_path)) // this produces Option<Option<T>>
|
||||
.unwrap_or_default() |
||||
} else { |
||||
self.get(my) |
||||
.map(ToOwned::to_owned) |
||||
.map(serde_json::from_value) |
||||
.transpose() // Option<Result> to Result<Option>
|
||||
.unwrap_or_default() // get rid of the result
|
||||
} |
||||
} |
||||
|
||||
fn dot_get_mut(&mut self, path: &str) -> Option<&mut Value> { |
||||
let (my, sub) = path_split(path); |
||||
validate_map_key(my); |
||||
|
||||
if let Some(sub_path) = sub { |
||||
self.get_mut(my) |
||||
.map(|m| m.get_mut(sub_path)) |
||||
.unwrap_or_default() |
||||
} else { |
||||
self.get_mut(my) |
||||
} |
||||
} |
||||
|
||||
fn dot_set<T>(&mut self, path: &str, value: T) |
||||
where |
||||
T: Serialize, |
||||
{ |
||||
let (my, sub) = path_split(path); |
||||
validate_map_key(my); |
||||
|
||||
if let Some(subpath) = sub { |
||||
if self.contains_key(my) { |
||||
self.get_mut(my).unwrap().dot_set(subpath, value); |
||||
} else { |
||||
self.insert(my.into(), new_by_path_root(subpath, value)); |
||||
} |
||||
} else { |
||||
let packed = serde_json::to_value(value).expect("Serialize error"); |
||||
self.insert(my.into(), packed); |
||||
} |
||||
} |
||||
|
||||
fn dot_replace<T, U>(&mut self, path: &str, value: T) -> Option<U> |
||||
where |
||||
T: Serialize, |
||||
U: DeserializeOwned, |
||||
{ |
||||
let (my, sub) = path_split(path); |
||||
validate_map_key(my); |
||||
|
||||
if let Some(subpath) = sub { |
||||
if self.contains_key(my) { |
||||
self.get_mut(my).unwrap().dot_replace(subpath, value) |
||||
} else { |
||||
self.dot_set(path, value); |
||||
None |
||||
} |
||||
} else { |
||||
let packed = serde_json::to_value(value).expect("Serialize error"); |
||||
self.insert(my.to_string(), packed) |
||||
.map(serde_json::from_value) |
||||
.transpose() |
||||
.expect("Unserialize error") |
||||
} |
||||
} |
||||
|
||||
fn dot_take<T>(&mut self, path: &str) -> Option<T> |
||||
where |
||||
T: DeserializeOwned, |
||||
{ |
||||
let (my, sub) = path_split(path); |
||||
validate_map_key(my); |
||||
|
||||
if let Some(subpath) = sub { |
||||
if let Some(item) = self.get_mut(my) { |
||||
item.dot_take(subpath) |
||||
} else { |
||||
None |
||||
} |
||||
} else { |
||||
self.remove(my) |
||||
.map(serde_json::from_value) |
||||
.transpose() // Option<Result> to Result<Option>
|
||||
.expect("Unserialize error") // get rid of the result
|
||||
} |
||||
} |
||||
} |
||||
|
||||
impl DotPaths for Vec<serde_json::Value> { |
||||
fn dot_get<T>(&self, path: &str) -> Option<T> |
||||
where |
||||
T: DeserializeOwned, |
||||
{ |
||||
let (my, sub) = path_split(path); |
||||
|
||||
let index: usize = if my == ">" { |
||||
self.len() - 1 |
||||
} else { |
||||
my.parse().unwrap() |
||||
}; |
||||
|
||||
if let Some(subpath) = sub { |
||||
self.get(index) |
||||
.map(ToOwned::to_owned) |
||||
.map(|child| child.dot_get(subpath)) |
||||
.unwrap_or_default() |
||||
} else { |
||||
self.get(index) |
||||
.map(ToOwned::to_owned) |
||||
.map(serde_json::from_value) |
||||
.transpose() |
||||
.unwrap_or_default() |
||||
} |
||||
} |
||||
|
||||
fn dot_get_mut(&mut self, path: &str) -> Option<&mut Value> { |
||||
let (my, sub) = path_split(path); |
||||
|
||||
let my: usize = my |
||||
.parse() |
||||
.unwrap_or_else(|_| panic!("Cannot index an array by \"{}\"", my)); |
||||
|
||||
if let Some(subpath) = sub { |
||||
self.get_mut(my) |
||||
.map(|m: &mut Value| m.get_mut(subpath)) |
||||
.unwrap_or_default() |
||||
} else { |
||||
self.get_mut(my) |
||||
} |
||||
} |
||||
|
||||
fn dot_set<T>(&mut self, path: &str, value: T) |
||||
where |
||||
T: Serialize, |
||||
{ |
||||
let (mut my, sub) = path_split(path); |
||||
|
||||
if my.is_empty() { |
||||
panic!("Cannot index array by empty key"); |
||||
} |
||||
|
||||
let mut insert = false; |
||||
let mut add = 0isize; |
||||
|
||||
if my.starts_with('<') { |
||||
// "<n" means insert before n
|
||||
// "<" means prepend
|
||||
insert = true; |
||||
my = &my[1..]; |
||||
} else if my.starts_with('>') { |
||||
insert = true; |
||||
my = &my[1..]; |
||||
if my.is_empty() { |
||||
// ">" means append
|
||||
add = self.len() as isize; |
||||
} else { |
||||
// ">n" means insert after
|
||||
add = 1; |
||||
} |
||||
} |
||||
|
||||
let mut my = match my { |
||||
// append
|
||||
"+" => self.len(), |
||||
// prepend
|
||||
"-" => { |
||||
insert = true; |
||||
0 |
||||
} |
||||
// empty (this happens only if the < or > was removed above)
|
||||
"" => 0, |
||||
_ => my |
||||
.parse() |
||||
.unwrap_or_else(|_| panic!("Cannot index an array by \"{}\"", my)), |
||||
}; |
||||
|
||||
my = (my as isize + add) as usize; |
||||
|
||||
match my.cmp(&self.len()) { |
||||
Ordering::Less => { |
||||
if insert { |
||||
// insert
|
||||
if let Some(subpath) = sub { |
||||
self.insert(my, new_by_path_root(subpath, value)); |
||||
} else { |
||||
self.insert(my, serde_json::to_value(value).expect("Serialize error")); |
||||
} |
||||
} else { |
||||
// replace
|
||||
if let Some(subpath) = sub { |
||||
self[my].dot_set(subpath, value); |
||||
} else { |
||||
self[my] = serde_json::to_value(value).expect("Serialize error"); |
||||
} |
||||
} |
||||
} |
||||
Ordering::Equal => { |
||||
if let Some(subpath) = sub { |
||||
self.push(new_by_path_root(subpath, value)) |
||||
} else { |
||||
self.push(serde_json::to_value(value).expect("Serialize error")) |
||||
} |
||||
} |
||||
Ordering::Greater => { |
||||
panic!("Index out of bounds."); |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn dot_replace<T, U>(&mut self, path: &str, value: T) -> Option<U> |
||||
where |
||||
T: Serialize, |
||||
U: DeserializeOwned, |
||||
{ |
||||
let (my, sub) = path_split(path); |
||||
let index = my |
||||
.parse::<usize>() |
||||
.unwrap_or_else(|_| panic!("Cannot index an array by \"{}\"", my)); |
||||
|
||||
if let Some(subpath) = sub { |
||||
if index < self.len() { |
||||
let a_mut: &mut Value = self.get_mut(index).unwrap(); |
||||
a_mut.dot_replace(subpath, value) |
||||
} else { |
||||
// use the append implementation & validations from dot_set
|
||||
self.dot_set(path, value); |
||||
None |
||||
} |
||||
} else { |
||||
// No subpath
|
||||
if index < self.len() { |
||||
let other = serde_json::to_value(value).expect("Serialize error"); |
||||
let old = mem::replace(&mut self[index], other); |
||||
Some(serde_json::from_value(old).expect("Deserialize error")) |
||||
} else { |
||||
// use the append implementation & validations from dot_set
|
||||
self.dot_set(my, value); |
||||
None |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn dot_take<T>(&mut self, path: &str) -> Option<T> |
||||
where |
||||
T: DeserializeOwned, |
||||
{ |
||||
let (my, sub) = path_split(path); |
||||
let my = my |
||||
.parse() |
||||
.unwrap_or_else(|_| panic!("Cannot index an array by \"{}\"", my)); |
||||
|
||||
if my >= self.len() { |
||||
return None; |
||||
} |
||||
|
||||
if let Some(subpath) = sub { |
||||
let value: &mut Value = &mut self[my]; |
||||
value.dot_take(subpath) |
||||
} else { |
||||
// bounds are checked above
|
||||
serde_json::from_value(self.remove(my)).expect("Deserialize error") |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use crate::DotPaths; |
||||
use serde_json::json; |
||||
use serde_json::Value; |
||||
|
||||
#[test] |
||||
fn get_scalar_with_empty_path() { |
||||
let value = Value::String("Hello".to_string()); |
||||
assert_eq!(Some("Hello".to_string()), value.dot_get("")); |
||||
} |
||||
|
||||
#[test] |
||||
#[should_panic] |
||||
fn cant_get_scalar_with_path() { |
||||
let value = Value::String("Hello".to_string()); |
||||
let _ = value.dot_get::<Value>("1.2.3"); |
||||
} |
||||
|
||||
#[test] |
||||
fn set_vec() { |
||||
let mut vec = Value::Array(vec![]); |
||||
vec.dot_set("0", "first"); |
||||
vec.dot_set("0", "second"); // this replaces it
|
||||
vec.dot_set("1", "third"); |
||||
vec.dot_set("+", "append"); |
||||
vec.dot_set(">", "append2"); |
||||
vec.dot_set("-", "prepend"); |
||||
vec.dot_set("<", "prepend2"); |
||||
vec.dot_set("<0", "prepend3"); |
||||
vec.dot_set(">1", "insert after 1"); |
||||
vec.dot_set(">0", "after0"); |
||||
vec.dot_set("<2", "before2"); |
||||
assert_eq!( |
||||
json!([ |
||||
"prepend3", |
||||
"after0", |
||||
"before2", |
||||
"prepend2", |
||||
"insert after 1", |
||||
"prepend", |
||||
"second", |
||||
"third", |
||||
"append", |
||||
"append2" |
||||
]), |
||||
vec |
||||
); |
||||
} |
||||
|
||||
#[test] |
||||
#[should_panic] |
||||
fn set_vec_panic_bad_index() { |
||||
let mut vec = Value::Array(vec![]); |
||||
vec.dot_set("1", "first"); |
||||
} |
||||
|
||||
#[test] |
||||
#[should_panic] |
||||
fn set_vec_panic_index_not_numeric() { |
||||
let mut vec = Value::Array(vec![]); |
||||
vec.dot_set("abc", "first"); |
||||
} |
||||
|
||||
#[test] |
||||
fn set_vec_spawn() { |
||||
let mut vec = Value::Array(vec![]); |
||||
|
||||
vec.dot_set("0.0.0", "first"); |
||||
vec.dot_set("+", "append"); |
||||
vec.dot_set("<1", "in between"); |
||||
assert_eq!(json!([[["first"]], "in between", "append"]), vec); |
||||
|
||||
vec.dot_set("0.0.+", "second"); |
||||
assert_eq!(json!([[["first", "second"]], "in between", "append"]), vec); |
||||
|
||||
vec.dot_set("0.+", "mmm"); |
||||
assert_eq!( |
||||
json!([[["first", "second"], "mmm"], "in between", "append"]), |
||||
vec |
||||
); |
||||
|
||||
vec.dot_set("0.+.0", "xyz"); |
||||
assert_eq!( |
||||
json!([ |
||||
[["first", "second"], "mmm", ["xyz"]], |
||||
"in between", |
||||
"append" |
||||
]), |
||||
vec |
||||
); |
||||
|
||||
// append and prepend can also spawn a new array
|
||||
let mut vec = Value::Array(vec![]); |
||||
vec.dot_set(">.>.>", "first"); |
||||
assert_eq!(json!([[["first"]]]), vec); |
||||
vec.dot_set(">.<.>", "second"); |
||||
assert_eq!(json!([[["first"]], [["second"]]]), vec); |
||||
} |
||||
|
||||
#[test] |
||||
fn get_vec() { |
||||
let vec = json!([ |
||||
[["first", "second"], "mmm", ["xyz"]], |
||||
"in between", |
||||
"append" |
||||
]); |
||||
assert_eq!(Some("first".to_string()), vec.dot_get("0.0.0")); |
||||
assert_eq!(Some("second".to_string()), vec.dot_get("0.0.1")); |
||||
|
||||
// this one doesn't exist
|
||||
assert_eq!(None, vec.dot_get::<String>("0.0.3")); |
||||
|
||||
// get last
|
||||
assert_eq!(Some(json!(["xyz"])), vec.dot_get("0.>")); |
||||
|
||||
// retrieve a Value
|
||||
assert_eq!( |
||||
Some(json!([["first", "second"], "mmm", ["xyz"]])), |
||||
vec.dot_get("0") |
||||
); |
||||
assert_eq!(Some(json!(["first", "second"])), vec.dot_get("0.0")); |
||||
} |
||||
|
||||
#[test] |
||||
#[should_panic] |
||||
fn get_vec_panic_index_scalar() { |
||||
let vec = json!([ |
||||
[["first", "second"], "mmm", ["xyz"]], |
||||
"in between", |
||||
"append" |
||||
]); |
||||
let _ = vec.dot_get::<Value>("0.0.1.4"); |
||||
} |
||||
|
||||
#[test] |
||||
fn take_from_vec() { |
||||
let mut vec = json!(["a", "b", "c"]); |
||||
|
||||
// test Take
|
||||
assert_eq!(Some("b".to_string()), vec.dot_take("1")); |
||||
assert_eq!(None, vec.dot_take::<Value>("4")); |
||||
assert_eq!(json!(["a", "c"]), vec); |
||||
|
||||
let mut vec = json!([[["a"], "b"], "c"]); |
||||
assert_eq!(Some("a".to_string()), vec.dot_take("0.0.0")); |
||||
assert_eq!(json!([[[], "b"], "c"]), vec); // empty vec is left in place
|
||||
} |
||||
|
||||
#[test] |
||||
fn remove_from_vec() { |
||||
let mut vec = json!(["a", "b", "c"]); |
||||
|
||||
assert_eq!(true, vec.dot_remove("1")); |
||||
assert_eq!(false, vec.dot_remove("4")); |
||||
assert_eq!(json!(["a", "c"]), vec); |
||||
|
||||
let mut vec = json!([[["a"], "b"], "c"]); |
||||
assert_eq!(true, vec.dot_remove("0.0.0")); |
||||
assert_eq!(false, vec.dot_remove("0.0.0")); |
||||
assert_eq!(json!([[[], "b"], "c"]), vec); // empty vec is left in place
|
||||
} |
||||
|
||||
#[test] |
||||
fn replace_in_vec() { |
||||
let mut vec = json!(["a", "b", "c"]); |
||||
|
||||
assert_eq!(Some("b".to_string()), vec.dot_replace("1", "BBB")); |
||||
|
||||
let mut vec = json!([[["a"], "b"], "c"]); |
||||
assert_eq!(Some("a".to_string()), vec.dot_replace("0.0.0", "AAA")); |
||||
assert_eq!(json!([[["AAA"], "b"], "c"]), vec); |
||||
} |
||||
|
||||
#[test] |
||||
fn set_map() { |
||||
let mut vec = json!({}); |
||||
vec.dot_set("foo", "bar"); |
||||
assert_eq!(json!({"foo": "bar"}), vec); |
||||
|
||||
let mut vec = json!({}); |
||||
vec.dot_set("foo.bar.baz", "binx"); |
||||
assert_eq!(json!({"foo": {"bar": {"baz": "binx"}}}), vec); |
||||
|
||||
vec.dot_set("foo.bar.abc.0", "aaa"); |
||||
assert_eq!(json!({"foo": {"bar": {"baz": "binx","abc": ["aaa"]}}}), vec); |
||||
} |
||||
|
||||
#[test] |
||||
#[should_panic] |
||||
fn set_map_panic_bad_index() { |
||||
let mut vec = json!({}); |
||||
vec.dot_set("0", "first"); |
||||
} |
||||
|
||||
#[test] |
||||
fn get_map() { |
||||
let vec = json!({"one": "two"}); |
||||
assert_eq!(Some("two".to_string()), vec.dot_get("one")); |
||||
|
||||
let vec = json!({"one": {"two": "three"}}); |
||||
assert_eq!(Some("three".to_string()), vec.dot_get("one.two")); |
||||
|
||||
let vec = json!({"one": "two"}); |
||||
assert_eq!(None, vec.dot_get::<Value>("xxx")); |
||||
} |
||||
|
||||
#[test] |
||||
fn remove_from_map() { |
||||
let mut vec = json!({"one": "two", "x": "y"}); |
||||
assert_eq!(true, vec.dot_remove("one")); |
||||
assert_eq!(json!({"x": "y"}), vec); |
||||
} |
||||
|
||||
#[test] |
||||
fn take_from_map() { |
||||
let mut vec = json!({"one": "two", "x": "y"}); |
||||
assert_eq!(Some("two".to_string()), vec.dot_take("one")); |
||||
assert_eq!(json!({"x": "y"}), vec); |
||||
} |
||||
|
||||
#[test] |
||||
fn replace_in_map() { |
||||
let mut vec = json!({"one": "two", "x": "y"}); |
||||
assert_eq!(Some("two".to_string()), vec.dot_replace("one", "fff")); |
||||
assert_eq!(json!({"one": "fff", "x": "y"}), vec); |
||||
|
||||
// replace value for string
|
||||
let mut vec = json!({"one": "two", "x": {"bbb": "y"}}); |
||||
assert_eq!(Some("y".to_string()), vec.dot_replace("x.bbb", "mm")); |
||||
assert_eq!( |
||||
Some(json!({"bbb": "mm"})), |
||||
vec.dot_replace("x", "betelgeuze") |
||||
); |
||||
assert_eq!(json!({"one": "two", "x": "betelgeuze"}), vec); |
||||
|
||||
// nested replace
|
||||
let mut vec = json!({"one": "two", "x": {"bbb": ["y"]}}); |
||||
assert_eq!( |
||||
Some("y".to_string()), |
||||
vec.dot_replace("x.bbb.0", "betelgeuze") |
||||
); |
||||
assert_eq!(json!({"one": "two", "x": {"bbb": ["betelgeuze"]}}), vec); |
||||
} |
||||
} |
Loading…
Reference in new issue