ADD Ability to test if key exists
4 files changed, 69 insertions(+), 0 deletions(-)

M README.org
M src/snabela/snabela.ml
M src/snabela/snabela_lexer.ml
M tests/snabela/test.ml
M README.org +3 -0
@@ 118,6 118,9 @@ My name is Arthur Digby Sellers and I ha
 Like Mustache, Snabela has sections.  The identifier to enter a section depends
 on the type of section however each section is exited with ~@/~.
 
+*** Testing for existence of a key
+Testing for the existence of a key is done with ~@^~ and ~@^!~ if the key does
+not exist.
 *** Boolean testing
 A boolean value in the template can be tested for ~true~ with ~@?~ and ~false~
 with ~@!~.

          
M src/snabela/snabela.ml +13 -0
@@ 225,6 225,10 @@ let rec eval_template buf t kv template 
     | At ln::List::Neg_test::Key k::At _::ts ->
       (* @#!key@ ... @/key@ *)
       eval_list_test_section ln buf t kv ts section k `Empty
+    | At ln::Exists::Test::Key k::At _::ts ->
+      eval_exists_test_section ln buf t kv ts section k `Exists
+    | At ln::Exists::Neg_test::Key k::At _::ts ->
+      eval_exists_test_section ln buf t kv ts section k `Not_exists
     | At _::End_section::Key k::At _::ts when k = section ->
       (* @/key@ *)
       ts

          
@@ 279,6 283,15 @@ and eval_list_test_section ln buf t kv t
       raise (Apply_error (`Expected_list (key, ln)))
     | None ->
       raise (Apply_error (`Missing_key (key, ln)))
+and eval_exists_test_section ln buf t kv ts section key exists =
+  match (Kv.Map.get key kv, exists) with
+    | (Some _, `Exists)
+    | (None, `Not_exists) ->
+      let ts = eval_template buf t kv ts key in
+      eval_template buf t kv ts section
+    | _ ->
+      let ts = skip_section key ts in
+      eval_template buf t kv ts section
 
 let apply t kv =
   let buf = Buffer.create 100 in

          
M src/snabela/snabela_lexer.ml +11 -0
@@ 12,6 12,7 @@ module Token = struct
     | String of string
     | End_section
     | Comment
+    | Exists
   [@@deriving show,eq]
 
   type t = token list [@@deriving show,eq]

          
@@ 54,12 55,22 @@ let rec token ln bldr buf =
       comment ln (Tb.add_l [At ln; Comment] bldr) buf
     | "@-#?" ->
       replacement ln (Tb.add_l [At ln; Left_trim; List; Test] bldr) buf
+    | "@-#!" ->
+      replacement ln (Tb.add_l [At ln; Left_trim; List; Neg_test] bldr) buf
     | "@-#" ->
       replacement ln (Tb.add_l [At ln; Left_trim; List] bldr) buf
+    | "@-^!" ->
+      replacement ln (Tb.add_l [At ln; Left_trim; Exists; Neg_test] bldr) buf
+    | "@-^" ->
+      replacement ln (Tb.add_l [At ln; Left_trim; Exists; Test] bldr) buf
     | "@#?" ->
       replacement ln (Tb.add_l [At ln; List; Test] bldr) buf
     | "@#!" ->
       replacement ln (Tb.add_l [At ln; List; Neg_test] bldr) buf
+    | "@^!" ->
+      replacement ln (Tb.add_l [At ln; Exists; Neg_test] bldr) buf
+    | "@^" ->
+      replacement ln (Tb.add_l [At ln; Exists; Test] bldr) buf
     | "@#" ->
       replacement ln (Tb.add_l [At ln; List] bldr) buf
     | "@-?" ->

          
M tests/snabela/test.ml +42 -0
@@ 119,6 119,23 @@ let test_tokenizer8 =
              ; At 12; End_section; Key "parties"; Right_trim; At 12
              ]))
 
+let test_tokenizer9 =
+  Oth.test
+    ~name:"Tokenizer: Key test"
+    (fun _ ->
+       let template = "Hello, @^name@@name@@/name@" in
+       let lexbuf = Sedlexing.Utf8.from_string template in
+       let tokens = CCResult.get_exn (Snabela_lexer.tokenize lexbuf) in
+       assert
+         Snabela_lexer.Token.(
+           equal
+             tokens
+             [ String "Hello, "
+             ; At 1; Exists; Test; Key "name"; At 1
+             ; At 1; Key "name"; At 1
+             ; At 1; End_section; Key "name"; At 1
+             ]))
+
 let test_apply1 =
   Oth.test
     ~name:"Apply: Simple"

          
@@ 319,6 336,28 @@ let test_apply12 =
        let applied = CCResult.get_exn (Snabela.apply compile kv) in
        assert ("Hello, foo" = applied))
 
+let test_apply13 =
+  Oth.test
+    ~name:"Apply: Key test"
+    (fun _ ->
+       let template = "Hello, @^name@@name@@/name@" in
+       let kv = Snabela.Kv.(Map.of_list [("name", string "foo")]) in
+       let t = CCResult.get_exn (Snabela.Template.of_utf8_string template) in
+       let compile = Snabela.of_template t [] in
+       let applied = CCResult.get_exn (Snabela.apply compile kv) in
+       assert ("Hello, foo" = applied))
+
+let test_apply14 =
+  Oth.test
+    ~name:"Apply: Neg key test"
+    (fun _ ->
+       let template = "Hello, @^!name@bar@/name@" in
+       let kv = Snabela.Kv.(Map.of_list []) in
+       let t = CCResult.get_exn (Snabela.Template.of_utf8_string template) in
+       let compile = Snabela.of_template t [] in
+       let applied = CCResult.get_exn (Snabela.apply compile kv) in
+       assert ("Hello, bar" = applied))
+
 let test_apply_fail1 =
   Oth.test
     ~name:"Apply Fail: Missing key"

          
@@ 483,6 522,7 @@ let test =
     ; test_tokenizer6
     ; test_tokenizer7
     ; test_tokenizer8
+    ; test_tokenizer9
     ; test_apply1
     ; test_apply2
     ; test_apply3

          
@@ 495,6 535,8 @@ let test =
     ; test_apply10
     ; test_apply11
     ; test_apply12
+    ; test_apply13
+    ; test_apply14
     ; test_apply_fail1
     ; test_apply_fail2
     ; test_apply_fail3