@@ -16,6 +16,7 @@ use rust_decimal::prelude::FromPrimitive;
16
16
use rust_decimal:: Decimal ;
17
17
use std:: borrow:: { Borrow , Cow } ;
18
18
use std:: fmt:: Debug ;
19
+ use std:: iter:: Peekable ;
19
20
20
21
pub ( crate ) mod pattern_match;
21
22
@@ -107,54 +108,126 @@ pub(crate) enum EvalPathComponent {
107
108
KeyExpr ( Box < dyn EvalExpr > ) ,
108
109
Index ( i64 ) ,
109
110
IndexExpr ( Box < dyn EvalExpr > ) ,
111
+ PathWildcard ,
112
+ PathUnpivot ,
110
113
}
111
114
112
115
impl EvalExpr for EvalPath {
113
116
fn evaluate < ' a > ( & ' a self , bindings : & ' a Tuple , ctx : & ' a dyn EvalContext ) -> Cow < ' a , Value > {
114
117
#[ inline]
115
- fn path_into < ' a > (
116
- value : & ' a Value ,
117
- path : & EvalPathComponent ,
118
+ fn path < ' a , I > (
119
+ v : Value ,
120
+ mut paths : Peekable < I > ,
118
121
bindings : & ' a Tuple ,
119
122
ctx : & dyn EvalContext ,
120
- ) -> Option < & ' a Value > {
121
- match path {
122
- EvalPathComponent :: Key ( k) => match value {
123
- Value :: Tuple ( tuple) => tuple. get ( k) ,
124
- _ => None ,
125
- } ,
126
- EvalPathComponent :: Index ( idx) => match value {
127
- Value :: List ( list) if ( * idx as usize ) < list. len ( ) => list. get ( * idx) ,
128
- _ => None ,
129
- } ,
130
- EvalPathComponent :: KeyExpr ( ke) => {
131
- let key = ke. evaluate ( bindings, ctx) ;
132
- match ( value, key. as_ref ( ) ) {
133
- ( Value :: Tuple ( tuple) , Value :: String ( key) ) => {
134
- tuple. get ( & BindingsName :: CaseInsensitive ( key. as_ref ( ) . clone ( ) ) )
123
+ ) -> Value
124
+ where
125
+ I : Iterator < Item = & ' a EvalPathComponent > ,
126
+ I : Clone ,
127
+ {
128
+ let mut value = v;
129
+ while let Some ( p) = paths. next ( ) {
130
+ match p {
131
+ EvalPathComponent :: Key ( k) => {
132
+ value = match value {
133
+ Value :: Tuple ( tuple) => tuple. get ( k) . unwrap_or_else ( || & Missing ) . clone ( ) ,
134
+ _ => Missing ,
135
135
}
136
- _ => None ,
137
136
}
138
- }
139
- EvalPathComponent :: IndexExpr ( ie) => {
140
- if let Value :: Integer ( idx) = ie. evaluate ( bindings, ctx) . as_ref ( ) {
141
- match value {
142
- Value :: List ( list) if ( * idx as usize ) < list. len ( ) => list. get ( * idx) ,
143
- _ => None ,
137
+ EvalPathComponent :: Index ( idx) => {
138
+ value = match & value {
139
+ Value :: List ( list) if ( * idx as usize ) < list. len ( ) => {
140
+ list. get ( * idx) . unwrap_or_else ( || & Missing ) . clone ( )
141
+ }
142
+ _ => Missing ,
143
+ }
144
+ }
145
+ EvalPathComponent :: KeyExpr ( ke) => {
146
+ let key = ke. evaluate ( bindings, ctx) ;
147
+ value = match ( value, key. as_ref ( ) ) {
148
+ ( Value :: Tuple ( tuple) , Value :: String ( key) ) => tuple
149
+ . get ( & BindingsName :: CaseInsensitive ( key. as_ref ( ) . clone ( ) ) )
150
+ . unwrap_or_else ( || & Missing )
151
+ . clone ( ) ,
152
+ _ => Missing ,
153
+ }
154
+ }
155
+ EvalPathComponent :: IndexExpr ( ie) => {
156
+ value = if let Value :: Integer ( idx) = ie. evaluate ( bindings, ctx) . as_ref ( ) {
157
+ match & value {
158
+ Value :: List ( list) if ( * idx as usize ) < list. len ( ) => {
159
+ list. get ( * idx) . unwrap_or_else ( || & Missing ) . clone ( )
160
+ }
161
+ _ => Missing ,
162
+ }
163
+ } else {
164
+ Missing
144
165
}
145
- } else {
146
- None
166
+ }
167
+ EvalPathComponent :: PathWildcard => {
168
+ return match paths. peek ( ) . is_some ( ) {
169
+ true => {
170
+ // iterator is not empty
171
+ let other_wildcards_present = paths
172
+ . clone ( )
173
+ . any ( |_p| matches ! ( EvalPathComponent :: PathWildcard , _p) ) ;
174
+ if other_wildcards_present {
175
+ // other path wildcards so flatten
176
+ let values = value
177
+ . into_iter ( )
178
+ . flat_map ( |v| path ( v, paths. clone ( ) , bindings, ctx) )
179
+ . collect :: < Vec < Value > > ( ) ;
180
+ Value :: from ( Bag :: from ( values) )
181
+ } else {
182
+ // no other path wildcards
183
+ let values = value
184
+ . into_iter ( )
185
+ . map ( |v| path ( v, paths. clone ( ) , bindings, ctx) )
186
+ . collect :: < Vec < Value > > ( ) ;
187
+ Value :: from ( Bag :: from ( values) )
188
+ }
189
+ }
190
+ false => {
191
+ // iterator is empty; path wildcard is last component
192
+ Value :: from ( Bag :: from_iter ( value. into_iter ( ) ) )
193
+ }
194
+ } ;
195
+ }
196
+ EvalPathComponent :: PathUnpivot => {
197
+ return match paths. peek ( ) . is_some ( ) {
198
+ true => {
199
+ // iterator is not empty
200
+ let values = value
201
+ . coerce_to_tuple ( )
202
+ . into_values ( )
203
+ . flat_map ( |v| path ( v, paths. clone ( ) , bindings, ctx) )
204
+ . collect :: < Vec < Value > > ( ) ;
205
+ Value :: from ( Bag :: from ( values) )
206
+ }
207
+ false =>
208
+ // iterator is empty; path unpivot is last component
209
+ {
210
+ match value {
211
+ Value :: Tuple ( tuple) => {
212
+ let values = tuple. into_values ( ) . collect :: < Vec < Value > > ( ) ;
213
+ Value :: from ( Bag :: from ( values) )
214
+ }
215
+ non_tuple => Value :: from ( Value :: coerce_to_bag ( non_tuple) ) ,
216
+ }
217
+ }
218
+ } ;
147
219
}
148
220
}
149
221
}
222
+ value
150
223
}
151
- let value = self . expr . evaluate ( bindings, ctx) ;
152
- self . components
153
- . iter ( )
154
- . fold ( Some ( value . as_ref ( ) ) , |v , path| {
155
- v . and_then ( |v| path_into ( v , path , bindings, ctx ) )
156
- } )
157
- . map_or_else ( || Cow :: Owned ( Value :: Missing ) , |v| Cow :: Owned ( v . clone ( ) ) )
224
+ let value = self . expr . evaluate ( bindings, ctx) . into_owned ( ) ;
225
+ Cow :: Owned ( path (
226
+ value ,
227
+ self . components . iter ( ) . peekable ( ) ,
228
+ bindings,
229
+ ctx ,
230
+ ) )
158
231
}
159
232
}
160
233
0 commit comments