|
|
@ -145,14 +145,7 @@ pub trait DotPaths { |
|
|
|
/// - `<` ... first element of an array (same as `0`)
|
|
|
|
/// - `<` ... first element of an array (same as `0`)
|
|
|
|
fn dot_set<T>(&mut self, path: &str, value: T) -> Result<()> |
|
|
|
fn dot_set<T>(&mut self, path: &str, value: T) -> Result<()> |
|
|
|
where |
|
|
|
where |
|
|
|
T: Serialize, |
|
|
|
T: Serialize; |
|
|
|
{ |
|
|
|
|
|
|
|
// This is a default implementation.
|
|
|
|
|
|
|
|
// Vec uses a custom implementation to support the special syntax.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let _ = self.dot_replace::<T, Value>(path, value)?; // Original value is dropped
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Replace a value by path with a new value.
|
|
|
|
/// Replace a value by path with a new value.
|
|
|
|
/// The value types do not have to match.
|
|
|
|
/// The value types do not have to match.
|
|
|
@ -321,6 +314,7 @@ impl DotPaths for serde_json::Value { |
|
|
|
match self { |
|
|
|
match self { |
|
|
|
// Special case for Vec, which implements additional path symbols
|
|
|
|
// Special case for Vec, which implements additional path symbols
|
|
|
|
Value::Array(a) => a.dot_set(path, value), |
|
|
|
Value::Array(a) => a.dot_set(path, value), |
|
|
|
|
|
|
|
Value::Object(m) => m.dot_set(path, value), |
|
|
|
_ => { |
|
|
|
_ => { |
|
|
|
let _ = self.dot_replace::<T, Value>(path, value)?; // Original value is dropped
|
|
|
|
let _ = self.dot_replace::<T, Value>(path, value)?; // Original value is dropped
|
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
@ -412,6 +406,29 @@ impl DotPaths for serde_json::Map<String, serde_json::Value> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn dot_set<T>(&mut self, path: &str, value: T) -> Result<()> where |
|
|
|
|
|
|
|
T: Serialize { |
|
|
|
|
|
|
|
let (my, sub) = path_split(path); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if my.is_empty() { |
|
|
|
|
|
|
|
return Err(InvalidKey(my)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(subpath) = sub { |
|
|
|
|
|
|
|
if self.contains_key(&my) { |
|
|
|
|
|
|
|
self.get_mut(&my).unwrap().dot_set(subpath, value) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// Build new subpath
|
|
|
|
|
|
|
|
let _ = self.insert(my, new_by_path_root(subpath, value)?); // always returns None here
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
let packed = serde_json::to_value(value)?; |
|
|
|
|
|
|
|
self.insert(my, packed); |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn dot_replace<NEW, OLD>(&mut self, path: &str, value: NEW) -> Result<Option<OLD>> |
|
|
|
fn dot_replace<NEW, OLD>(&mut self, path: &str, value: NEW) -> Result<Option<OLD>> |
|
|
|
where |
|
|
|
where |
|
|
|
NEW: Serialize, |
|
|
|
NEW: Serialize, |
|
|
@ -425,10 +442,7 @@ impl DotPaths for serde_json::Map<String, serde_json::Value> { |
|
|
|
|
|
|
|
|
|
|
|
if let Some(subpath) = sub { |
|
|
|
if let Some(subpath) = sub { |
|
|
|
if self.contains_key(&my) { |
|
|
|
if self.contains_key(&my) { |
|
|
|
match self.get_mut(&my) { |
|
|
|
self.get_mut(&my).unwrap().dot_replace(subpath, value) |
|
|
|
None => Ok(None), |
|
|
|
|
|
|
|
Some(m) => m.dot_replace(subpath, value), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// Build new subpath
|
|
|
|
// Build new subpath
|
|
|
|
let _ = self.insert(my, new_by_path_root(subpath, value)?); // always returns None here
|
|
|
|
let _ = self.insert(my, new_by_path_root(subpath, value)?); // always returns None here
|
|
|
@ -487,7 +501,9 @@ impl DotPaths for Vec<serde_json::Value> { |
|
|
|
let index: usize = match my.as_str() { |
|
|
|
let index: usize = match my.as_str() { |
|
|
|
">" => self.len() - 1, // non-empty checked above
|
|
|
|
">" => self.len() - 1, // non-empty checked above
|
|
|
|
"<" => 0, |
|
|
|
"<" => 0, |
|
|
|
_ => my.parse().map_err(|_| InvalidKey(my))?, |
|
|
|
_ => my.parse().map_err(|_| { |
|
|
|
|
|
|
|
InvalidKey(my) |
|
|
|
|
|
|
|
})?, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
if index >= self.len() { |
|
|
|
if index >= self.len() { |
|
|
@ -524,7 +540,9 @@ impl DotPaths for Vec<serde_json::Value> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
"<" => 0, |
|
|
|
"<" => 0, |
|
|
|
_ => my.parse().map_err(|_| InvalidKey(my))?, |
|
|
|
_ => my.parse().map_err(|_| { |
|
|
|
|
|
|
|
InvalidKey(my) |
|
|
|
|
|
|
|
})?, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
if index > self.len() { |
|
|
|
if index > self.len() { |
|
|
@ -593,14 +611,23 @@ impl DotPaths for Vec<serde_json::Value> { |
|
|
|
_ if my.starts_with('>') => { |
|
|
|
_ if my.starts_with('>') => { |
|
|
|
// insert after
|
|
|
|
// insert after
|
|
|
|
insert = true; |
|
|
|
insert = true; |
|
|
|
(&my[1..]).parse::<usize>().map_err(|_| InvalidKey(my_s))? + 1 |
|
|
|
(&my[1..]).parse::<usize>() |
|
|
|
|
|
|
|
.map_err(|_| { |
|
|
|
|
|
|
|
InvalidKey(my_s) |
|
|
|
|
|
|
|
})? + 1 |
|
|
|
} |
|
|
|
} |
|
|
|
_ if my.starts_with('<') => { |
|
|
|
_ if my.starts_with('<') => { |
|
|
|
// insert before
|
|
|
|
// insert before
|
|
|
|
insert = true; |
|
|
|
insert = true; |
|
|
|
(&my[1..]).parse::<usize>().map_err(|_| InvalidKey(my_s))? |
|
|
|
(&my[1..]).parse::<usize>() |
|
|
|
} |
|
|
|
.map_err(|_| { |
|
|
|
_ => my.parse::<usize>().map_err(|_| InvalidKey(my_s))?, |
|
|
|
InvalidKey(my_s) |
|
|
|
|
|
|
|
})? |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
_ => my.parse::<usize>() |
|
|
|
|
|
|
|
.map_err(|_| { |
|
|
|
|
|
|
|
InvalidKey(my_s) |
|
|
|
|
|
|
|
})?, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
if index > self.len() { |
|
|
|
if index > self.len() { |
|
|
@ -657,7 +684,9 @@ impl DotPaths for Vec<serde_json::Value> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
"<" => 0, |
|
|
|
"<" => 0, |
|
|
|
_ => my.parse().map_err(|_| InvalidKey(my))?, |
|
|
|
_ => my.parse().map_err(|_| { |
|
|
|
|
|
|
|
InvalidKey(my) |
|
|
|
|
|
|
|
})?, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
if index >= self.len() { |
|
|
|
if index >= self.len() { |
|
|
@ -700,7 +729,9 @@ impl DotPaths for Vec<serde_json::Value> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
"<" => 0, |
|
|
|
"<" => 0, |
|
|
|
_ => my.parse().map_err(|_| InvalidKey(my))?, |
|
|
|
_ => my.parse().map_err(|_| { |
|
|
|
|
|
|
|
InvalidKey(my) |
|
|
|
|
|
|
|
})?, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
if index >= self.len() { |
|
|
|
if index >= self.len() { |
|
|
@ -838,6 +869,46 @@ mod tests { |
|
|
|
assert_eq!(json!([[["first"]], [["second"]]]), vec); |
|
|
|
assert_eq!(json!([[["first"]], [["second"]]]), vec); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn array_append1() { |
|
|
|
|
|
|
|
let mut test_array = Value::Array(vec![]); |
|
|
|
|
|
|
|
test_array.dot_set(">>", Value::String(String::from("Go to class"))).unwrap(); |
|
|
|
|
|
|
|
test_array.dot_set(">>", Value::String(String::from("Fish"))).unwrap(); |
|
|
|
|
|
|
|
assert_eq!(json!(["Go to class","Fish"]), test_array); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn array_append2() { |
|
|
|
|
|
|
|
let mut test_array = Value::Array(vec![]); |
|
|
|
|
|
|
|
test_array.dot_set("+", Value::String(String::from("Go to class"))).unwrap(); |
|
|
|
|
|
|
|
test_array.dot_set("+", Value::String(String::from("Fish"))).unwrap(); |
|
|
|
|
|
|
|
assert_eq!(json!(["Go to class","Fish"]), test_array); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn array_append_in_object1() { |
|
|
|
|
|
|
|
let mut test_inner_array = Value::Null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_inner_array.dot_set("todos.+", Value::String(String::from("Go to class"))).unwrap(); |
|
|
|
|
|
|
|
assert_eq!(json!({"todos" : ["Go to class"] }), test_inner_array); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_inner_array.dot_set("todos.+", Value::String(String::from("Fish"))).unwrap(); |
|
|
|
|
|
|
|
assert_eq!(json!({"todos" : ["Go to class","Fish"] }), test_inner_array); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn array_append_in_object2() { |
|
|
|
|
|
|
|
let mut test_inner_array = Value::Null; |
|
|
|
|
|
|
|
test_inner_array.dot_set("name", Value::String(String::from("Google"))).unwrap(); |
|
|
|
|
|
|
|
assert_eq!(json!({"name" : "Google"}), test_inner_array); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_inner_array.dot_set("todos.+", Value::String(String::from("Go to class"))).unwrap(); |
|
|
|
|
|
|
|
assert_eq!(json!({"name" : "Google", "todos" : ["Go to class"] }), test_inner_array); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_inner_array.dot_set("todos.+", Value::String(String::from("Fish"))).unwrap(); |
|
|
|
|
|
|
|
assert_eq!(json!({"name" : "Google", "todos" : ["Go to class","Fish"] }), test_inner_array); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
#[test] |
|
|
|
fn get_vec() { |
|
|
|
fn get_vec() { |
|
|
|
let vec = json!([ |
|
|
|
let vec = json!([ |
|
|
|