Skip to main content

pick/selector/
manipulate.rs

1use crate::error::PickError;
2use serde_json::Value;
3use super::types::Segment;
4
5/// Set a value at the given path, returning a new Value with the modification.
6/// Creates intermediate objects/arrays as needed.
7///
8/// Time: O(d * w) where d = path depth, w = width at each level (clone cost).
9/// Space: O(N) for the cloned tree.
10pub fn apply_set(value: &Value, segments: &[Segment], new_value: &Value) -> Result<Value, PickError> {
11    if segments.is_empty() {
12        return Ok(new_value.clone());
13    }
14
15    let segment = &segments[0];
16    let rest = &segments[1..];
17
18    if let Some(ref key) = segment.key {
19        // Navigate into object by key
20        match value {
21            Value::Object(map) => {
22                let mut new_map = map.clone();
23                let child = map.get(key).unwrap_or(&Value::Null);
24                let child = apply_set_with_indices(child, &segment.indices, rest, new_value)?;
25                new_map.insert(key.clone(), child);
26                Ok(Value::Object(new_map))
27            }
28            // If value is not an object, create one
29            _ => {
30                let mut new_map = serde_json::Map::new();
31                let child = apply_set_with_indices(&Value::Null, &segment.indices, rest, new_value)?;
32                new_map.insert(key.clone(), child);
33                Ok(Value::Object(new_map))
34            }
35        }
36    } else {
37        // Index-only segment
38        apply_set_with_indices(value, &segment.indices, rest, new_value)
39    }
40}
41
42fn apply_set_with_indices(
43    value: &Value,
44    indices: &[super::types::Index],
45    remaining_segments: &[Segment],
46    new_value: &Value,
47) -> Result<Value, PickError> {
48    if indices.is_empty() {
49        return apply_set(value, remaining_segments, new_value);
50    }
51
52    let index = &indices[0];
53    let rest_indices = &indices[1..];
54
55    match index {
56        super::types::Index::Number(n) => {
57            match value {
58                Value::Array(arr) => {
59                    let len = arr.len();
60                    let i = resolve_index(*n, len)?;
61                    let mut new_arr = arr.clone();
62                    if i < len {
63                        let child = &arr[i];
64                        new_arr[i] = apply_set_with_indices(child, rest_indices, remaining_segments, new_value)?;
65                    }
66                    Ok(Value::Array(new_arr))
67                }
68                _ => Err(PickError::NotAnArray(
69                    super::extract::value_type_name(value).into(),
70                )),
71            }
72        }
73        // Wildcard and Slice set operations are not supported
74        _ => Err(PickError::InvalidSelector(
75            "set() does not support wildcard or slice indices".into(),
76        )),
77    }
78}
79
80/// Delete the value at the given path, returning a new Value with the deletion.
81///
82/// Time: O(d * w) where d = path depth, w = width at each level.
83/// Space: O(N) for the cloned tree.
84pub fn apply_del(value: &Value, segments: &[Segment]) -> Result<Value, PickError> {
85    if segments.is_empty() {
86        // Deleting root → return null
87        return Ok(Value::Null);
88    }
89
90    let segment = &segments[0];
91    let rest = &segments[1..];
92
93    if let Some(ref key) = segment.key {
94        match value {
95            Value::Object(map) => {
96                if rest.is_empty() && segment.indices.is_empty() {
97                    // Terminal key: remove it
98                    let mut new_map = map.clone();
99                    new_map.remove(key);
100                    Ok(Value::Object(new_map))
101                } else if let Some(child) = map.get(key) {
102                    // Intermediate key: recurse
103                    let mut new_map = map.clone();
104                    let new_child = apply_del_with_indices(child, &segment.indices, rest)?;
105                    new_map.insert(key.clone(), new_child);
106                    Ok(Value::Object(new_map))
107                } else {
108                    // Key doesn't exist: no-op
109                    Ok(value.clone())
110                }
111            }
112            _ => Ok(value.clone()), // Can't delete from non-object: no-op
113        }
114    } else {
115        // Index-only segment
116        apply_del_with_indices(value, &segment.indices, rest)
117    }
118}
119
120fn apply_del_with_indices(
121    value: &Value,
122    indices: &[super::types::Index],
123    remaining_segments: &[Segment],
124) -> Result<Value, PickError> {
125    if indices.is_empty() {
126        return apply_del(value, remaining_segments);
127    }
128
129    let index = &indices[0];
130    let rest_indices = &indices[1..];
131
132    match index {
133        super::types::Index::Number(n) => {
134            match value {
135                Value::Array(arr) => {
136                    let len = arr.len();
137                    let i = resolve_index(*n, len)?;
138                    if i >= len {
139                        return Ok(value.clone()); // Out of bounds: no-op
140                    }
141
142                    if rest_indices.is_empty() && remaining_segments.is_empty() {
143                        // Terminal index: remove element
144                        let mut new_arr = arr.clone();
145                        new_arr.remove(i);
146                        Ok(Value::Array(new_arr))
147                    } else {
148                        // Intermediate index: recurse
149                        let mut new_arr = arr.clone();
150                        new_arr[i] = apply_del_with_indices(&arr[i], rest_indices, remaining_segments)?;
151                        Ok(Value::Array(new_arr))
152                    }
153                }
154                _ => Ok(value.clone()), // Not an array: no-op
155            }
156        }
157        _ => Err(PickError::InvalidSelector(
158            "del() does not support wildcard or slice indices".into(),
159        )),
160    }
161}
162
163fn resolve_index(n: i64, len: usize) -> Result<usize, PickError> {
164    if n < 0 {
165        let len_i64 = len as i64;
166        if n.unsigned_abs() > len as u64 {
167            return Err(PickError::IndexOutOfBounds(n));
168        }
169        Ok((len_i64 + n) as usize)
170    } else {
171        Ok(n as usize)
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178    use crate::selector::types::Selector;
179    use serde_json::json;
180
181    // ── apply_set ──
182
183    #[test]
184    fn set_top_level_key() {
185        let val = json!({"a": 1, "b": 2});
186        let sel = Selector::parse("a").unwrap();
187        let result = apply_set(&val, &sel.segments, &json!(99)).unwrap();
188        assert_eq!(result, json!({"a": 99, "b": 2}));
189    }
190
191    #[test]
192    fn set_nested_key() {
193        let val = json!({"x": {"y": 1}});
194        let sel = Selector::parse("x.y").unwrap();
195        let result = apply_set(&val, &sel.segments, &json!(2)).unwrap();
196        assert_eq!(result, json!({"x": {"y": 2}}));
197    }
198
199    #[test]
200    fn set_new_key() {
201        let val = json!({"a": 1});
202        let sel = Selector::parse("b").unwrap();
203        let result = apply_set(&val, &sel.segments, &json!(2)).unwrap();
204        assert_eq!(result["a"], json!(1));
205        assert_eq!(result["b"], json!(2));
206    }
207
208    #[test]
209    fn set_deep_new_path() {
210        let val = json!({});
211        let sel = Selector::parse("a.b").unwrap();
212        let result = apply_set(&val, &sel.segments, &json!(1)).unwrap();
213        assert_eq!(result, json!({"a": {"b": 1}}));
214    }
215
216    #[test]
217    fn set_array_element() {
218        let val = json!({"items": [1, 2, 3]});
219        let sel = Selector::parse("items[1]").unwrap();
220        let result = apply_set(&val, &sel.segments, &json!(99)).unwrap();
221        assert_eq!(result, json!({"items": [1, 99, 3]}));
222    }
223
224    #[test]
225    fn set_array_negative_index() {
226        let val = json!({"items": [1, 2, 3]});
227        let sel = Selector::parse("items[-1]").unwrap();
228        let result = apply_set(&val, &sel.segments, &json!(99)).unwrap();
229        assert_eq!(result, json!({"items": [1, 2, 99]}));
230    }
231
232    #[test]
233    fn set_nested_in_array() {
234        let val = json!({"users": [{"name": "Alice"}, {"name": "Bob"}]});
235        let sel = Selector::parse("users[0].name").unwrap();
236        let result = apply_set(&val, &sel.segments, &json!("Charlie")).unwrap();
237        assert_eq!(result["users"][0]["name"], json!("Charlie"));
238        assert_eq!(result["users"][1]["name"], json!("Bob"));
239    }
240
241    #[test]
242    fn set_string_value() {
243        let val = json!({"name": "old"});
244        let sel = Selector::parse("name").unwrap();
245        let result = apply_set(&val, &sel.segments, &json!("new")).unwrap();
246        assert_eq!(result, json!({"name": "new"}));
247    }
248
249    #[test]
250    fn set_null_value() {
251        let val = json!({"x": 1});
252        let sel = Selector::parse("x").unwrap();
253        let result = apply_set(&val, &sel.segments, &json!(null)).unwrap();
254        assert_eq!(result, json!({"x": null}));
255    }
256
257    #[test]
258    fn set_bool_value() {
259        let val = json!({"active": false});
260        let sel = Selector::parse("active").unwrap();
261        let result = apply_set(&val, &sel.segments, &json!(true)).unwrap();
262        assert_eq!(result, json!({"active": true}));
263    }
264
265    #[test]
266    fn set_empty_path_replaces_root() {
267        let val = json!({"a": 1});
268        let result = apply_set(&val, &[], &json!(42)).unwrap();
269        assert_eq!(result, json!(42));
270    }
271
272    // ── apply_del ──
273
274    #[test]
275    fn del_top_level_key() {
276        let val = json!({"a": 1, "b": 2});
277        let sel = Selector::parse("a").unwrap();
278        let result = apply_del(&val, &sel.segments).unwrap();
279        assert_eq!(result, json!({"b": 2}));
280    }
281
282    #[test]
283    fn del_nested_key() {
284        let val = json!({"x": {"y": 1, "z": 2}});
285        let sel = Selector::parse("x.y").unwrap();
286        let result = apply_del(&val, &sel.segments).unwrap();
287        assert_eq!(result, json!({"x": {"z": 2}}));
288    }
289
290    #[test]
291    fn del_missing_key() {
292        let val = json!({"a": 1});
293        let sel = Selector::parse("missing").unwrap();
294        let result = apply_del(&val, &sel.segments).unwrap();
295        assert_eq!(result, json!({"a": 1}));
296    }
297
298    #[test]
299    fn del_array_element() {
300        let val = json!({"items": [1, 2, 3]});
301        let sel = Selector::parse("items[1]").unwrap();
302        let result = apply_del(&val, &sel.segments).unwrap();
303        assert_eq!(result, json!({"items": [1, 3]}));
304    }
305
306    #[test]
307    fn del_array_first() {
308        let val = json!({"items": [1, 2, 3]});
309        let sel = Selector::parse("items[0]").unwrap();
310        let result = apply_del(&val, &sel.segments).unwrap();
311        assert_eq!(result, json!({"items": [2, 3]}));
312    }
313
314    #[test]
315    fn del_array_last_negative() {
316        let val = json!({"items": [1, 2, 3]});
317        let sel = Selector::parse("items[-1]").unwrap();
318        let result = apply_del(&val, &sel.segments).unwrap();
319        assert_eq!(result, json!({"items": [1, 2]}));
320    }
321
322    #[test]
323    fn del_nested_in_array() {
324        let val = json!({"users": [{"name": "Alice", "temp": "x"}, {"name": "Bob"}]});
325        let sel = Selector::parse("users[0].temp").unwrap();
326        let result = apply_del(&val, &sel.segments).unwrap();
327        assert_eq!(result["users"][0], json!({"name": "Alice"}));
328        assert_eq!(result["users"][1], json!({"name": "Bob"}));
329    }
330
331    #[test]
332    fn del_from_non_object_noop() {
333        let val = json!("hello");
334        let sel = Selector::parse("foo").unwrap();
335        let result = apply_del(&val, &sel.segments).unwrap();
336        assert_eq!(result, json!("hello"));
337    }
338
339    #[test]
340    fn del_empty_path_returns_null() {
341        let val = json!({"a": 1});
342        let result = apply_del(&val, &[]).unwrap();
343        assert_eq!(result, json!(null));
344    }
345
346    #[test]
347    fn del_preserves_other_keys() {
348        let val = json!({"a": 1, "b": 2, "c": 3});
349        let sel = Selector::parse("b").unwrap();
350        let result = apply_del(&val, &sel.segments).unwrap();
351        assert_eq!(result["a"], json!(1));
352        assert_eq!(result["c"], json!(3));
353        assert!(result.get("b").is_none());
354    }
355
356    // ══════════════════════════════════════════════
357    // Additional coverage tests
358    // ══════════════════════════════════════════════
359
360    // ── apply_set edge cases ──
361
362    #[test]
363    fn set_triple_nested_creates_intermediates() {
364        let val = json!({});
365        let sel = Selector::parse("a.b.c").unwrap();
366        let result = apply_set(&val, &sel.segments, &json!(42)).unwrap();
367        assert_eq!(result, json!({"a": {"b": {"c": 42}}}));
368    }
369
370    #[test]
371    fn set_deep_new_path_four_levels() {
372        let val = json!({});
373        let sel = Selector::parse("a.b.c.d").unwrap();
374        let result = apply_set(&val, &sel.segments, &json!("deep")).unwrap();
375        assert_eq!(result["a"]["b"]["c"]["d"], json!("deep"));
376    }
377
378    #[test]
379    fn set_overwrites_non_object_with_object() {
380        // Setting a.b when a is a string creates an object
381        let val = json!({"a": "string"});
382        let sel = Selector::parse("a.b").unwrap();
383        let result = apply_set(&val, &sel.segments, &json!(1)).unwrap();
384        assert_eq!(result, json!({"a": {"b": 1}}));
385    }
386
387    #[test]
388    fn set_on_root_number() {
389        let val = json!(42);
390        let sel = Selector::parse("a").unwrap();
391        let result = apply_set(&val, &sel.segments, &json!(1)).unwrap();
392        assert_eq!(result, json!({"a": 1}));
393    }
394
395    #[test]
396    fn set_preserves_sibling_keys() {
397        let val = json!({"x": {"a": 1, "b": 2, "c": 3}});
398        let sel = Selector::parse("x.b").unwrap();
399        let result = apply_set(&val, &sel.segments, &json!(99)).unwrap();
400        assert_eq!(result["x"]["a"], json!(1));
401        assert_eq!(result["x"]["b"], json!(99));
402        assert_eq!(result["x"]["c"], json!(3));
403    }
404
405    #[test]
406    fn set_array_first_element() {
407        let val = json!({"items": [10, 20, 30]});
408        let sel = Selector::parse("items[0]").unwrap();
409        let result = apply_set(&val, &sel.segments, &json!(99)).unwrap();
410        assert_eq!(result, json!({"items": [99, 20, 30]}));
411    }
412
413    #[test]
414    fn set_array_middle_element() {
415        let val = json!([1, 2, 3, 4, 5]);
416        let sel = Selector::parse("[2]").unwrap();
417        let result = apply_set(&val, &sel.segments, &json!(99)).unwrap();
418        assert_eq!(result, json!([1, 2, 99, 4, 5]));
419    }
420
421    #[test]
422    fn set_nested_array_in_object() {
423        let val = json!({"data": {"items": [1, 2, 3]}});
424        let sel = Selector::parse("data.items[1]").unwrap();
425        let result = apply_set(&val, &sel.segments, &json!(99)).unwrap();
426        assert_eq!(result["data"]["items"], json!([1, 99, 3]));
427    }
428
429    #[test]
430    fn set_with_negative_index_middle() {
431        let val = json!([10, 20, 30, 40]);
432        let sel = Selector::parse("[-2]").unwrap();
433        let result = apply_set(&val, &sel.segments, &json!(99)).unwrap();
434        assert_eq!(result, json!([10, 20, 99, 40]));
435    }
436
437    #[test]
438    fn set_wildcard_error() {
439        let val = json!({"items": [1, 2, 3]});
440        let sel = Selector::parse("items[*]").unwrap();
441        assert!(apply_set(&val, &sel.segments, &json!(0)).is_err());
442    }
443
444    #[test]
445    fn set_slice_error() {
446        let val = json!({"items": [1, 2, 3]});
447        let sel = Selector::parse("items[0:2]").unwrap();
448        assert!(apply_set(&val, &sel.segments, &json!(0)).is_err());
449    }
450
451    #[test]
452    fn set_object_value() {
453        let val = json!({"config": {}});
454        let sel = Selector::parse("config.server").unwrap();
455        let result = apply_set(&val, &sel.segments, &json!({"host": "localhost", "port": 8080})).unwrap();
456        assert_eq!(result["config"]["server"]["host"], json!("localhost"));
457    }
458
459    #[test]
460    fn set_array_value() {
461        let val = json!({"data": {}});
462        let sel = Selector::parse("data.items").unwrap();
463        let result = apply_set(&val, &sel.segments, &json!([1, 2, 3])).unwrap();
464        assert_eq!(result["data"]["items"], json!([1, 2, 3]));
465    }
466
467    // ── apply_del edge cases ──
468
469    #[test]
470    fn del_deeply_nested_missing_intermediate() {
471        // del(.a.b.c) when a.b doesn't exist → no-op
472        let val = json!({"a": 1});
473        let sel = Selector::parse("a.b.c").unwrap();
474        let result = apply_del(&val, &sel.segments).unwrap();
475        // a is not an object, so it's a no-op on the inner path
476        assert_eq!(result, json!({"a": 1}));
477    }
478
479    #[test]
480    fn del_deeply_nested_key() {
481        let val = json!({"a": {"b": {"c": 1, "d": 2}}});
482        let sel = Selector::parse("a.b.c").unwrap();
483        let result = apply_del(&val, &sel.segments).unwrap();
484        assert_eq!(result, json!({"a": {"b": {"d": 2}}}));
485    }
486
487    #[test]
488    fn del_array_last_element() {
489        let val = json!([1, 2, 3]);
490        let sel = Selector::parse("[2]").unwrap();
491        let result = apply_del(&val, &sel.segments).unwrap();
492        assert_eq!(result, json!([1, 2]));
493    }
494
495    #[test]
496    fn del_array_out_of_bounds_noop() {
497        // Deleting index beyond array length → no-op
498        let val = json!({"items": [1, 2, 3]});
499        let sel = Selector::parse("items[10]").unwrap();
500        let result = apply_del(&val, &sel.segments).unwrap();
501        assert_eq!(result, json!({"items": [1, 2, 3]}));
502    }
503
504    #[test]
505    fn del_negative_index_first() {
506        let val = json!([10, 20, 30]);
507        let sel = Selector::parse("[-3]").unwrap();
508        let result = apply_del(&val, &sel.segments).unwrap();
509        assert_eq!(result, json!([20, 30]));
510    }
511
512    #[test]
513    fn del_all_keys_one_by_one() {
514        let val = json!({"a": 1});
515        let sel = Selector::parse("a").unwrap();
516        let result = apply_del(&val, &sel.segments).unwrap();
517        assert_eq!(result, json!({}));
518    }
519
520    #[test]
521    fn del_from_array_non_array_noop() {
522        let val = json!({"items": "not an array"});
523        let sel = Selector::parse("items[0]").unwrap();
524        // items is a string, so applying del with index is a no-op on the index
525        let result = apply_del(&val, &sel.segments).unwrap();
526        // The key "items" exists but it's a string — applying [0] on string is no-op
527        assert_eq!(result["items"], json!("not an array"));
528    }
529
530    #[test]
531    fn del_wildcard_error() {
532        let val = json!({"items": [1, 2, 3]});
533        let sel = Selector::parse("items[*]").unwrap();
534        assert!(apply_del(&val, &sel.segments).is_err());
535    }
536
537    #[test]
538    fn del_slice_error() {
539        let val = json!({"items": [1, 2, 3]});
540        let sel = Selector::parse("items[0:2]").unwrap();
541        assert!(apply_del(&val, &sel.segments).is_err());
542    }
543
544    #[test]
545    fn del_nested_in_array_preserves_siblings() {
546        let val = json!([
547            {"name": "Alice", "age": 30, "temp": "x"},
548            {"name": "Bob", "age": 25}
549        ]);
550        let sel = Selector::parse("[0].temp").unwrap();
551        let result = apply_del(&val, &sel.segments).unwrap();
552        assert_eq!(result[0], json!({"name": "Alice", "age": 30}));
553        assert_eq!(result[1], json!({"name": "Bob", "age": 25}));
554    }
555
556    #[test]
557    fn del_single_element_array() {
558        let val = json!([42]);
559        let sel = Selector::parse("[0]").unwrap();
560        let result = apply_del(&val, &sel.segments).unwrap();
561        assert_eq!(result, json!([]));
562    }
563
564    // ── resolve_index ──
565
566    #[test]
567    fn resolve_positive_index() {
568        assert_eq!(resolve_index(0, 5).unwrap(), 0);
569        assert_eq!(resolve_index(4, 5).unwrap(), 4);
570    }
571
572    #[test]
573    fn resolve_negative_index() {
574        assert_eq!(resolve_index(-1, 5).unwrap(), 4);
575        assert_eq!(resolve_index(-5, 5).unwrap(), 0);
576    }
577
578    #[test]
579    fn resolve_negative_out_of_bounds() {
580        assert!(resolve_index(-6, 5).is_err());
581    }
582}