use serde_json::{Map, Number, Value};
pub fn extend_object(target: &mut Map<String, Value>, source: &Map<String, Value>) {
for (key, value) in source {
append_object(target, key, value.clone());
}
}
pub fn append_object(target: &mut Map<String, Value>, key: impl ToString, value: Value) {
do_append(target, key.to_string(), process_value(value));
}
pub fn do_append(target: &mut Map<String, Value>, key: String, value: Value) {
if let Some((base, rest)) = key.split_once(".") {
if let Some(inner) = target.get_mut(base) {
if let Some(inner) = inner.as_object_mut() {
do_append(inner, rest.to_string(), value);
return;
}
} else {
let mut inner: Value = serde_json::from_str("{}").unwrap();
let proxy = inner.as_object_mut().unwrap();
do_append(proxy, rest.to_string(), value);
target.insert(base.to_string(), inner);
}
return
}
if let Some(mut current) = target.remove(&key) {
if let Some(current) = current.as_array_mut() {
current.push(value);
} else {
current = vec![current, value].into();
}
target.insert(key, current);
} else {
target.insert(key, value);
}
}
fn process_value(value: Value) -> Value {
if !value.is_string() {
return value;
}
if let Some(value) = value.as_str() {
if let Some(value) = value.strip_prefix("!!bool ") {
return Value::Bool(value.trim() == "true");
}
if let Some(value) = value.strip_prefix("!!float ") {
return Value::Number(
Number::from_f64(value.trim().parse::<f64>().unwrap())
.unwrap()
);
}
if let Some(value) = value.strip_prefix("!!int ") {
return Value::Number(value.trim().parse::<i64>().unwrap().into());
}
if let Some(value) = value.strip_prefix("!!str ") {
return Value::String(value.trim().into());
}
};
value
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_append_object_to_empty_target() {
let mut target: Value = serde_json::from_str("{}").unwrap();
let proxy = target.as_object_mut().unwrap();
append_object(proxy, "test", true.into());
assert!(target["test"].as_bool().unwrap());
}
#[test]
fn test_append_object_to_target_containing_data() {
let mut target: Value = serde_json::from_str("{}").unwrap();
let proxy = target.as_object_mut().unwrap();
proxy.insert("a".to_string(), 42.into());
proxy.insert("b".to_string(), "test".into());
append_object(proxy, "test", true.into());
assert!(target["test"].as_bool().unwrap());
assert_eq!(42, target["a"].as_i64().unwrap());
assert_eq!("test", target["b"].as_str().unwrap());
}
#[test]
fn test_append_object_to_target_containing_the_same_key() {
let mut target: Value = serde_json::from_str("{}").unwrap();
let proxy = target.as_object_mut().unwrap();
append_object(proxy, "test", "a".into());
append_object(proxy, "test", "b".into());
append_object(proxy, "test", 42.into());
let proxy = target["test"].as_array().unwrap();
assert_eq!("a", proxy[0].as_str().unwrap());
assert_eq!("b", proxy[1].as_str().unwrap());
assert_eq!(42, proxy[2].as_i64().unwrap());
}
#[test]
fn test_append_inner_object() {
let mut target: Value = serde_json::from_str("{}").unwrap();
let proxy = target.as_object_mut().unwrap();
append_object(proxy, "name", "test".into());
append_object(proxy, "test.a", 2.into());
append_object(proxy, "test.b", 3.into());
append_object(proxy, "test.c", 5.into());
append_object(proxy, "test.d.e", true.into());
assert_eq!("test", target["name"].as_str().unwrap());
assert_eq!(2, target["test"]["a"].as_i64().unwrap());
assert_eq!(3, target["test"]["b"].as_i64().unwrap());
assert_eq!(5, target["test"]["c"].as_i64().unwrap());
assert!(target["test"]["d"]["e"].as_bool().unwrap());
}
#[test]
fn test_extend_object() {
let mut target: Value = serde_json::from_str("{}").unwrap();
let proxy = target.as_object_mut().unwrap();
let source: Value = serde_json::from_str("{\"x\": 3, \"y\": 4, \"a\": 5}").unwrap();
extend_object(proxy, source.as_object().unwrap());
let source: Value = serde_json::from_str("{\"a\": true, \"name\": \"test\"}").unwrap();
extend_object(proxy, source.as_object().unwrap());
assert_eq!(3, proxy["x"].as_i64().unwrap());
assert_eq!(4, proxy["y"].as_i64().unwrap());
let proxy = target["a"].as_array().unwrap();
assert_eq!(5, proxy[0].as_i64().unwrap());
assert!(proxy[1].as_bool().unwrap());
}
#[test]
fn test_yaml_style_boolean() {
let mut target: Value = serde_json::from_str("{}").unwrap();
let proxy = target.as_object_mut().unwrap();
append_object(proxy, "a", Value::String("!!bool true".into()));
append_object(proxy, "b", Value::String("!!bool false".into()));
debug_assert!(target["a"].as_bool().unwrap());
debug_assert!(!target["b"].as_bool().unwrap());
}
#[test]
fn test_yaml_style_float() {
let mut target: Value = serde_json::from_str("{}").unwrap();
let proxy = target.as_object_mut().unwrap();
append_object(proxy, "test", Value::String("!!float 123".into()));
debug_assert_eq!(123.0, target["test"].as_f64().unwrap());
}
#[test]
fn test_yaml_style_integer() {
let mut target: Value = serde_json::from_str("{}").unwrap();
let proxy = target.as_object_mut().unwrap();
append_object(proxy, "test", Value::String("!!int 123".into()));
debug_assert_eq!(123, target["test"].as_i64().unwrap());
}
#[test]
fn test_yaml_style_string() {
let mut target: Value = serde_json::from_str("{}").unwrap();
let proxy = target.as_object_mut().unwrap();
append_object(proxy, "test", Value::String("!!str 123".into()));
debug_assert_eq!("123", target["test"].as_str().unwrap());
}
}