1use crate::{is_trace_level_enabled, position::InputPosition, Input};
2use cfg_if::cfg_if;
3
4macro_rules! trace_merge {
5 ($to_be_merged_input_position:expr, $to_be_merged_input:expr, $input_position:expr, $input:expr, $action:expr) => {
6 cfg_if! {
7 if #[cfg(feature = "tracing")] {
8 tracing::trace!(
9 position = %$input_position,
10 old_value = %$input,
11 from = %$to_be_merged_input_position,
12 new_value = %$to_be_merged_input,
13 $action
14 );
15 } else if #[cfg(feature = "logging")] {
16 log::trace!(
17 "position={:?} old_value={:?} from={:?} new_value={:?} message={:?}",
18 $input_position.to_string(),
19 $input.to_string(),
20 $to_be_merged_input_position.to_string(),
21 $to_be_merged_input.to_string(),
22 $action,
23 );
24 }
25 }
26 };
27}
28
29pub fn merge(input: &mut Input, to_be_merged_input: &Input) {
30 merge_with_positions(
31 input,
32 InputPosition::new(),
33 to_be_merged_input,
34 InputPosition::new(),
35 )
36}
37
38pub fn merge_with_positions(
39 input: &mut Input,
40 input_position: InputPosition,
41 to_be_merged_input: &Input,
42 to_be_merged_input_position: InputPosition,
43) {
44 if input.is_map() {
45 merge_map(
46 input,
47 input_position,
48 to_be_merged_input,
49 to_be_merged_input_position,
50 )
51 } else if input.is_list() {
52 merge_list(
53 input,
54 input_position,
55 to_be_merged_input,
56 to_be_merged_input_position,
57 )
58 } else if input.is_str() {
59 merge_str(
60 input,
61 input_position,
62 to_be_merged_input,
63 to_be_merged_input_position,
64 )
65 } else if input.is_float() {
66 merge_float(
67 input,
68 input_position,
69 to_be_merged_input,
70 to_be_merged_input_position,
71 )
72 } else if input.is_int() {
73 merge_int(
74 input,
75 input_position,
76 to_be_merged_input,
77 to_be_merged_input_position,
78 )
79 } else if input.is_bool() {
80 merge_bool(
81 input,
82 input_position,
83 to_be_merged_input,
84 to_be_merged_input_position,
85 )
86 } else {
87 unreachable!("{input:?}!!!")
88 }
89}
90
91fn merge_map(
92 input: &mut Input,
93 input_position: InputPosition,
94 to_be_merged_input: &Input,
95 to_be_merged_input_position: InputPosition,
96) {
97 if !(to_be_merged_input.is_map() && input.is_map()) {
98 trace_merge!(
99 to_be_merged_input_position,
100 to_be_merged_input,
101 input_position,
102 input,
103 "replaced"
104 );
105 *input = to_be_merged_input.clone();
106 return;
107 }
108 let map = input.map_mut();
109 let to_be_merged_map = to_be_merged_input.as_map();
110 for (key, inner_to_be_merged_input) in to_be_merged_map {
111 if let Some(inner_input) = map.get_mut(key) {
112 merge_with_positions(
113 inner_input,
114 input_position.new_with_key(key),
115 inner_to_be_merged_input,
116 to_be_merged_input_position.new_with_key(key),
117 );
118 } else {
119 map.insert(key.clone(), inner_to_be_merged_input.clone());
120 }
121 }
122}
123
124fn merge_list(
125 input: &mut Input,
126 _input_position: InputPosition,
127 to_be_merged_input: &Input,
128 _to_be_merged_input_position: InputPosition,
129) {
130 if !(to_be_merged_input.is_list() && input.is_list()) {
131 trace_merge!(
132 _to_be_merged_input_position,
133 to_be_merged_input,
134 _input_position,
135 input,
136 "replaced"
137 );
138 *input = to_be_merged_input.clone();
139 return;
140 }
141 let mut _input_clone = input.clone();
142 let list = input.list_mut();
143 let to_be_merged_list = to_be_merged_input.as_list();
144 for (_index, inner_to_be_merged_input) in to_be_merged_list.iter().enumerate() {
145 if !list.contains(inner_to_be_merged_input) {
146 if is_trace_level_enabled!() {
147 _input_clone
148 .list_mut()
149 .push(inner_to_be_merged_input.clone());
150 trace_merge!(
151 _to_be_merged_input_position.new_with_index(_index),
152 inner_to_be_merged_input,
153 _input_position,
154 _input_clone,
155 "appended"
156 );
157 }
158 list.push(inner_to_be_merged_input.clone());
159 }
160 }
161}
162
163fn merge_str(
164 input: &mut Input,
165 _input_position: InputPosition,
166 to_be_merged_input: &Input,
167 _to_be_merged_input_position: InputPosition,
168) {
169 if input != to_be_merged_input {
170 trace_merge!(
171 _to_be_merged_input_position,
172 to_be_merged_input,
173 _input_position,
174 input,
175 "replaced"
176 );
177 *input = to_be_merged_input.clone();
178 }
179}
180
181fn merge_float(
182 input: &mut Input,
183 _input_position: InputPosition,
184 to_be_merged_input: &Input,
185 _to_be_merged_input_position: InputPosition,
186) {
187 if input != to_be_merged_input {
188 trace_merge!(
189 _to_be_merged_input_position,
190 to_be_merged_input,
191 _input_position,
192 input,
193 "replaced"
194 );
195 *input = to_be_merged_input.clone();
196 }
197}
198
199fn merge_int(
200 input: &mut Input,
201 _input_position: InputPosition,
202 to_be_merged_input: &Input,
203 _to_be_merged_input_position: InputPosition,
204) {
205 if input != to_be_merged_input {
206 trace_merge!(
207 _to_be_merged_input_position,
208 to_be_merged_input,
209 _input_position,
210 input,
211 "replaced"
212 );
213 *input = to_be_merged_input.clone();
214 }
215}
216
217fn merge_bool(
218 input: &mut Input,
219 _input_position: InputPosition,
220 to_be_merged_input: &Input,
221 _to_be_merged_input_position: InputPosition,
222) {
223 if input != to_be_merged_input {
224 trace_merge!(
225 _to_be_merged_input_position,
226 to_be_merged_input,
227 _input_position,
228 input,
229 "replaced"
230 );
231 *input = to_be_merged_input.clone();
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238 use crate::logging::enable_logging;
239 use std::collections::HashMap;
240
241 #[test]
242 fn it_works() {
243 enable_logging();
244
245 let mut map = HashMap::new();
246 map.insert(
247 "foo",
248 Input::from(HashMap::from([
249 ("key", Input::from("value")),
250 (
251 "list",
252 Input::from([Input::from(1), Input::from(2), Input::from(3)]),
253 ),
254 ("number", Input::from(3.14)),
255 ])),
256 );
257 let mut input = Input::from(map);
258 let position = InputPosition::new().new_with_key("first");
259
260 let mut to_be_merged_map = HashMap::new();
261 to_be_merged_map.insert(
262 "foo",
263 Input::from(HashMap::from([
264 ("key", Input::from("new value")),
265 ("new key", Input::from("value")),
266 (
267 "list",
268 Input::from([Input::from(1), Input::from(10), Input::from(3)]),
269 ),
270 ("number", Input::from(0.0)),
271 ])),
272 );
273 let to_be_merged_input = Input::from(to_be_merged_map);
274 let to_be_merged_position = InputPosition::new().new_with_key("second");
275 merge_with_positions(
276 &mut input,
277 position,
278 &to_be_merged_input,
279 to_be_merged_position,
280 );
281 }
282}