1use datavalue::OwnedDataValue;
9use std::sync::Arc;
10
11pub fn get_nested_value<'b>(data: &'b OwnedDataValue, path: &str) -> Option<&'b OwnedDataValue> {
23 if path.is_empty() {
24 return Some(data);
25 }
26
27 let mut current = data;
28
29 for part in path.split('.') {
30 match current {
31 OwnedDataValue::Object(pairs) => {
32 let key = strip_hash_prefix(part);
33 let slot = pairs.iter().find(|(k, _)| k == key)?;
34 current = &slot.1;
35 }
36 OwnedDataValue::Array(items) => {
37 let idx: usize = part.parse().ok()?;
38 current = items.get(idx)?;
39 }
40 _ => return None,
41 }
42 }
43
44 Some(current)
45}
46
47pub fn set_nested_value(data: &mut OwnedDataValue, path: &str, value: OwnedDataValue) {
59 if path.is_empty() {
60 return;
61 }
62
63 let parts: Vec<&str> = path.split('.').collect();
64 let last = parts.len() - 1;
65 let mut current = data;
66
67 for (i, part) in parts.iter().enumerate() {
68 if i == last {
69 match current {
70 OwnedDataValue::Object(pairs) => {
71 let key = strip_hash_prefix(part);
72 if let Some(slot) = pairs.iter_mut().find(|(k, _)| k == key) {
73 slot.1 = value;
74 } else {
75 pairs.push((key.to_string(), value));
76 }
77 }
78 OwnedDataValue::Array(items) => {
79 if let Ok(idx) = part.parse::<usize>() {
80 while items.len() <= idx {
81 items.push(OwnedDataValue::Null);
82 }
83 items[idx] = value;
84 }
85 }
86 _ => {}
87 }
88 return;
89 }
90
91 let next_is_array = parts[i + 1].parse::<usize>().is_ok();
95
96 match current {
97 OwnedDataValue::Object(pairs) => {
98 let key = strip_hash_prefix(part);
99 let idx = match pairs.iter().position(|(k, _)| k == key) {
100 Some(idx) => idx,
101 None => {
102 let child = if next_is_array {
103 OwnedDataValue::Array(Vec::new())
104 } else {
105 OwnedDataValue::Object(Vec::new())
106 };
107 pairs.push((key.to_string(), child));
108 pairs.len() - 1
109 }
110 };
111 current = &mut pairs[idx].1;
112 }
113 OwnedDataValue::Array(items) => {
114 let Ok(idx) = part.parse::<usize>() else {
115 return; };
117 while items.len() <= idx {
118 items.push(OwnedDataValue::Null);
119 }
120 if matches!(items[idx], OwnedDataValue::Null) {
121 items[idx] = if next_is_array {
122 OwnedDataValue::Array(Vec::new())
123 } else {
124 OwnedDataValue::Object(Vec::new())
125 };
126 }
127 current = &mut items[idx];
128 }
129 _ => return,
130 }
131 }
132}
133
134#[inline]
136pub fn get_nested_value_cloned(data: &OwnedDataValue, path: &str) -> Option<OwnedDataValue> {
137 get_nested_value(data, path).cloned()
138}
139
140pub fn get_nested_value_parts<'b>(
144 data: &'b OwnedDataValue,
145 parts: &[Arc<str>],
146) -> Option<&'b OwnedDataValue> {
147 if parts.is_empty() {
148 return Some(data);
149 }
150 let mut current = data;
151 for part in parts {
152 match current {
153 OwnedDataValue::Object(pairs) => {
154 let key = strip_hash_prefix(part);
155 let slot = pairs.iter().find(|(k, _)| k == key)?;
156 current = &slot.1;
157 }
158 OwnedDataValue::Array(items) => {
159 let idx: usize = part.parse().ok()?;
160 current = items.get(idx)?;
161 }
162 _ => return None,
163 }
164 }
165 Some(current)
166}
167
168pub fn set_nested_value_parts(
174 data: &mut OwnedDataValue,
175 parts: &[Arc<str>],
176 value: OwnedDataValue,
177) {
178 if parts.is_empty() {
179 return;
180 }
181 let last = parts.len() - 1;
182 let mut current = data;
183
184 for (i, part) in parts.iter().enumerate() {
185 if i == last {
186 match current {
187 OwnedDataValue::Object(pairs) => {
188 let key = strip_hash_prefix(part);
189 if let Some(slot) = pairs.iter_mut().find(|(k, _)| k == key) {
190 slot.1 = value;
191 } else {
192 pairs.push((key.to_string(), value));
193 }
194 }
195 OwnedDataValue::Array(items) => {
196 if let Ok(idx) = part.parse::<usize>() {
197 while items.len() <= idx {
198 items.push(OwnedDataValue::Null);
199 }
200 items[idx] = value;
201 }
202 }
203 _ => {}
204 }
205 return;
206 }
207
208 let next_part: &str = &parts[i + 1];
209 let next_is_array = next_part.parse::<usize>().is_ok();
210
211 match current {
212 OwnedDataValue::Object(pairs) => {
213 let key = strip_hash_prefix(part);
214 let idx = match pairs.iter().position(|(k, _)| k == key) {
215 Some(idx) => idx,
216 None => {
217 let child = if next_is_array {
218 OwnedDataValue::Array(Vec::new())
219 } else {
220 OwnedDataValue::Object(Vec::new())
221 };
222 pairs.push((key.to_string(), child));
223 pairs.len() - 1
224 }
225 };
226 current = &mut pairs[idx].1;
227 }
228 OwnedDataValue::Array(items) => {
229 let Ok(idx) = part.parse::<usize>() else {
230 return;
231 };
232 while items.len() <= idx {
233 items.push(OwnedDataValue::Null);
234 }
235 if matches!(items[idx], OwnedDataValue::Null) {
236 items[idx] = if next_is_array {
237 OwnedDataValue::Array(Vec::new())
238 } else {
239 OwnedDataValue::Object(Vec::new())
240 };
241 }
242 current = &mut items[idx];
243 }
244 _ => return,
245 }
246 }
247}
248
249#[inline]
252fn strip_hash_prefix(part: &str) -> &str {
253 part.strip_prefix('#').unwrap_or(part)
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259 use serde_json::json;
260
261 fn dv(v: serde_json::Value) -> OwnedDataValue {
263 OwnedDataValue::from(&v)
264 }
265
266 #[test]
267 fn test_get_nested_value() {
268 let data = dv(json!({
269 "user": {
270 "name": "John",
271 "age": 30,
272 "addresses": [
273 {"city": "New York", "zip": "10001"},
274 {"city": "San Francisco", "zip": "94102"}
275 ],
276 "preferences": {
277 "theme": "dark",
278 "notifications": true
279 }
280 },
281 "items": [1, 2, 3]
282 }));
283
284 assert_eq!(
285 get_nested_value(&data, "user.name"),
286 Some(&dv(json!("John")))
287 );
288 assert_eq!(get_nested_value(&data, "user.age"), Some(&dv(json!(30))));
289
290 assert_eq!(
291 get_nested_value(&data, "user.preferences.theme"),
292 Some(&dv(json!("dark")))
293 );
294 assert_eq!(
295 get_nested_value(&data, "user.preferences.notifications"),
296 Some(&dv(json!(true)))
297 );
298
299 assert_eq!(get_nested_value(&data, "items.0"), Some(&dv(json!(1))));
300 assert_eq!(get_nested_value(&data, "items.2"), Some(&dv(json!(3))));
301
302 assert_eq!(
303 get_nested_value(&data, "user.addresses.0.city"),
304 Some(&dv(json!("New York")))
305 );
306 assert_eq!(
307 get_nested_value(&data, "user.addresses.1.zip"),
308 Some(&dv(json!("94102")))
309 );
310
311 assert_eq!(get_nested_value(&data, "user.missing"), None);
312 assert_eq!(get_nested_value(&data, "items.10"), None);
313 assert_eq!(get_nested_value(&data, "user.addresses.2.city"), None);
314 assert_eq!(get_nested_value(&data, "nonexistent.path"), None);
315 }
316
317 #[test]
318 fn test_set_nested_value() {
319 let mut data = dv(json!({}));
320
321 set_nested_value(&mut data, "name", dv(json!("Alice")));
322 assert_eq!(data, dv(json!({"name": "Alice"})));
323
324 set_nested_value(&mut data, "user.email", dv(json!("alice@example.com")));
325 assert_eq!(
326 data,
327 dv(json!({
328 "name": "Alice",
329 "user": {"email": "alice@example.com"}
330 }))
331 );
332
333 set_nested_value(&mut data, "name", dv(json!("Bob")));
334 assert_eq!(
335 data,
336 dv(json!({
337 "name": "Bob",
338 "user": {"email": "alice@example.com"}
339 }))
340 );
341
342 set_nested_value(&mut data, "settings.theme.mode", dv(json!("dark")));
343 assert_eq!(data["settings"]["theme"]["mode"], dv(json!("dark")));
344
345 set_nested_value(&mut data, "user.age", dv(json!(25)));
346 assert_eq!(data["user"]["age"], dv(json!(25)));
347 assert_eq!(data["user"]["email"], dv(json!("alice@example.com")));
348 }
349
350 #[test]
351 fn test_set_nested_value_with_arrays() {
352 let mut data = dv(json!({ "items": [1, 2, 3] }));
353
354 set_nested_value(&mut data, "items.0", dv(json!(10)));
355 assert_eq!(data["items"], dv(json!([10, 2, 3])));
356
357 set_nested_value(&mut data, "items.5", dv(json!(50)));
358 assert_eq!(data["items"], dv(json!([10, 2, 3, null, null, 50])));
359
360 let mut data2 = dv(json!({}));
361 set_nested_value(&mut data2, "matrix.0.0", dv(json!(1)));
362 set_nested_value(&mut data2, "matrix.0.1", dv(json!(2)));
363 set_nested_value(&mut data2, "matrix.1.0", dv(json!(3)));
364 assert_eq!(data2, dv(json!({ "matrix": [[1, 2], [3]] })));
365 }
366
367 #[test]
368 fn test_set_nested_value_array_expansion() {
369 let mut data = dv(json!({}));
370
371 set_nested_value(&mut data, "array.2", dv(json!("value")));
372 assert_eq!(data, dv(json!({ "array": [null, null, "value"] })));
373
374 let mut data2 = dv(json!({}));
375 set_nested_value(&mut data2, "deep.nested.0.field", dv(json!("test")));
376 assert_eq!(
377 data2,
378 dv(json!({ "deep": { "nested": [{ "field": "test" }] } }))
379 );
380 }
381
382 #[test]
383 fn test_get_nested_value_cloned() {
384 let data = dv(json!({
385 "user": {
386 "profile": {
387 "name": "Alice",
388 "settings": {"theme": "dark"}
389 }
390 }
391 }));
392
393 assert_eq!(
394 get_nested_value_cloned(&data, "user.profile.name"),
395 Some(dv(json!("Alice")))
396 );
397 assert_eq!(
398 get_nested_value_cloned(&data, "user.profile.settings"),
399 Some(dv(json!({ "theme": "dark" })))
400 );
401 assert_eq!(get_nested_value_cloned(&data, "user.missing"), None);
402 }
403
404 #[test]
405 fn test_get_nested_value_bounds_checking() {
406 let data = dv(json!({
407 "items": [1, 2, 3],
408 "nested": {
409 "array": [
410 {"id": 1},
411 {"id": 2}
412 ]
413 }
414 }));
415
416 assert_eq!(get_nested_value(&data, "items.0"), Some(&dv(json!(1))));
417 assert_eq!(get_nested_value(&data, "items.2"), Some(&dv(json!(3))));
418
419 assert_eq!(get_nested_value(&data, "items.10"), None);
420 assert_eq!(get_nested_value(&data, "items.999999"), None);
421
422 assert_eq!(get_nested_value(&data, "items.abc"), None);
423 assert_eq!(get_nested_value(&data, "items.-1"), None);
424 assert_eq!(get_nested_value(&data, "items.2.5"), None);
425
426 assert_eq!(
427 get_nested_value(&data, "nested.array.0.id"),
428 Some(&dv(json!(1)))
429 );
430 assert_eq!(get_nested_value(&data, "nested.array.5.id"), None);
431
432 assert_eq!(get_nested_value(&data, ""), Some(&data));
433 }
434
435 #[test]
436 fn test_set_nested_value_bounds_safety() {
437 let mut data = dv(json!({}));
438
439 set_nested_value(&mut data, "large.10", dv(json!("value")));
440 assert_eq!(data["large"].as_array().unwrap().len(), 11);
441 assert_eq!(data["large"][10], dv(json!("value")));
442 for i in 0..10usize {
443 assert_eq!(data["large"][i], dv(json!(null)));
444 }
445
446 let mut data2 = dv(json!({ "matrix": [] }));
447 set_nested_value(&mut data2, "matrix.2.1", dv(json!(5)));
448 assert_eq!(data2["matrix"][0], dv(json!(null)));
449 assert_eq!(data2["matrix"][1], dv(json!(null)));
450 assert_eq!(data2["matrix"][2][0], dv(json!(null)));
451 assert_eq!(data2["matrix"][2][1], dv(json!(5)));
452
453 let mut data3 = dv(json!({ "arr": [1, 2, 3] }));
454 set_nested_value(&mut data3, "arr.1", dv(json!("replaced")));
455 assert_eq!(data3["arr"], dv(json!([1, "replaced", 3])));
456 }
457
458 #[test]
459 fn test_hash_prefix_in_paths() {
460 let data = dv(json!({
461 "fields": {
462 "20": "numeric field name",
463 "#": "hash field",
464 "##": "double hash field",
465 "normal": "normal field"
466 }
467 }));
468
469 assert_eq!(
470 get_nested_value(&data, "fields.#20"),
471 Some(&dv(json!("numeric field name")))
472 );
473 assert_eq!(
474 get_nested_value(&data, "fields.##"),
475 Some(&dv(json!("hash field")))
476 );
477 assert_eq!(
478 get_nested_value(&data, "fields.###"),
479 Some(&dv(json!("double hash field")))
480 );
481 assert_eq!(
482 get_nested_value(&data, "fields.normal"),
483 Some(&dv(json!("normal field")))
484 );
485 assert_eq!(get_nested_value(&data, "fields.#999"), None);
486 }
487
488 #[test]
489 fn test_set_hash_prefix_in_paths() {
490 let mut data = dv(json!({}));
491
492 set_nested_value(&mut data, "fields.#20", dv(json!("value for 20")));
493 assert_eq!(data["fields"]["20"], dv(json!("value for 20")));
494
495 set_nested_value(&mut data, "fields.##", dv(json!("hash value")));
496 assert_eq!(data["fields"]["#"], dv(json!("hash value")));
497
498 set_nested_value(&mut data, "fields.###", dv(json!("double hash value")));
499 assert_eq!(data["fields"]["##"], dv(json!("double hash value")));
500
501 set_nested_value(&mut data, "fields.normal", dv(json!("normal value")));
502 assert_eq!(data["fields"]["normal"], dv(json!("normal value")));
503
504 assert_eq!(
505 data,
506 dv(json!({
507 "fields": {
508 "20": "value for 20",
509 "#": "hash value",
510 "##": "double hash value",
511 "normal": "normal value"
512 }
513 }))
514 );
515 }
516
517 #[test]
518 fn test_hash_prefix_with_arrays() {
519 let mut data = dv(json!({
520 "items": [
521 {"0": "field named zero", "id": 1},
522 {"1": "field named one", "id": 2}
523 ]
524 }));
525
526 assert_eq!(
527 get_nested_value(&data, "items.0.#0"),
528 Some(&dv(json!("field named zero")))
529 );
530 assert_eq!(
531 get_nested_value(&data, "items.1.#1"),
532 Some(&dv(json!("field named one")))
533 );
534
535 set_nested_value(&mut data, "items.0.#2", dv(json!("field named two")));
536 assert_eq!(data["items"][0]["2"], dv(json!("field named two")));
537
538 assert_eq!(get_nested_value(&data, "items.0.id"), Some(&dv(json!(1))));
539 assert_eq!(get_nested_value(&data, "items.1.id"), Some(&dv(json!(2))));
540 }
541
542 #[test]
543 fn test_hash_prefix_field_with_array_value() {
544 let data = dv(json!({
545 "data": {
546 "fields": {
547 "72": ["first", "second", "third"],
548 "100": ["alpha", "beta", "gamma"],
549 "normal": ["one", "two", "three"]
550 }
551 }
552 }));
553
554 assert_eq!(
555 get_nested_value(&data, "data.fields.#72.0"),
556 Some(&dv(json!("first")))
557 );
558 assert_eq!(
559 get_nested_value(&data, "data.fields.#72.1"),
560 Some(&dv(json!("second")))
561 );
562 assert_eq!(
563 get_nested_value(&data, "data.fields.#72.2"),
564 Some(&dv(json!("third")))
565 );
566
567 assert_eq!(
568 get_nested_value(&data, "data.fields.#100.0"),
569 Some(&dv(json!("alpha")))
570 );
571 assert_eq!(
572 get_nested_value(&data, "data.fields.#100.1"),
573 Some(&dv(json!("beta")))
574 );
575
576 assert_eq!(
577 get_nested_value(&data, "data.fields.normal.0"),
578 Some(&dv(json!("one")))
579 );
580
581 let mut data_mut = data.clone();
582 set_nested_value(&mut data_mut, "data.fields.#72.0", dv(json!("modified")));
583 assert_eq!(data_mut["data"]["fields"]["72"][0], dv(json!("modified")));
584
585 set_nested_value(&mut data_mut, "data.fields.#999.0", dv(json!("new value")));
586 assert_eq!(data_mut["data"]["fields"]["999"][0], dv(json!("new value")));
587
588 let complex_data = dv(json!({
589 "fields": {
590 "42": [
591 {"name": "item1", "value": 100},
592 {"name": "item2", "value": 200}
593 ]
594 }
595 }));
596
597 assert_eq!(
598 get_nested_value(&complex_data, "fields.#42.0.name"),
599 Some(&dv(json!("item1")))
600 );
601 assert_eq!(
602 get_nested_value(&complex_data, "fields.#42.1.value"),
603 Some(&dv(json!(200)))
604 );
605
606 let multi_hash_data = dv(json!({
607 "data": {
608 "#fields": {
609 "##": ["hash array"],
610 "10": ["numeric array"]
611 }
612 }
613 }));
614
615 assert_eq!(
616 get_nested_value(&multi_hash_data, "data.##fields.###.0"),
617 Some(&dv(json!("hash array")))
618 );
619 assert_eq!(
620 get_nested_value(&multi_hash_data, "data.##fields.#10.0"),
621 Some(&dv(json!("numeric array")))
622 );
623 }
624}