1use serde_json::{Map, Value};
6
7pub fn array_merge(a: &Value, b: &Value) -> Value {
12 match (empty_as_object(a), empty_as_object(b)) {
13 (Value::Object(oa), Value::Object(ob)) => {
14 let mut out = oa.clone();
15 for (k, v) in ob {
16 out.insert(k.clone(), v.clone());
17 }
18 Value::Object(out)
19 }
20 (Value::Array(la), Value::Array(lb)) => {
21 let mut out = la.clone();
22 out.extend(lb.iter().cloned());
23 Value::Array(out)
24 }
25 (Value::Object(_), _) => a.clone(),
26 (_, other) => other.clone(),
27 }
28}
29
30pub fn array_merge_recursive(a: &Value, b: &Value) -> Value {
35 match (empty_as_object(a), empty_as_object(b)) {
36 (Value::Object(oa), Value::Object(ob)) => {
37 let mut out: Map<String, Value> = oa.clone();
38 for (k, vb) in ob {
39 match out.get(k).cloned() {
40 None => {
41 out.insert(k.clone(), vb.clone());
42 }
43 Some(va) => {
44 let merged = match (&va, vb) {
45 (Value::Object(_), Value::Object(_))
46 | (Value::Array(_), Value::Array(_)) => array_merge_recursive(&va, vb),
47 (Value::Array(la), scalar) => {
48 let mut l = la.clone();
49 l.push(scalar.clone());
50 Value::Array(l)
51 }
52 (scalar, Value::Array(lb)) => {
53 let mut l = vec![scalar.clone()];
54 l.extend(lb.iter().cloned());
55 Value::Array(l)
56 }
57 (sa, sb) => Value::Array(vec![sa.clone(), sb.clone()]),
58 };
59 out.insert(k.clone(), merged);
60 }
61 }
62 }
63 Value::Object(out)
64 }
65 (Value::Array(la), Value::Array(lb)) => {
66 let mut out = la.clone();
67 out.extend(lb.iter().cloned());
68 Value::Array(out)
69 }
70 (Value::Array(la), other) => {
71 let mut out = la.clone();
72 out.push(other.clone());
73 Value::Array(out)
74 }
75 (other, Value::Array(lb)) => {
76 let mut out = vec![other.clone()];
77 out.extend(lb.iter().cloned());
78 Value::Array(out)
79 }
80 (x, y) => Value::Array(vec![x.clone(), y.clone()]),
81 }
82}
83
84pub fn merge_deep(a: &Value, b: &Value) -> Value {
88 match (empty_as_object(a), empty_as_object(b)) {
89 (Value::Object(oa), Value::Object(ob)) => {
90 let mut out = oa.clone();
91 for (k, vb) in ob {
92 let merged = match out.get(k) {
93 Some(va)
94 if matches!(
95 (va, vb),
96 (Value::Object(_), Value::Object(_))
97 | (Value::Array(_), Value::Array(_))
98 | (Value::Object(_), Value::Array(_))
99 | (Value::Array(_), Value::Object(_))
100 ) =>
101 {
102 merge_deep(va, vb)
103 }
104 _ => vb.clone(),
105 };
106 out.insert(k.clone(), merged);
107 }
108 Value::Object(out)
109 }
110 (Value::Array(la), Value::Array(lb)) => {
111 let mut out = la.clone();
112 out.extend(lb.iter().cloned());
113 Value::Array(out)
114 }
115 (_, other) => other.clone(),
119 }
120}
121
122fn empty_as_object(v: &Value) -> &Value {
125 static EMPTY: std::sync::OnceLock<Value> = std::sync::OnceLock::new();
126 match v {
127 Value::Array(a) if a.is_empty() => EMPTY.get_or_init(|| Value::Object(Map::new())),
128 other => other,
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use serde_json::json;
136
137 #[test]
138 fn empty_list_is_an_empty_array() {
139 assert_eq!(
140 array_merge_recursive(&json!([]), &json!({"a": 1})),
141 json!({"a": 1})
142 );
143 assert_eq!(array_merge(&json!({"a": 1}), &json!([])), json!({"a": 1}));
144 assert_eq!(
145 merge_deep(&json!([]), &json!({"a": [1]})),
146 json!({"a": [1]})
147 );
148 }
149
150 #[test]
151 fn merge_recursive_like_php() {
152 let root = json!({"psr-4": {"App\\": "src/"}, "files": ["a.php"]});
154 let inc = json!({"psr-4": {"App\\": "modules/x/src/", "Mod\\": "modules/x/lib/"}, "files": ["modules/x/b.php"], "exclude-from-classmap": ["modules/x/tests/"]});
155 let m = array_merge_recursive(&root, &inc);
156 assert_eq!(
157 m,
158 json!({"psr-4": {"App\\": ["src/", "modules/x/src/"], "Mod\\": "modules/x/lib/"}, "files": ["a.php", "modules/x/b.php"], "exclude-from-classmap": ["modules/x/tests/"]})
159 );
160 let inc2 = json!({"psr-4": {"App\\": ["y/", "z/"]}});
161 let m2 = array_merge_recursive(&m, &inc2);
162 assert_eq!(
163 m2["psr-4"]["App\\"],
164 json!(["src/", "modules/x/src/", "y/", "z/"])
165 );
166 assert_eq!(
168 array_merge_recursive(&json!({"k": "a"}), &json!({"k": ["b", "c"]})),
169 json!({"k": ["a", "b", "c"]})
170 );
171 }
172
173 #[test]
174 fn merge_and_deep_like_php() {
175 assert_eq!(
176 array_merge(
177 &json!({"a": 1, "b": {"x": 1}}),
178 &json!({"b": {"y": 2}, "c": 3})
179 ),
180 json!({"a": 1, "b": {"y": 2}, "c": 3})
181 );
182 assert_eq!(
183 merge_deep(
184 &json!({"a": 1, "b": {"x": 1, "l": [1]}}),
185 &json!({"b": {"y": 2, "l": [2]}, "a": 9})
186 ),
187 json!({"a": 9, "b": {"x": 1, "l": [1, 2], "y": 2}})
188 );
189 }
190}