use serde_json::{Map, Value};
#[allow(non_snake_case)]
pub fn REMOVE_THIS_KEY() -> Value {
serde_json::json!({"__powerliners_remove_this_key__": true})
}
pub fn mergeargs<I>(argvalue: I, remove: bool) -> Option<Map<String, Value>>
where
I: IntoIterator<Item = (String, Value)>,
{
let mut iter = argvalue.into_iter().peekable(); iter.peek()?;
let mut r: Map<String, Value> = Map::new(); for (k, v) in iter {
let mut single = Map::new();
single.insert(k, v);
mergedicts(&mut r, single, remove);
}
Some(r) }
pub fn _clear_special_values(d: &mut Map<String, Value>) {
let mut pops: Vec<String> = Vec::new(); for (k, v) in d.iter() {
if matches!(
v.get("__powerliners_remove_this_key__"),
Some(Value::Bool(true))
) {
pops.push(k.clone()); }
}
for k in pops {
d.remove(&k);
}
for (_, v) in d.iter_mut() {
if let Value::Object(child) = v {
_clear_special_values(child);
}
}
}
pub fn mergedicts(d1: &mut Map<String, Value>, d2: Map<String, Value>, remove: bool) {
_setmerged(d1, &d2); for (k, v) in d2 {
let in_d1_as_dict = matches!(d1.get(&k), Some(Value::Object(_)));
let v_is_dict = matches!(v, Value::Object(_));
if in_d1_as_dict && v_is_dict {
if let (Some(Value::Object(inner1)), Value::Object(inner2)) = (d1.get_mut(&k), v) {
mergedicts(inner1, inner2, remove); }
} else if remove
&& matches!(
v.get("__powerliners_remove_this_key__"),
Some(Value::Bool(true))
)
{
d1.remove(&k); } else {
let mut owned = v;
if remove {
if let Value::Object(ref mut inner) = owned {
_clear_special_values(inner); }
}
d1.insert(k, owned); }
}
}
pub fn mergedefaults(d1: &mut Map<String, Value>, d2: Map<String, Value>) {
for (k, v) in d2 {
let in_d1_as_dict = matches!(d1.get(&k), Some(Value::Object(_)));
let v_is_dict = matches!(v, Value::Object(_));
if in_d1_as_dict && v_is_dict {
if let (Some(Value::Object(inner1)), Value::Object(inner2)) = (d1.get_mut(&k), v) {
mergedefaults(inner1, inner2); }
} else {
d1.entry(k).or_insert(v); }
}
}
pub fn _setmerged(_d1: &mut Map<String, Value>, _d2: &Map<String, Value>) {
}
pub fn mergedicts_copy(d1: &Map<String, Value>, d2: Map<String, Value>) -> Map<String, Value> {
let mut ret = d1.clone(); _setmerged(&mut ret, &d2); for (k, v) in d2 {
let in_d1_as_dict = matches!(d1.get(&k), Some(Value::Object(_)));
let v_is_dict = matches!(v, Value::Object(_));
if in_d1_as_dict && v_is_dict {
if let (Some(Value::Object(inner1)), Value::Object(inner2)) = (d1.get(&k), v) {
ret.insert(k, Value::Object(mergedicts_copy(inner1, inner2))); }
} else {
ret.insert(k, v); }
}
ret }
pub fn updated<I>(d: &Map<String, Value>, updates: I) -> Map<String, Value>
where
I: IntoIterator<Item = (String, Value)>,
{
let mut d = d.clone(); for (k, v) in updates {
d.insert(k, v);
}
d }
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn obj(v: Value) -> Map<String, Value> {
match v {
Value::Object(m) => m,
_ => panic!("not an object: {:?}", v),
}
}
#[test]
fn mergedicts_overwrites_scalar() {
let mut d1 = obj(json!({"a": 1, "b": 2}));
let d2 = obj(json!({"b": 3, "c": 4}));
mergedicts(&mut d1, d2, true);
assert_eq!(Value::Object(d1), json!({"a": 1, "b": 3, "c": 4}));
}
#[test]
fn mergedicts_recurses_into_nested() {
let mut d1 = obj(json!({"a": {"x": 1, "y": 2}}));
let d2 = obj(json!({"a": {"y": 20, "z": 30}}));
mergedicts(&mut d1, d2, true);
assert_eq!(Value::Object(d1), json!({"a": {"x": 1, "y": 20, "z": 30}}));
}
#[test]
fn mergedicts_remove_this_key_deletes() {
let mut d1 = obj(json!({"a": 1, "b": 2}));
let mut d2 = Map::new();
d2.insert("b".to_string(), REMOVE_THIS_KEY());
mergedicts(&mut d1, d2, true);
assert_eq!(Value::Object(d1), json!({"a": 1}));
}
#[test]
fn mergedefaults_keeps_existing() {
let mut d1 = obj(json!({"a": 1}));
let d2 = obj(json!({"a": 2, "b": 3}));
mergedefaults(&mut d1, d2);
assert_eq!(Value::Object(d1), json!({"a": 1, "b": 3}));
}
#[test]
fn mergedicts_copy_does_not_mutate_inputs() {
let d1 = obj(json!({"a": 1, "nested": {"x": 1}}));
let d2 = obj(json!({"a": 2, "nested": {"y": 2}}));
let merged = mergedicts_copy(&d1, d2.clone());
assert_eq!(d1.get("a"), Some(&json!(1)));
assert_eq!(d2.get("a"), Some(&json!(2)));
assert_eq!(
Value::Object(merged),
json!({"a": 2, "nested": {"x": 1, "y": 2}})
);
}
#[test]
fn mergeargs_empty_returns_none() {
let r: Option<Map<String, Value>> = mergeargs(std::iter::empty(), false);
assert!(r.is_none());
}
#[test]
fn mergeargs_folds_pairs() {
let r = mergeargs(
vec![("a".to_string(), json!(1)), ("b".to_string(), json!(2))],
false,
)
.unwrap();
assert_eq!(Value::Object(r), json!({"a": 1, "b": 2}));
}
#[test]
fn updated_does_not_mutate_input() {
let d = obj(json!({"a": 1}));
let r = updated(&d, vec![("b".to_string(), json!(2))]);
assert_eq!(d.get("a"), Some(&json!(1)));
assert!(d.get("b").is_none(), "original was mutated");
assert_eq!(Value::Object(r), json!({"a": 1, "b": 2}));
}
}