sc_chain_spec/
json_patch.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! A helper module providing json patching functions.
20
21use serde_json::Value;
22
23/// Recursively merges two JSON objects, `a` and `b`, into a single object.
24///
25/// If a key exists in both objects, the value from `b` will override the value from `a`.
26/// If a key exists in `b` with a `null` value, it will be removed from `a`.
27/// If a key exists only in `b` and not in `a`, it will be added to `a`.
28///
29/// # Arguments
30///
31/// * `a` - A mutable reference to the target JSON object to merge into.
32/// * `b` - The JSON object to merge with `a`.
33pub fn merge(a: &mut Value, b: Value) {
34	match (a, b) {
35		(Value::Object(a), Value::Object(b)) =>
36			for (k, v) in b {
37				if v.is_null() {
38					a.remove(&k);
39				} else {
40					merge(a.entry(k).or_insert(Value::Null), v);
41				}
42			},
43		(a, b) => *a = b,
44	};
45}
46
47#[cfg(test)]
48mod tests {
49	use super::*;
50	use serde_json::json;
51
52	#[test]
53	fn test1_simple_merge() {
54		let mut j1 = json!({ "a":123 });
55		merge(&mut j1, json!({ "b":256 }));
56		assert_eq!(j1, json!({ "a":123, "b":256 }));
57	}
58
59	#[test]
60	fn test2_patch_simple_merge_nested() {
61		let mut j1 = json!({
62			"a": {
63				"name": "xxx",
64				"value": 123
65			},
66			"b": { "c" : { "inner_name": "yyy" } }
67		});
68
69		let j2 = json!({
70			"a": {
71				"keys": ["a", "b", "c" ]
72			}
73		});
74
75		merge(&mut j1, j2);
76		assert_eq!(
77			j1,
78			json!({"a":{"keys":["a","b","c"],"name":"xxx","value":123}, "b": { "c" : { "inner_name": "yyy" } }})
79		);
80	}
81
82	#[test]
83	fn test3_patch_overrides_existing_keys() {
84		let mut j1 = json!({
85			"a": {
86				"name": "xxx",
87				"value": 123,
88				"keys": ["d"]
89
90			}
91		});
92
93		let j2 = json!({
94			"a": {
95				"keys": ["a", "b", "c" ]
96			}
97		});
98
99		merge(&mut j1, j2);
100		assert_eq!(j1, json!({"a":{"keys":["a","b","c"],"name":"xxx","value":123}}));
101	}
102
103	#[test]
104	fn test4_patch_overrides_existing_keys() {
105		let mut j1 = json!({
106			"a": {
107				"name": "xxx",
108				"value": 123,
109				"b" : {
110					"inner_name": "yyy"
111				}
112			}
113		});
114
115		let j2 = json!({
116			"a": {
117				"name": "new_name",
118				"b" : {
119					"inner_name": "inner_new_name"
120				}
121			}
122		});
123
124		merge(&mut j1, j2);
125		assert_eq!(
126			j1,
127			json!({ "a": {"name":"new_name", "value":123, "b" : { "inner_name": "inner_new_name" }} })
128		);
129	}
130
131	#[test]
132	fn test5_patch_overrides_existing_nested_keys() {
133		let mut j1 = json!({
134			"a": {
135				"name": "xxx",
136				"value": 123,
137				"b": {
138					"c": {
139						"d": {
140							"name": "yyy",
141							"value": 256
142						}
143					}
144				}
145			},
146		});
147
148		let j2 = json!({
149			"a": {
150				"value": 456,
151				"b": {
152					"c": {
153						"d": {
154							"name": "new_name"
155						}
156					}
157				}
158			}
159		});
160
161		merge(&mut j1, j2);
162		assert_eq!(
163			j1,
164			json!({ "a": {"name":"xxx", "value":456, "b": { "c": { "d": { "name": "new_name", "value": 256 }}}}})
165		);
166	}
167
168	#[test]
169	fn test6_patch_removes_keys_if_null() {
170		let mut j1 = json!({
171			"a": {
172				"name": "xxx",
173				"value": 123,
174				"enum_variant_1": {
175					"name": "yyy",
176				}
177			},
178		});
179
180		let j2 = json!({
181			"a": {
182				"value": 456,
183				"enum_variant_1": null,
184				"enum_variant_2": 32,
185			}
186		});
187
188		merge(&mut j1, j2);
189		assert_eq!(j1, json!({ "a": {"name":"xxx", "value":456, "enum_variant_2": 32 }}));
190	}
191}