Skip to main content

pick/selector/
manipulate.rs

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