1
1
use error:: { Error , Result } ;
2
2
3
+ use value:: Value ;
3
4
use interpreter:: Argument ;
4
5
use interpreter:: Context ;
5
6
use interpreter:: Renderable ;
@@ -25,6 +26,29 @@ struct Conditional {
25
26
if_false : Option < Template > ,
26
27
}
27
28
29
+ fn contains_check ( a : & Value , b : & Value ) -> Result < bool > {
30
+ let b = b. as_str ( )
31
+ . ok_or ( "Right-hand side of contains operator must be a string" ) ?;
32
+
33
+ match * a {
34
+ Value :: Str ( ref val) => Ok ( val. contains ( b) ) ,
35
+ Value :: Object ( ref obj) => Ok ( obj. contains_key ( b) ) ,
36
+ Value :: Array ( ref arr) => {
37
+ for elem in arr {
38
+ let elem = elem. as_str ( )
39
+ . ok_or ( "The contains operator can only check for strings" ) ?;
40
+ if elem == b {
41
+ return Ok ( true ) ;
42
+ }
43
+ }
44
+ Ok ( false )
45
+ }
46
+ _ => {
47
+ Error :: renderer ( "Left-hand side of contains operator must be a string, array or object" )
48
+ }
49
+ }
50
+ }
51
+
28
52
impl Conditional {
29
53
fn compare ( & self , context : & Context ) -> Result < bool > {
30
54
let a = self . condition . lh . evaluate ( context) ?;
@@ -37,7 +61,7 @@ impl Conditional {
37
61
ComparisonOperator :: GreaterThan => a > b,
38
62
ComparisonOperator :: LessThanEquals => a <= b,
39
63
ComparisonOperator :: GreaterThanEquals => a >= b,
40
- ComparisonOperator :: Contains => false , // TODO!!!
64
+ ComparisonOperator :: Contains => contains_check ( & a , & b ) ? ,
41
65
} ;
42
66
43
67
Ok ( result == self . mode )
@@ -133,6 +157,7 @@ pub fn if_block(_tag_name: &str,
133
157
134
158
#[ cfg( test) ]
135
159
mod test {
160
+ use std:: collections:: HashMap ;
136
161
use super :: * ;
137
162
use value:: Value ;
138
163
use compiler;
@@ -336,4 +361,141 @@ mod test {
336
361
let output = template. render ( & mut context) . unwrap ( ) ;
337
362
assert_eq ! ( output, Some ( "fourth" . to_owned( ) ) ) ;
338
363
}
364
+
365
+ #[ test]
366
+ fn string_contains_with_literals ( ) {
367
+ let text = "{% if \" Star Wars\" contains \" Star\" %}if true{% endif %}" ;
368
+ let tokens = compiler:: tokenize ( & text) . unwrap ( ) ;
369
+ let template = compiler:: parse ( & tokens, & options ( ) )
370
+ . map ( interpreter:: Template :: new)
371
+ . unwrap ( ) ;
372
+
373
+ let mut context = Context :: new ( ) ;
374
+ let output = template. render ( & mut context) . unwrap ( ) ;
375
+ assert_eq ! ( output, Some ( "if true" . to_owned( ) ) ) ;
376
+
377
+ let text = "{% if \" Star Wars\" contains \" Alf\" %}if true{% else %}if false{% endif %}" ;
378
+ let tokens = compiler:: tokenize ( & text) . unwrap ( ) ;
379
+ let template = compiler:: parse ( & tokens, & options ( ) )
380
+ . map ( interpreter:: Template :: new)
381
+ . unwrap ( ) ;
382
+
383
+ let mut context = Context :: new ( ) ;
384
+ let output = template. render ( & mut context) . unwrap ( ) ;
385
+ assert_eq ! ( output, Some ( "if false" . to_owned( ) ) ) ;
386
+ }
387
+
388
+ #[ test]
389
+ fn string_contains_with_variables ( ) {
390
+ let text = "{% if movie contains \" Star\" %}if true{% endif %}" ;
391
+ let tokens = compiler:: tokenize ( & text) . unwrap ( ) ;
392
+ let template = compiler:: parse ( & tokens, & options ( ) )
393
+ . map ( interpreter:: Template :: new)
394
+ . unwrap ( ) ;
395
+
396
+ let mut context = Context :: new ( ) ;
397
+ context. set_global_val ( "movie" , Value :: Str ( "Star Wars" . into ( ) ) ) ;
398
+ let output = template. render ( & mut context) . unwrap ( ) ;
399
+ assert_eq ! ( output, Some ( "if true" . to_owned( ) ) ) ;
400
+
401
+ let text = "{% if movie contains \" Star\" %}if true{% else %}if false{% endif %}" ;
402
+ let tokens = compiler:: tokenize ( & text) . unwrap ( ) ;
403
+ let template = compiler:: parse ( & tokens, & options ( ) )
404
+ . map ( interpreter:: Template :: new)
405
+ . unwrap ( ) ;
406
+
407
+ let mut context = Context :: new ( ) ;
408
+ context. set_global_val ( "movie" , Value :: Str ( "Batman" . into ( ) ) ) ;
409
+ let output = template. render ( & mut context) . unwrap ( ) ;
410
+ assert_eq ! ( output, Some ( "if false" . to_owned( ) ) ) ;
411
+ }
412
+
413
+ #[ test]
414
+ fn contains_numeric_lhs ( ) {
415
+ let text = "{% if 7 contains \" Star\" %}if true{% endif %}" ;
416
+ let tokens = compiler:: tokenize ( & text) . unwrap ( ) ;
417
+ let template = compiler:: parse ( & tokens, & options ( ) )
418
+ . map ( interpreter:: Template :: new)
419
+ . unwrap ( ) ;
420
+
421
+ let mut context = Context :: new ( ) ;
422
+ let output = template. render ( & mut context) ;
423
+ assert ! ( output. is_err( ) ) ;
424
+ }
425
+
426
+ #[ test]
427
+ fn contains_non_string_rhs ( ) {
428
+ let text = "{% if \" Star Wars\" contains 7 %}if true{% endif %}" ;
429
+ let tokens = compiler:: tokenize ( & text) . unwrap ( ) ;
430
+ let template = compiler:: parse ( & tokens, & options ( ) )
431
+ . map ( interpreter:: Template :: new)
432
+ . unwrap ( ) ;
433
+
434
+ let mut context = Context :: new ( ) ;
435
+ let output = template. render ( & mut context) ;
436
+ assert ! ( output. is_err( ) ) ;
437
+ }
438
+
439
+ #[ test]
440
+ fn contains_with_object_and_key ( ) {
441
+ let text = "{% if movies contains \" Star Wars\" %}if true{% endif %}" ;
442
+ let tokens = compiler:: tokenize ( & text) . unwrap ( ) ;
443
+ let template = compiler:: parse ( & tokens, & options ( ) )
444
+ . map ( interpreter:: Template :: new)
445
+ . unwrap ( ) ;
446
+
447
+ let mut context = Context :: new ( ) ;
448
+ let mut obj = HashMap :: new ( ) ;
449
+ obj. insert ( "Star Wars" . to_owned ( ) , Value :: str ( "1977" ) ) ;
450
+ context. set_global_val ( "movies" , Value :: Object ( obj) ) ;
451
+ let output = template. render ( & mut context) . unwrap ( ) ;
452
+ assert_eq ! ( output, Some ( "if true" . to_owned( ) ) ) ;
453
+ }
454
+
455
+ #[ test]
456
+ fn contains_with_object_and_missing_key ( ) {
457
+ let text = "{% if movies contains \" Star Wars\" %}if true{% else %}if false{% endif %}" ;
458
+ let tokens = compiler:: tokenize ( & text) . unwrap ( ) ;
459
+ let template = compiler:: parse ( & tokens, & options ( ) )
460
+ . map ( interpreter:: Template :: new)
461
+ . unwrap ( ) ;
462
+
463
+ let mut context = Context :: new ( ) ;
464
+ let obj = HashMap :: new ( ) ;
465
+ context. set_global_val ( "movies" , Value :: Object ( obj) ) ;
466
+ let output = template. render ( & mut context) . unwrap ( ) ;
467
+ assert_eq ! ( output, Some ( "if false" . to_owned( ) ) ) ;
468
+ }
469
+
470
+ #[ test]
471
+ fn contains_with_array_and_match ( ) {
472
+ let text = "{% if movies contains \" Star Wars\" %}if true{% endif %}" ;
473
+ let tokens = compiler:: tokenize ( & text) . unwrap ( ) ;
474
+ let template = compiler:: parse ( & tokens, & options ( ) )
475
+ . map ( interpreter:: Template :: new)
476
+ . unwrap ( ) ;
477
+
478
+ let mut context = Context :: new ( ) ;
479
+ let arr = vec ! [ Value :: str ( "Star Wars" ) ,
480
+ Value :: str ( "Star Trek" ) ,
481
+ Value :: str ( "Alien" ) ] ;
482
+ context. set_global_val ( "movies" , Value :: Array ( arr) ) ;
483
+ let output = template. render ( & mut context) . unwrap ( ) ;
484
+ assert_eq ! ( output, Some ( "if true" . to_owned( ) ) ) ;
485
+ }
486
+
487
+ #[ test]
488
+ fn contains_with_array_and_no_match ( ) {
489
+ let text = "{% if movies contains \" Star Wars\" %}if true{% else %}if false{% endif %}" ;
490
+ let tokens = compiler:: tokenize ( & text) . unwrap ( ) ;
491
+ let template = compiler:: parse ( & tokens, & options ( ) )
492
+ . map ( interpreter:: Template :: new)
493
+ . unwrap ( ) ;
494
+
495
+ let mut context = Context :: new ( ) ;
496
+ let arr = vec ! [ Value :: str ( "Alien" ) ] ;
497
+ context. set_global_val ( "movies" , Value :: Array ( arr) ) ;
498
+ let output = template. render ( & mut context) . unwrap ( ) ;
499
+ assert_eq ! ( output, Some ( "if false" . to_owned( ) ) ) ;
500
+ }
339
501
}
0 commit comments