plugx_input/
merge.rs

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}