1use datavalue::OwnedDataValue;
9use std::sync::Arc;
10
11pub(crate) fn default_condition() -> serde_json::Value {
15 serde_json::Value::Bool(true)
16}
17
18pub fn get_nested_value<'b>(data: &'b OwnedDataValue, path: &str) -> Option<&'b OwnedDataValue> {
30 if path.is_empty() {
31 return Some(data);
32 }
33 get_nested_value_impl(data, path.split('.'))
34}
35
36pub fn set_nested_value(data: &mut OwnedDataValue, path: &str, value: OwnedDataValue) {
48 if path.is_empty() {
49 return;
50 }
51 let parts: Vec<&str> = path.split('.').collect();
52 set_nested_value_impl(data, &parts, value);
53}
54
55#[inline]
57pub fn get_nested_value_cloned(data: &OwnedDataValue, path: &str) -> Option<OwnedDataValue> {
58 get_nested_value(data, path).cloned()
59}
60
61pub fn get_nested_value_parts<'b>(
65 data: &'b OwnedDataValue,
66 parts: &[Arc<str>],
67) -> Option<&'b OwnedDataValue> {
68 get_nested_value_impl(data, parts.iter().map(Arc::as_ref))
69}
70
71fn get_nested_value_impl<'b, 'p>(
80 data: &'b OwnedDataValue,
81 parts: impl Iterator<Item = &'p str>,
82) -> Option<&'b OwnedDataValue> {
83 let mut current = data;
84 for part in parts {
85 match current {
86 OwnedDataValue::Object(pairs) => {
87 let key = strip_hash_prefix(part);
88 let slot = pairs.iter().find(|(k, _)| k == key)?;
89 current = &slot.1;
90 }
91 OwnedDataValue::Array(items) => {
92 let idx: usize = part.parse().ok()?;
93 current = items.get(idx)?;
94 }
95 _ => return None,
96 }
97 }
98 Some(current)
99}
100
101pub fn set_nested_value_parts(
107 data: &mut OwnedDataValue,
108 parts: &[Arc<str>],
109 value: OwnedDataValue,
110) {
111 if parts.is_empty() {
112 return;
113 }
114 set_nested_value_impl(data, parts, value);
115}
116
117fn set_nested_value_impl<P: AsRef<str>>(
127 data: &mut OwnedDataValue,
128 parts: &[P],
129 value: OwnedDataValue,
130) {
131 let last = parts.len() - 1;
132 let mut current = data;
133
134 for (i, part) in parts.iter().enumerate() {
135 let part = part.as_ref();
136 if i == last {
137 match current {
138 OwnedDataValue::Object(pairs) => {
139 let key = strip_hash_prefix(part);
140 if let Some(slot) = pairs.iter_mut().find(|(k, _)| k == key) {
141 slot.1 = value;
142 } else {
143 pairs.push((key.to_string(), value));
144 }
145 }
146 OwnedDataValue::Array(items) => {
147 if let Ok(idx) = part.parse::<usize>() {
148 while items.len() <= idx {
149 items.push(OwnedDataValue::Null);
150 }
151 items[idx] = value;
152 }
153 }
154 _ => {}
155 }
156 return;
157 }
158
159 let next_is_array = parts[i + 1].as_ref().parse::<usize>().is_ok();
163
164 match current {
165 OwnedDataValue::Object(pairs) => {
166 let key = strip_hash_prefix(part);
167 let idx = match pairs.iter().position(|(k, _)| k == key) {
168 Some(idx) => idx,
169 None => {
170 let child = if next_is_array {
171 OwnedDataValue::Array(Vec::new())
172 } else {
173 OwnedDataValue::Object(Vec::new())
174 };
175 pairs.push((key.to_string(), child));
176 pairs.len() - 1
177 }
178 };
179 current = &mut pairs[idx].1;
180 }
181 OwnedDataValue::Array(items) => {
182 let Ok(idx) = part.parse::<usize>() else {
183 return; };
185 while items.len() <= idx {
186 items.push(OwnedDataValue::Null);
187 }
188 if matches!(items[idx], OwnedDataValue::Null) {
189 items[idx] = if next_is_array {
190 OwnedDataValue::Array(Vec::new())
191 } else {
192 OwnedDataValue::Object(Vec::new())
193 };
194 }
195 current = &mut items[idx];
196 }
197 _ => return,
198 }
199 }
200}
201
202pub fn remove_nested_value(data: &mut OwnedDataValue, path: &str) -> Option<OwnedDataValue> {
240 if path.is_empty() {
241 return None;
242 }
243 let parts: Vec<&str> = path.split('.').collect();
244 remove_nested_value_parts(data, &parts)
245}
246
247pub(crate) fn remove_nested_value_parts<P: AsRef<str>>(
251 data: &mut OwnedDataValue,
252 parts: &[P],
253) -> Option<OwnedDataValue> {
254 let (last, parents) = parts.split_last()?;
255 let last = last.as_ref();
256
257 let mut current = data;
258 for part in parents {
259 let part = part.as_ref();
260 current = match current {
261 OwnedDataValue::Object(pairs) => {
262 let key = strip_hash_prefix(part);
263 let idx = pairs.iter().position(|(k, _)| k == key)?;
264 &mut pairs[idx].1
265 }
266 OwnedDataValue::Array(items) => {
267 let idx: usize = part.parse().ok()?;
268 items.get_mut(idx)?
269 }
270 _ => return None,
271 };
272 }
273
274 match current {
275 OwnedDataValue::Object(pairs) => {
276 let key = strip_hash_prefix(last);
277 let pos = pairs.iter().position(|(k, _)| k == key)?;
278 Some(pairs.remove(pos).1)
279 }
280 OwnedDataValue::Array(items) => {
281 let idx: usize = last.parse().ok()?;
282 if idx < items.len() {
283 Some(items.remove(idx))
284 } else {
285 None
286 }
287 }
288 _ => None,
289 }
290}
291
292#[inline]
295pub(crate) fn strip_hash_prefix(part: &str) -> &str {
296 part.strip_prefix('#').unwrap_or(part)
297}
298
299pub(crate) fn compute_data_path(target: &str) -> (Arc<str>, Arc<[Arc<str>]>) {
306 (
307 Arc::from(format!("data.{target}")),
308 compute_path_parts("data", target),
309 )
310}
311
312pub(crate) fn compute_path_parts(prefix: &str, target: &str) -> Arc<[Arc<str>]> {
319 std::iter::once(Arc::from(prefix))
320 .chain(target.split('.').map(Arc::from))
321 .collect()
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327 use serde_json::json;
328
329 fn dv(v: serde_json::Value) -> OwnedDataValue {
331 OwnedDataValue::from(&v)
332 }
333
334 #[test]
335 fn test_get_nested_value() {
336 let data = dv(json!({
337 "user": {
338 "name": "John",
339 "age": 30,
340 "addresses": [
341 {"city": "New York", "zip": "10001"},
342 {"city": "San Francisco", "zip": "94102"}
343 ],
344 "preferences": {
345 "theme": "dark",
346 "notifications": true
347 }
348 },
349 "items": [1, 2, 3]
350 }));
351
352 assert_eq!(
353 get_nested_value(&data, "user.name"),
354 Some(&dv(json!("John")))
355 );
356 assert_eq!(get_nested_value(&data, "user.age"), Some(&dv(json!(30))));
357
358 assert_eq!(
359 get_nested_value(&data, "user.preferences.theme"),
360 Some(&dv(json!("dark")))
361 );
362 assert_eq!(
363 get_nested_value(&data, "user.preferences.notifications"),
364 Some(&dv(json!(true)))
365 );
366
367 assert_eq!(get_nested_value(&data, "items.0"), Some(&dv(json!(1))));
368 assert_eq!(get_nested_value(&data, "items.2"), Some(&dv(json!(3))));
369
370 assert_eq!(
371 get_nested_value(&data, "user.addresses.0.city"),
372 Some(&dv(json!("New York")))
373 );
374 assert_eq!(
375 get_nested_value(&data, "user.addresses.1.zip"),
376 Some(&dv(json!("94102")))
377 );
378
379 assert_eq!(get_nested_value(&data, "user.missing"), None);
380 assert_eq!(get_nested_value(&data, "items.10"), None);
381 assert_eq!(get_nested_value(&data, "user.addresses.2.city"), None);
382 assert_eq!(get_nested_value(&data, "nonexistent.path"), None);
383 }
384
385 #[test]
386 fn test_set_nested_value() {
387 let mut data = dv(json!({}));
388
389 set_nested_value(&mut data, "name", dv(json!("Alice")));
390 assert_eq!(data, dv(json!({"name": "Alice"})));
391
392 set_nested_value(&mut data, "user.email", dv(json!("alice@example.com")));
393 assert_eq!(
394 data,
395 dv(json!({
396 "name": "Alice",
397 "user": {"email": "alice@example.com"}
398 }))
399 );
400
401 set_nested_value(&mut data, "name", dv(json!("Bob")));
402 assert_eq!(
403 data,
404 dv(json!({
405 "name": "Bob",
406 "user": {"email": "alice@example.com"}
407 }))
408 );
409
410 set_nested_value(&mut data, "settings.theme.mode", dv(json!("dark")));
411 assert_eq!(data["settings"]["theme"]["mode"], dv(json!("dark")));
412
413 set_nested_value(&mut data, "user.age", dv(json!(25)));
414 assert_eq!(data["user"]["age"], dv(json!(25)));
415 assert_eq!(data["user"]["email"], dv(json!("alice@example.com")));
416 }
417
418 #[test]
419 fn test_set_nested_value_with_arrays() {
420 let mut data = dv(json!({ "items": [1, 2, 3] }));
421
422 set_nested_value(&mut data, "items.0", dv(json!(10)));
423 assert_eq!(data["items"], dv(json!([10, 2, 3])));
424
425 set_nested_value(&mut data, "items.5", dv(json!(50)));
426 assert_eq!(data["items"], dv(json!([10, 2, 3, null, null, 50])));
427
428 let mut data2 = dv(json!({}));
429 set_nested_value(&mut data2, "matrix.0.0", dv(json!(1)));
430 set_nested_value(&mut data2, "matrix.0.1", dv(json!(2)));
431 set_nested_value(&mut data2, "matrix.1.0", dv(json!(3)));
432 assert_eq!(data2, dv(json!({ "matrix": [[1, 2], [3]] })));
433 }
434
435 #[test]
436 fn test_set_nested_value_array_expansion() {
437 let mut data = dv(json!({}));
438
439 set_nested_value(&mut data, "array.2", dv(json!("value")));
440 assert_eq!(data, dv(json!({ "array": [null, null, "value"] })));
441
442 let mut data2 = dv(json!({}));
443 set_nested_value(&mut data2, "deep.nested.0.field", dv(json!("test")));
444 assert_eq!(
445 data2,
446 dv(json!({ "deep": { "nested": [{ "field": "test" }] } }))
447 );
448 }
449
450 #[test]
451 fn test_get_nested_value_cloned() {
452 let data = dv(json!({
453 "user": {
454 "profile": {
455 "name": "Alice",
456 "settings": {"theme": "dark"}
457 }
458 }
459 }));
460
461 assert_eq!(
462 get_nested_value_cloned(&data, "user.profile.name"),
463 Some(dv(json!("Alice")))
464 );
465 assert_eq!(
466 get_nested_value_cloned(&data, "user.profile.settings"),
467 Some(dv(json!({ "theme": "dark" })))
468 );
469 assert_eq!(get_nested_value_cloned(&data, "user.missing"), None);
470 }
471
472 #[test]
473 fn test_get_nested_value_bounds_checking() {
474 let data = dv(json!({
475 "items": [1, 2, 3],
476 "nested": {
477 "array": [
478 {"id": 1},
479 {"id": 2}
480 ]
481 }
482 }));
483
484 assert_eq!(get_nested_value(&data, "items.0"), Some(&dv(json!(1))));
485 assert_eq!(get_nested_value(&data, "items.2"), Some(&dv(json!(3))));
486
487 assert_eq!(get_nested_value(&data, "items.10"), None);
488 assert_eq!(get_nested_value(&data, "items.999999"), None);
489
490 assert_eq!(get_nested_value(&data, "items.abc"), None);
491 assert_eq!(get_nested_value(&data, "items.-1"), None);
492 assert_eq!(get_nested_value(&data, "items.2.5"), None);
493
494 assert_eq!(
495 get_nested_value(&data, "nested.array.0.id"),
496 Some(&dv(json!(1)))
497 );
498 assert_eq!(get_nested_value(&data, "nested.array.5.id"), None);
499
500 assert_eq!(get_nested_value(&data, ""), Some(&data));
501 }
502
503 #[test]
504 fn test_set_nested_value_bounds_safety() {
505 let mut data = dv(json!({}));
506
507 set_nested_value(&mut data, "large.10", dv(json!("value")));
508 assert_eq!(data["large"].as_array().unwrap().len(), 11);
509 assert_eq!(data["large"][10], dv(json!("value")));
510 for i in 0..10usize {
511 assert_eq!(data["large"][i], dv(json!(null)));
512 }
513
514 let mut data2 = dv(json!({ "matrix": [] }));
515 set_nested_value(&mut data2, "matrix.2.1", dv(json!(5)));
516 assert_eq!(data2["matrix"][0], dv(json!(null)));
517 assert_eq!(data2["matrix"][1], dv(json!(null)));
518 assert_eq!(data2["matrix"][2][0], dv(json!(null)));
519 assert_eq!(data2["matrix"][2][1], dv(json!(5)));
520
521 let mut data3 = dv(json!({ "arr": [1, 2, 3] }));
522 set_nested_value(&mut data3, "arr.1", dv(json!("replaced")));
523 assert_eq!(data3["arr"], dv(json!([1, "replaced", 3])));
524 }
525
526 #[test]
527 fn test_hash_prefix_in_paths() {
528 let data = dv(json!({
529 "fields": {
530 "20": "numeric field name",
531 "#": "hash field",
532 "##": "double hash field",
533 "normal": "normal field"
534 }
535 }));
536
537 assert_eq!(
538 get_nested_value(&data, "fields.#20"),
539 Some(&dv(json!("numeric field name")))
540 );
541 assert_eq!(
542 get_nested_value(&data, "fields.##"),
543 Some(&dv(json!("hash field")))
544 );
545 assert_eq!(
546 get_nested_value(&data, "fields.###"),
547 Some(&dv(json!("double hash field")))
548 );
549 assert_eq!(
550 get_nested_value(&data, "fields.normal"),
551 Some(&dv(json!("normal field")))
552 );
553 assert_eq!(get_nested_value(&data, "fields.#999"), None);
554 }
555
556 #[test]
557 fn test_set_hash_prefix_in_paths() {
558 let mut data = dv(json!({}));
559
560 set_nested_value(&mut data, "fields.#20", dv(json!("value for 20")));
561 assert_eq!(data["fields"]["20"], dv(json!("value for 20")));
562
563 set_nested_value(&mut data, "fields.##", dv(json!("hash value")));
564 assert_eq!(data["fields"]["#"], dv(json!("hash value")));
565
566 set_nested_value(&mut data, "fields.###", dv(json!("double hash value")));
567 assert_eq!(data["fields"]["##"], dv(json!("double hash value")));
568
569 set_nested_value(&mut data, "fields.normal", dv(json!("normal value")));
570 assert_eq!(data["fields"]["normal"], dv(json!("normal value")));
571
572 assert_eq!(
573 data,
574 dv(json!({
575 "fields": {
576 "20": "value for 20",
577 "#": "hash value",
578 "##": "double hash value",
579 "normal": "normal value"
580 }
581 }))
582 );
583 }
584
585 #[test]
586 fn test_hash_prefix_with_arrays() {
587 let mut data = dv(json!({
588 "items": [
589 {"0": "field named zero", "id": 1},
590 {"1": "field named one", "id": 2}
591 ]
592 }));
593
594 assert_eq!(
595 get_nested_value(&data, "items.0.#0"),
596 Some(&dv(json!("field named zero")))
597 );
598 assert_eq!(
599 get_nested_value(&data, "items.1.#1"),
600 Some(&dv(json!("field named one")))
601 );
602
603 set_nested_value(&mut data, "items.0.#2", dv(json!("field named two")));
604 assert_eq!(data["items"][0]["2"], dv(json!("field named two")));
605
606 assert_eq!(get_nested_value(&data, "items.0.id"), Some(&dv(json!(1))));
607 assert_eq!(get_nested_value(&data, "items.1.id"), Some(&dv(json!(2))));
608 }
609
610 #[test]
611 fn test_hash_prefix_field_with_array_value() {
612 let data = dv(json!({
613 "data": {
614 "fields": {
615 "72": ["first", "second", "third"],
616 "100": ["alpha", "beta", "gamma"],
617 "normal": ["one", "two", "three"]
618 }
619 }
620 }));
621
622 assert_eq!(
623 get_nested_value(&data, "data.fields.#72.0"),
624 Some(&dv(json!("first")))
625 );
626 assert_eq!(
627 get_nested_value(&data, "data.fields.#72.1"),
628 Some(&dv(json!("second")))
629 );
630 assert_eq!(
631 get_nested_value(&data, "data.fields.#72.2"),
632 Some(&dv(json!("third")))
633 );
634
635 assert_eq!(
636 get_nested_value(&data, "data.fields.#100.0"),
637 Some(&dv(json!("alpha")))
638 );
639 assert_eq!(
640 get_nested_value(&data, "data.fields.#100.1"),
641 Some(&dv(json!("beta")))
642 );
643
644 assert_eq!(
645 get_nested_value(&data, "data.fields.normal.0"),
646 Some(&dv(json!("one")))
647 );
648
649 let mut data_mut = data.clone();
650 set_nested_value(&mut data_mut, "data.fields.#72.0", dv(json!("modified")));
651 assert_eq!(data_mut["data"]["fields"]["72"][0], dv(json!("modified")));
652
653 set_nested_value(&mut data_mut, "data.fields.#999.0", dv(json!("new value")));
654 assert_eq!(data_mut["data"]["fields"]["999"][0], dv(json!("new value")));
655
656 let complex_data = dv(json!({
657 "fields": {
658 "42": [
659 {"name": "item1", "value": 100},
660 {"name": "item2", "value": 200}
661 ]
662 }
663 }));
664
665 assert_eq!(
666 get_nested_value(&complex_data, "fields.#42.0.name"),
667 Some(&dv(json!("item1")))
668 );
669 assert_eq!(
670 get_nested_value(&complex_data, "fields.#42.1.value"),
671 Some(&dv(json!(200)))
672 );
673
674 let multi_hash_data = dv(json!({
675 "data": {
676 "#fields": {
677 "##": ["hash array"],
678 "10": ["numeric array"]
679 }
680 }
681 }));
682
683 assert_eq!(
684 get_nested_value(&multi_hash_data, "data.##fields.###.0"),
685 Some(&dv(json!("hash array")))
686 );
687 assert_eq!(
688 get_nested_value(&multi_hash_data, "data.##fields.#10.0"),
689 Some(&dv(json!("numeric array")))
690 );
691 }
692
693 fn as_json(v: &OwnedDataValue) -> serde_json::Value {
700 serde_json::Value::from(v)
701 }
702
703 #[test]
704 fn test_remove_nested_value_object() {
705 let mut data = dv(json!({"data": {"a": 1, "_b": 2}}));
706
707 assert_eq!(
708 remove_nested_value(&mut data, "data._b"),
709 Some(dv(json!(2)))
710 );
711 assert_eq!(as_json(&data), json!({"data": {"a": 1}}));
713
714 assert_eq!(remove_nested_value(&mut data, "data._b"), None);
716 assert_eq!(as_json(&data), json!({"data": {"a": 1}}));
717 }
718
719 #[test]
720 fn test_remove_nested_value_array_shifts_tail() {
721 let mut data = dv(json!({"items": [1, 2, 3]}));
722
723 assert_eq!(
724 remove_nested_value(&mut data, "items.1"),
725 Some(dv(json!(2)))
726 );
727 assert_eq!(as_json(&data), json!({"items": [1, 3]}));
729 }
730
731 #[test]
732 fn test_remove_nested_value_returns_subtree_intact() {
733 let mut data = dv(json!({"data": {"nested": {"x": [1, 2]}}}));
734
735 assert_eq!(
736 remove_nested_value(&mut data, "data.nested"),
737 Some(dv(json!({"x": [1, 2]})))
738 );
739 assert_eq!(as_json(&data), json!({"data": {}}));
740 }
741
742 #[test]
743 fn test_remove_nested_value_traverses_array_in_non_terminal_position() {
744 let mut data = dv(json!({"a": [{"k": 1}, {"k": 2}]}));
745
746 assert_eq!(remove_nested_value(&mut data, "a.1.k"), Some(dv(json!(2))));
747 assert_eq!(as_json(&data), json!({"a": [{"k": 1}, {}]}));
748 }
749
750 #[test]
751 fn test_remove_hash_prefix_in_paths() {
752 let mut data = dv(json!({"fields": {"20": "x", "#": "y", "##": "z"}}));
755
756 assert_eq!(
757 remove_nested_value(&mut data, "fields.#20"),
758 Some(dv(json!("x")))
759 );
760 assert_eq!(
761 remove_nested_value(&mut data, "fields.##"),
762 Some(dv(json!("y")))
763 );
764 assert_eq!(
765 remove_nested_value(&mut data, "fields.###"),
766 Some(dv(json!("z")))
767 );
768 assert_eq!(as_json(&data), json!({"fields": {}}));
769 }
770
771 #[test]
772 fn test_remove_nested_value_negative_cases_leave_tree_untouched() {
773 let original = json!({
776 "items": [1, 2, 3],
777 "a": [{"k": 1}],
778 "data": {"x": 1},
779 "b": 1
780 });
781
782 for path in [
783 "", "data.nope", "nope.x", "items.9", "a.5.k", "items.abc", "items.-1", "b.c", "b.c.d", ] {
793 let mut data = dv(original.clone());
794 assert_eq!(
795 remove_nested_value(&mut data, path),
796 None,
797 "path '{path}' should not resolve"
798 );
799 assert_eq!(
800 as_json(&data),
801 original,
802 "path '{path}' must leave the tree untouched"
803 );
804 }
805 }
806
807 #[test]
808 fn test_remove_nested_value_scalar_root() {
809 let mut scalar = dv(json!("scalar"));
810 assert_eq!(remove_nested_value(&mut scalar, "a"), None);
811 assert_eq!(as_json(&scalar), json!("scalar"));
812 }
813
814 #[test]
815 fn test_remove_nested_value_non_ascii_keys() {
816 let mut data = dv(json!({"データ": {"ключ": "значение"}}));
817
818 assert_eq!(
819 remove_nested_value(&mut data, "データ.ключ"),
820 Some(dv(json!("значение")))
821 );
822 assert_eq!(as_json(&data), json!({"データ": {}}));
823 }
824}