@@ -227,18 +227,30 @@ fn get_key(data: &Value, key: KeyType) -> Option<Value> {
227
227
}
228
228
}
229
229
230
- fn split_path ( path : & str ) -> impl Iterator < Item = String > + ' _ {
231
- let mut index = 0 ;
232
- return path
233
- . split ( move |c : char | {
234
- if c == '.' && path. chars ( ) . nth ( index - 1 ) . unwrap ( ) != '\\' {
235
- index += 1 ;
236
- return true ;
237
- }
238
- index += 1 ;
239
- return false ;
240
- } )
241
- . map ( |part| part. replace ( "\\ ." , "." ) ) ;
230
+ pub fn split_with_escape ( input : & str , delimiter : char ) -> Vec < String > {
231
+ let mut result = Vec :: new ( ) ;
232
+ let mut slice = String :: new ( ) ;
233
+ let mut escape = false ;
234
+
235
+ for c in input. chars ( ) {
236
+ if escape {
237
+ slice. push ( c) ;
238
+ escape = false ;
239
+ } else if c == '\\' {
240
+ escape = true ;
241
+ } else if c == delimiter {
242
+ result. push ( slice. clone ( ) ) ;
243
+ slice. clear ( ) ;
244
+ } else {
245
+ slice. push ( c) ;
246
+ }
247
+ }
248
+
249
+ if !slice. is_empty ( ) {
250
+ result. push ( slice) ;
251
+ }
252
+
253
+ result
242
254
}
243
255
244
256
fn get_str_key < K : AsRef < str > > ( data : & Value , key : K ) -> Option < Value > {
@@ -249,30 +261,63 @@ fn get_str_key<K: AsRef<str>>(data: &Value, key: K) -> Option<Value> {
249
261
match data {
250
262
Value :: Object ( _) | Value :: Array ( _) | Value :: String ( _) => {
251
263
// Exterior ref in case we need to make a new value in the match.
252
- split_path ( k) . fold ( Some ( data. clone ( ) ) , |acc, i| match acc? {
253
- // If the current value is an object, try to get the value
254
- Value :: Object ( map) => map. get ( & i) . map ( Value :: clone) ,
255
- // If the current value is an array, we need an integer
256
- // index. If integer conversion fails, return None.
257
- Value :: Array ( arr) => i
258
- . parse :: < i64 > ( )
259
- . ok ( )
260
- . and_then ( |i| get ( & arr, i) )
261
- . map ( Value :: clone) ,
262
- // Same deal if it's a string.
263
- Value :: String ( s) => {
264
- let s_chars: Vec < char > = s. chars ( ) . collect ( ) ;
265
- i. parse :: < i64 > ( )
264
+ split_with_escape ( k, '.' )
265
+ . into_iter ( )
266
+ . fold ( Some ( data. clone ( ) ) , |acc, i| match acc? {
267
+ // If the current value is an object, try to get the value
268
+ Value :: Object ( map) => map. get ( & i) . map ( Value :: clone) ,
269
+ // If the current value is an array, we need an integer
270
+ // index. If integer conversion fails, return None.
271
+ Value :: Array ( arr) => i
272
+ . parse :: < i64 > ( )
266
273
. ok ( )
267
- . and_then ( |i| get ( & s_chars, i) )
268
- . map ( |c| c. to_string ( ) )
269
- . map ( Value :: String )
270
- }
271
- // This handles cases where we've got an un-indexable
272
- // type or similar.
273
- _ => None ,
274
- } )
274
+ . and_then ( |i| get ( & arr, i) )
275
+ . map ( Value :: clone) ,
276
+ // Same deal if it's a string.
277
+ Value :: String ( s) => {
278
+ let s_chars: Vec < char > = s. chars ( ) . collect ( ) ;
279
+ i. parse :: < i64 > ( )
280
+ . ok ( )
281
+ . and_then ( |i| get ( & s_chars, i) )
282
+ . map ( |c| c. to_string ( ) )
283
+ . map ( Value :: String )
284
+ }
285
+ // This handles cases where we've got an un-indexable
286
+ // type or similar.
287
+ _ => None ,
288
+ } )
275
289
}
276
290
_ => None ,
277
291
}
278
292
}
293
+
294
+ #[ cfg( test) ]
295
+ mod tests {
296
+ use super :: * ;
297
+
298
+ // All the tests cases have been discussed here: https://github.com/Bestowinc/json-logic-rs/pull/37
299
+ fn cases ( ) -> Vec < ( & ' static str , Vec < & ' static str > ) > {
300
+ vec ! [
301
+ ( "" , vec![ ] ) ,
302
+ ( "foo" , vec![ "foo" ] ) ,
303
+ ( "foo.bar" , vec![ "foo" , "bar" ] ) ,
304
+ ( r#"foo\.bar"# , vec![ "foo.bar" ] ) ,
305
+ ( r#"foo\.bar.biz"# , vec![ "foo.bar" , "biz" ] ) ,
306
+ ( r#"foo\\.bar"# , vec![ "foo\\ " , "bar" ] ) ,
307
+ ( r#"foo\\.bar\.biz"# , vec![ "foo\\ " , "bar.biz" ] ) ,
308
+ ( r#"foo\\\.bar"# , vec![ "foo\\ .bar" ] ) ,
309
+ ( r#"foo\\\.bar.biz"# , vec![ "foo\\ .bar" , "biz" ] ) ,
310
+ ( r#"foo\\bar"# , vec![ "foo\\ bar" ] ) ,
311
+ ( r#"foo\\bar.biz"# , vec![ "foo\\ bar" , "biz" ] ) ,
312
+ ( r#"foo\\bar\.biz"# , vec![ "foo\\ bar.biz" ] ) ,
313
+ ( r#"foo\\bar\\.biz"# , vec![ "foo\\ bar\\ " , "biz" ] ) ,
314
+ ]
315
+ }
316
+
317
+ #[ test]
318
+ fn test_split_with_escape ( ) {
319
+ cases ( )
320
+ . into_iter ( )
321
+ . for_each ( |( input, exp) | assert_eq ! ( split_with_escape( & input, '.' ) , exp) ) ;
322
+ }
323
+ }
0 commit comments