@@ -227,18 +227,30 @@ fn get_key(data: &Value, key: KeyType) -> Option<Value> {
227227 }
228228}
229229
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
242254}
243255
244256fn 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> {
249261 match data {
250262 Value :: Object ( _) | Value :: Array ( _) | Value :: String ( _) => {
251263 // 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 > ( )
266273 . 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+ } )
275289 }
276290 _ => None ,
277291 }
278292}
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