@@ -540,42 +540,58 @@ impl ApplyToInternal for WithRange<PathList> {
540
540
}
541
541
}
542
542
PathList :: Key ( key, tail) => {
543
- if let JSON :: Array ( array) = data {
544
- return self . apply_to_array ( array, vars, input_path) ;
545
- }
546
-
547
543
let input_path_with_key = input_path. append ( key. to_json ( ) ) ;
548
544
549
- if !matches ! ( data, JSON :: Object ( _) ) {
550
- return (
551
- None ,
552
- vec ! [ ApplyToError :: new(
553
- format!(
554
- "Property {} not found in {}" ,
555
- key. dotted( ) ,
556
- json_type_name( data) ,
557
- ) ,
558
- input_path_with_key. to_vec( ) ,
559
- key. range( ) ,
560
- ) ] ,
561
- ) ;
562
- }
563
-
564
- if let Some ( child) = data. get ( key. as_str ( ) ) {
565
- tail. apply_to_path ( child, vars, & input_path_with_key)
545
+ if let JSON :: Array ( array) = data {
546
+ // If we recursively call self.apply_to_array, it will end
547
+ // up invoking the tail of the key recursively, whereas we
548
+ // want to apply the tail once to the entire output array of
549
+ // shallow key lookups. To keep the recursion shallow, we
550
+ // need a version of self that has the same key but no tail.
551
+ let empty_tail = WithRange :: new ( PathList :: Empty , tail. range ( ) ) ;
552
+ let self_with_empty_tail =
553
+ WithRange :: new ( PathList :: Key ( key. clone ( ) , empty_tail) , key. range ( ) ) ;
554
+
555
+ self_with_empty_tail
556
+ . apply_to_array ( array, vars, input_path)
557
+ . and_then_collecting_errors ( |shallow_mapped_array| {
558
+ // This tail.apply_to_path call happens only once,
559
+ // passing to the original/top-level tail the entire
560
+ // array produced by key-related recursion/mapping.
561
+ tail. apply_to_path ( shallow_mapped_array, vars, & input_path_with_key)
562
+ } )
566
563
} else {
567
- (
568
- None ,
569
- vec ! [ ApplyToError :: new(
570
- format!(
571
- "Property {} not found in {}" ,
572
- key. dotted( ) ,
573
- json_type_name( data) ,
574
- ) ,
575
- input_path_with_key. to_vec( ) ,
576
- key. range( ) ,
577
- ) ] ,
578
- )
564
+ if !matches ! ( data, JSON :: Object ( _) ) {
565
+ return (
566
+ None ,
567
+ vec ! [ ApplyToError :: new(
568
+ format!(
569
+ "Property {} not found in {}" ,
570
+ key. dotted( ) ,
571
+ json_type_name( data) ,
572
+ ) ,
573
+ input_path_with_key. to_vec( ) ,
574
+ key. range( ) ,
575
+ ) ] ,
576
+ ) ;
577
+ }
578
+
579
+ if let Some ( child) = data. get ( key. as_str ( ) ) {
580
+ tail. apply_to_path ( child, vars, & input_path_with_key)
581
+ } else {
582
+ (
583
+ None ,
584
+ vec ! [ ApplyToError :: new(
585
+ format!(
586
+ "Property {} not found in {}" ,
587
+ key. dotted( ) ,
588
+ json_type_name( data) ,
589
+ ) ,
590
+ input_path_with_key. to_vec( ) ,
591
+ key. range( ) ,
592
+ ) ] ,
593
+ )
594
+ }
579
595
}
580
596
}
581
597
PathList :: Expr ( expr, tail) => expr
@@ -586,14 +602,20 @@ impl ApplyToInternal for WithRange<PathList> {
586
602
input_path. append ( JSON :: String ( format ! ( "->{}" , method_name. as_ref( ) ) . into ( ) ) ) ;
587
603
588
604
if let Some ( method) = ArrowMethod :: lookup ( method_name) {
589
- method. apply (
590
- method_name,
591
- method_args. as_ref ( ) ,
592
- data,
593
- vars,
594
- & method_path,
595
- tail,
596
- )
605
+ let ( result_opt, errors) =
606
+ method. apply ( method_name, method_args. as_ref ( ) , data, vars, & method_path) ;
607
+
608
+ if let Some ( result) = result_opt {
609
+ tail. apply_to_path ( & result, vars, & method_path)
610
+ . prepend_errors ( errors)
611
+ } else {
612
+ // If the method produced no output, assume the errors
613
+ // explain the None. Methods can legitimately produce
614
+ // None without errors (like ->first or ->last on an
615
+ // empty array), so we do not report any blanket error
616
+ // here when errors.is_empty().
617
+ ( None , errors)
618
+ }
597
619
} else {
598
620
(
599
621
None ,
0 commit comments