blob: 9722001cd22c049de3e0d04ac9de613fe917f3be [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::Result;
use serde::de::DeserializeOwned;
use serde_json::Value;
pub fn try_merge_into<T: DeserializeOwned>(base: Value, overrides: Value) -> Result<T> {
let merged = merge(base, overrides);
Ok(serde_json::from_value(merged)?)
}
fn merge(base: Value, value: Value) -> Value {
match (base, value) {
(base @ _, Value::Null) => {
// Override value is nothing, so nothing to do.
base
}
(Value::Object(mut merged), Value::Object(overrides)) => {
// Both are maps/dicts, so do a key-wise merging.
for (key, value) in overrides {
// Remove the existing value, so that it can be merged before re-inserting.
let existing = merged.remove(&key);
match existing {
Some(existing) => {
// If there's already a value, recursively merge the override value.
merged.insert(key, merge(existing, value));
}
None => {
// If there's not an existing value, just use the override value.
merged.insert(key, value);
}
}
}
Value::Object(merged)
}
(_, value) => {
// Either the override or the base value is a non-mergeable type,
// so just return the override value.
value
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_both_null() {
let base = Value::Null;
let value = Value::Null;
assert_eq!(Value::Null, merge(base, value))
}
#[test]
fn test_null_override_is_no_op() {
let base = json!("a string");
assert_eq!("a string", merge(base, Value::Null));
let base = json!(1);
assert_eq!(1, merge(base, Value::Null));
let base = json!(true);
assert_eq!(true, merge(base, Value::Null));
let base = json!(["item1", "item2"]);
assert_eq!(json!(["item1", "item2"]), merge(base, Value::Null));
}
#[test]
fn test_null_base_is_overridden() {
let value = json!("a string");
assert_eq!("a string", merge(Value::Null, value));
}
#[test]
fn test_scalar_value_is_overriden_by_scalar() {
assert_eq!(json!("override"), merge(json!(42), json!("override")));
assert_eq!(json!(36), merge(json!(4), json!(36)));
assert_eq!(json!(42), merge(json!(false), json!(42)));
assert_eq!(json!(false), merge(json!(3), json!(false)));
assert_eq!(json!(["item1", "item2"]), merge(json!(39), json!(["item1", "item2"])));
}
#[test]
fn test_object_overrides_scalar_or_list() {
let value = json!({
"key": "value"
});
assert_eq!(value, merge(Value::Null, value.clone()));
assert_eq!(value, merge(json!(true), value.clone()));
assert_eq!(value, merge(json!(42), value.clone()));
assert_eq!(value, merge(json!("a string"), value.clone()));
assert_eq!(value, merge(json!(["item1", "item2"]), value.clone()));
}
#[test]
fn test_merged_keys() {
assert_eq!(
json!({
"base_key": "a string",
"added_key": "some value"
}),
merge(
json!({
"base_key": "a string"
}),
json!({
"added_key": "some value"
})
)
);
}
#[test]
fn test_merged_keys_adds_object() {
assert_eq!(
json!({
"base_key": "a string",
"added_key": {
"inner_key": "some value"
}
}),
merge(
json!({
"base_key": "a string"
}),
json!({
"added_key": {
"inner_key": "some value"
}
})
)
);
}
#[test]
fn test_merge_overrides_values() {
assert_eq!(
json!({
"key1": "key1 value",
"key2": "key2 new value"
}),
merge(
json!({
"key1": "key1 value",
"key2": "key2 value"
}),
json!({
"key2": "key2 new value"
})
)
);
}
#[test]
fn test_merge_overrides_nested_value() {
assert_eq!(
json!({
"key1": "key1 value",
"key2": {
"inner1": "inner 1 value",
"inner2": "inner 2 new value"
}
}),
merge(
json!({
"key1": "key1 value",
"key2": {
"inner1": "inner 1 value",
"inner2": "inner 2 value"
}
}),
json!({
"key2": { "inner2": "inner 2 new value" }
})
)
);
}
#[test]
fn test_merge_adds_empty_objects() {
assert_eq!(
json!({
"key1": "key1 value",
"key2": {}
}),
merge(
json!({
"key1": "key1 value"
}),
json!({
"key2": {}
})
)
);
}
}