1#![doc = include_str!("../README.md")]
2
3use std::collections::HashSet;
4
5use serde_json::*;
6
7type Document = Map<String, Value>;
8
9const SPLIT_SYMBOL: char = '.';
10
11fn contained_in(selector: &str, key: &str) -> bool {
13 selector.starts_with(key)
14 && selector[key.len()..]
15 .chars()
16 .next()
17 .map(|c| c == SPLIT_SYMBOL)
18 .unwrap_or(true)
19}
20
21pub fn map_leaf_values<'a>(
57 value: &mut Map<String, Value>,
58 selectors: impl IntoIterator<Item = &'a str>,
59 mut mapper: impl FnMut(&str, &mut Value),
60) {
61 let selectors: Vec<_> = selectors.into_iter().collect();
62 map_leaf_values_in_object(value, &selectors, "", &mut mapper);
63}
64
65pub fn map_leaf_values_in_object<'a>(
66 value: &mut Map<String, Value>,
67 selectors: &[&'a str],
68 base_key: &str,
69 mapper: &mut impl FnMut(&str, &mut Value),
70) {
71 for (key, value) in value.iter_mut() {
72 let base_key = if base_key.is_empty() {
73 key.to_string()
74 } else {
75 format!("{}{}{}", base_key, SPLIT_SYMBOL, key)
76 };
77
78 let should_continue = selectors.into_iter().any(|ref selector| {
81 contained_in(selector, &base_key) || contained_in(&base_key, selector)
82 });
83
84 if should_continue {
85 match value {
86 Value::Object(object) => {
87 map_leaf_values_in_object(object, selectors, &base_key, mapper)
88 }
89 Value::Array(array) => {
90 map_leaf_values_in_array(array, selectors, &base_key, mapper)
91 }
92 value => mapper(&base_key, value),
93 }
94 }
95 }
96}
97
98pub fn map_leaf_values_in_array(
99 values: &mut Vec<Value>,
100 selectors: &[&str],
101 base_key: &str,
102 mapper: &mut impl FnMut(&str, &mut Value),
103) {
104 for value in values.iter_mut() {
105 match value {
106 Value::Object(object) => {
107 map_leaf_values_in_object(object, selectors, &base_key, mapper)
108 }
109 Value::Array(array) => map_leaf_values_in_array(array, selectors, &base_key, mapper),
110 value => mapper(&base_key, value),
111 }
112 }
113}
114
115pub fn select_values<'a>(
144 value: &Map<String, Value>,
145 selectors: impl IntoIterator<Item = &'a str>,
146) -> Map<String, Value> {
147 let selectors = selectors.into_iter().collect();
148 create_value(value, selectors)
149}
150
151fn create_value(value: &Document, mut selectors: HashSet<&str>) -> Document {
152 let mut new_value: Document = Map::new();
153
154 for (key, value) in value.iter() {
155 if selectors.contains(key as &str) {
157 new_value.insert(key.to_string(), value.clone());
158 if is_simple(key) {
161 selectors.remove(key as &str);
162 continue;
163 }
164 }
165
166 let sub_selectors: HashSet<&str> = selectors
170 .iter()
171 .filter(|s| contained_in(s, key))
172 .filter_map(|s| s.trim_start_matches(key).get(SPLIT_SYMBOL.len_utf8()..))
173 .collect();
174
175 if !sub_selectors.is_empty() {
176 match value {
177 Value::Array(array) => {
178 let array = create_array(array, &sub_selectors);
179 if !array.is_empty() {
180 new_value.insert(key.to_string(), array.into());
181 }
182 }
183 Value::Object(object) => {
184 let object = create_value(object, sub_selectors);
185 if !object.is_empty() {
186 new_value.insert(key.to_string(), object.into());
187 }
188 }
189 _ => (),
190 }
191 }
192 }
193
194 new_value
195}
196
197fn create_array(array: &Vec<Value>, selectors: &HashSet<&str>) -> Vec<Value> {
198 let mut res = Vec::new();
199
200 for value in array {
201 match value {
202 Value::Array(array) => {
203 let array = create_array(array, selectors);
204 if !array.is_empty() {
205 res.push(array.into());
206 }
207 }
208 Value::Object(object) => {
209 let object = create_value(object, selectors.clone());
210 if !object.is_empty() {
211 res.push(object.into());
212 }
213 }
214 _ => (),
215 }
216 }
217
218 res
219}
220
221fn is_simple(key: impl AsRef<str>) -> bool {
222 !key.as_ref().contains(SPLIT_SYMBOL)
223}
224
225#[cfg(test)]
226mod tests {
227 use big_s::S;
228
229 use super::*;
230
231 #[test]
232 fn test_contained_in() {
233 assert!(contained_in("animaux", "animaux"));
234 assert!(contained_in("animaux.chien", "animaux"));
235 assert!(contained_in(
236 "animaux.chien.race.bouvier bernois.fourrure.couleur",
237 "animaux"
238 ));
239 assert!(contained_in(
240 "animaux.chien.race.bouvier bernois.fourrure.couleur",
241 "animaux.chien"
242 ));
243 assert!(contained_in(
244 "animaux.chien.race.bouvier bernois.fourrure.couleur",
245 "animaux.chien.race.bouvier bernois"
246 ));
247 assert!(contained_in(
248 "animaux.chien.race.bouvier bernois.fourrure.couleur",
249 "animaux.chien.race.bouvier bernois.fourrure"
250 ));
251 assert!(contained_in(
252 "animaux.chien.race.bouvier bernois.fourrure.couleur",
253 "animaux.chien.race.bouvier bernois.fourrure.couleur"
254 ));
255
256 assert!(!contained_in("chien", "chat"));
258 assert!(!contained_in("animaux", "animaux.chien"));
259 assert!(!contained_in("animaux.chien", "animaux.chat"));
260
261 assert!(!contained_in("animaux.chien", "anima"));
263 assert!(!contained_in("animaux.chien", "animau"));
264 assert!(!contained_in("animaux.chien", "animaux."));
265 assert!(!contained_in("animaux.chien", "animaux.c"));
266 assert!(!contained_in("animaux.chien", "animaux.ch"));
267 assert!(!contained_in("animaux.chien", "animaux.chi"));
268 assert!(!contained_in("animaux.chien", "animaux.chie"));
269 }
270
271 #[test]
272 fn simple_key() {
273 let value: Value = json!({
274 "name": "peanut",
275 "age": 8,
276 "race": {
277 "name": "bernese mountain",
278 "avg_age": 12,
279 "size": "80cm",
280 }
281 });
282 let value: &Document = value.as_object().unwrap();
283
284 let res: Value = select_values(value, vec!["name"]).into();
285 assert_eq!(
286 res,
287 json!({
288 "name": "peanut",
289 })
290 );
291
292 let res: Value = select_values(value, vec!["age"]).into();
293 assert_eq!(
294 res,
295 json!({
296 "age": 8,
297 })
298 );
299
300 let res: Value = select_values(value, vec!["name", "age"]).into();
301 assert_eq!(
302 res,
303 json!({
304 "name": "peanut",
305 "age": 8,
306 })
307 );
308
309 let res: Value = select_values(value, vec!["race"]).into();
310 assert_eq!(
311 res,
312 json!({
313 "race": {
314 "name": "bernese mountain",
315 "avg_age": 12,
316 "size": "80cm",
317 }
318 })
319 );
320
321 let res: Value = select_values(value, vec!["name", "age", "race"]).into();
322 assert_eq!(
323 res,
324 json!({
325 "name": "peanut",
326 "age": 8,
327 "race": {
328 "name": "bernese mountain",
329 "avg_age": 12,
330 "size": "80cm",
331 }
332 })
333 );
334 }
335
336 #[test]
337 fn complex_key() {
338 let value: Value = json!({
339 "name": "peanut",
340 "age": 8,
341 "race": {
342 "name": "bernese mountain",
343 "avg_age": 12,
344 "size": "80cm",
345 }
346 });
347 let value: &Document = value.as_object().unwrap();
348
349 let res: Value = select_values(value, vec!["race"]).into();
350 assert_eq!(
351 res,
352 json!({
353 "race": {
354 "name": "bernese mountain",
355 "avg_age": 12,
356 "size": "80cm",
357 }
358 })
359 );
360
361 println!("RIGHT BEFORE");
362
363 let res: Value = select_values(value, vec!["race.name"]).into();
364 assert_eq!(
365 res,
366 json!({
367 "race": {
368 "name": "bernese mountain",
369 }
370 })
371 );
372
373 let res: Value = select_values(value, vec!["race.name", "race.size"]).into();
374 assert_eq!(
375 res,
376 json!({
377 "race": {
378 "name": "bernese mountain",
379 "size": "80cm",
380 }
381 })
382 );
383
384 let res: Value = select_values(
385 value,
386 vec!["race.name", "race.size", "race.avg_age", "race.size", "age"],
387 )
388 .into();
389 assert_eq!(
390 res,
391 json!({
392 "age": 8,
393 "race": {
394 "name": "bernese mountain",
395 "avg_age": 12,
396 "size": "80cm",
397 }
398 })
399 );
400
401 let res: Value = select_values(value, vec!["race.name", "race"]).into();
402 assert_eq!(
403 res,
404 json!({
405 "race": {
406 "name": "bernese mountain",
407 "avg_age": 12,
408 "size": "80cm",
409 }
410 })
411 );
412
413 let res: Value = select_values(value, vec!["race", "race.name"]).into();
414 assert_eq!(
415 res,
416 json!({
417 "race": {
418 "name": "bernese mountain",
419 "avg_age": 12,
420 "size": "80cm",
421 }
422 })
423 );
424 }
425
426 #[test]
427 fn multi_level_nested() {
428 let value: Value = json!({
429 "jean": {
430 "age": 8,
431 "race": {
432 "name": "bernese mountain",
433 "size": "80cm",
434 }
435 }
436 });
437 let value: &Document = value.as_object().unwrap();
438
439 let res: Value = select_values(value, vec!["jean"]).into();
440 assert_eq!(
441 res,
442 json!({
443 "jean": {
444 "age": 8,
445 "race": {
446 "name": "bernese mountain",
447 "size": "80cm",
448 }
449 }
450 })
451 );
452
453 let res: Value = select_values(value, vec!["jean.age"]).into();
454 assert_eq!(
455 res,
456 json!({
457 "jean": {
458 "age": 8,
459 }
460 })
461 );
462
463 let res: Value = select_values(value, vec!["jean.race.size"]).into();
464 assert_eq!(
465 res,
466 json!({
467 "jean": {
468 "race": {
469 "size": "80cm",
470 }
471 }
472 })
473 );
474
475 let res: Value = select_values(value, vec!["jean.race.name", "jean.age"]).into();
476 assert_eq!(
477 res,
478 json!({
479 "jean": {
480 "age": 8,
481 "race": {
482 "name": "bernese mountain",
483 }
484 }
485 })
486 );
487
488 let res: Value = select_values(value, vec!["jean.race"]).into();
489 assert_eq!(
490 res,
491 json!({
492 "jean": {
493 "race": {
494 "name": "bernese mountain",
495 "size": "80cm",
496 }
497 }
498 })
499 );
500 }
501
502 #[test]
503 fn array_and_deep_nested() {
504 let value: Value = json!({
505 "doggos": [
506 {
507 "jean": {
508 "age": 8,
509 "race": {
510 "name": "bernese mountain",
511 "size": "80cm",
512 }
513 }
514 },
515 {
516 "marc": {
517 "age": 4,
518 "race": {
519 "name": "golden retriever",
520 "size": "60cm",
521 }
522 }
523 },
524 ]
525 });
526 let value: &Document = value.as_object().unwrap();
527
528 let res: Value = select_values(value, vec!["doggos.jean"]).into();
529 assert_eq!(
530 res,
531 json!({
532 "doggos": [
533 {
534 "jean": {
535 "age": 8,
536 "race": {
537 "name": "bernese mountain",
538 "size": "80cm",
539 }
540 }
541 }
542 ]
543 })
544 );
545
546 let res: Value = select_values(value, vec!["doggos.marc"]).into();
547 assert_eq!(
548 res,
549 json!({
550 "doggos": [
551 {
552 "marc": {
553 "age": 4,
554 "race": {
555 "name": "golden retriever",
556 "size": "60cm",
557 }
558 }
559 }
560 ]
561 })
562 );
563
564 let res: Value = select_values(value, vec!["doggos.marc.race"]).into();
565 assert_eq!(
566 res,
567 json!({
568 "doggos": [
569 {
570 "marc": {
571 "race": {
572 "name": "golden retriever",
573 "size": "60cm",
574 }
575 }
576 }
577 ]
578 })
579 );
580
581 let res: Value =
582 select_values(value, vec!["doggos.marc.race.name", "doggos.marc.age"]).into();
583
584 assert_eq!(
585 res,
586 json!({
587 "doggos": [
588 {
589 "marc": {
590 "age": 4,
591 "race": {
592 "name": "golden retriever",
593 }
594 }
595 }
596 ]
597 })
598 );
599
600 let res: Value = select_values(
601 value,
602 vec![
603 "doggos.marc.race.name",
604 "doggos.marc.age",
605 "doggos.jean.race.name",
606 "other.field",
607 ],
608 )
609 .into();
610
611 assert_eq!(
612 res,
613 json!({
614 "doggos": [
615 {
616 "jean": {
617 "race": {
618 "name": "bernese mountain",
619 }
620 }
621 },
622 {
623 "marc": {
624 "age": 4,
625 "race": {
626 "name": "golden retriever",
627 }
628 }
629 }
630 ]
631 })
632 );
633 }
634
635 #[test]
636 fn all_conflict_variation() {
637 let value: Value = json!({
638 "pet.dog.name": "jean",
639 "pet.dog": {
640 "name": "bob"
641 },
642 "pet": {
643 "dog.name": "michel"
644 },
645 "pet": {
646 "dog": {
647 "name": "milan"
648 }
649 }
650 });
651 let value: &Document = value.as_object().unwrap();
652
653 let res: Value = select_values(value, vec!["pet.dog.name"]).into();
654 assert_eq!(
655 res,
656 json!({
657 "pet.dog.name": "jean",
658 "pet.dog": {
659 "name": "bob"
660 },
661 "pet": {
662 "dog.name": "michel"
663 },
664 "pet": {
665 "dog": {
666 "name": "milan"
667 }
668 }
669 })
670 );
671
672 let value: Value = json!({
673 "pet.dog.name": "jean",
674 "pet.dog": {
675 "name": "bob",
676 },
677 "pet": {
678 "dog.name": "michel",
679 "dog": {
680 "name": "milan",
681 }
682 }
683 });
684 let value: &Document = value.as_object().unwrap();
685
686 let res: Value = select_values(value, vec!["pet.dog.name", "pet.dog", "pet"]).into();
687
688 assert_eq!(
689 res,
690 json!({
691 "pet.dog.name": "jean",
692 "pet.dog": {
693 "name": "bob",
694 },
695 "pet": {
696 "dog.name": "michel",
697 "dog": {
698 "name": "milan",
699 }
700 }
701 })
702 );
703 }
704
705 #[test]
706 fn map_object() {
707 let mut value: Value = json!({
708 "jean": {
709 "age": 8,
710 "race": {
711 "name": "bernese mountain",
712 "size": "80cm",
713 }
714 }
715 });
716
717 map_leaf_values(
718 value.as_object_mut().unwrap(),
719 ["jean.race.name"],
720 |key, value| match (value, dbg!(key)) {
721 (Value::String(name), "jean.race.name") => *name = S("patou"),
722 _ => unreachable!(),
723 },
724 );
725
726 assert_eq!(
727 value,
728 json!({
729 "jean": {
730 "age": 8,
731 "race": {
732 "name": "patou",
733 "size": "80cm",
734 }
735 }
736 })
737 );
738
739 let mut value: Value = json!({
740 "jean": {
741 "age": 8,
742 "race": {
743 "name": "bernese mountain",
744 "size": "80cm",
745 }
746 },
747 "bob": "lolpied",
748 });
749
750 let mut calls = 0;
751 map_leaf_values(value.as_object_mut().unwrap(), ["jean"], |key, value| {
752 calls += 1;
753 match (value, key) {
754 (Value::String(name), "jean.race.name") => *name = S("patou"),
755 _ => println!("Called with {key}"),
756 }
757 });
758
759 assert_eq!(calls, 3);
760 assert_eq!(
761 value,
762 json!({
763 "jean": {
764 "age": 8,
765 "race": {
766 "name": "patou",
767 "size": "80cm",
768 }
769 },
770 "bob": "lolpied",
771 })
772 );
773 }
774}