handlebars/
grammar.rs

1// const _GRAMMAR: &'static str = include_str!("grammar.pest");
2
3#[derive(Parser)]
4#[grammar = "grammar.pest"]
5pub struct HandlebarsParser;
6
7#[cfg(test)]
8mod test {
9    use super::{HandlebarsParser, Rule};
10    use pest::Parser;
11
12    macro_rules! assert_rule {
13        ($rule:expr, $in:expr) => {
14            assert_eq!(
15                HandlebarsParser::parse($rule, $in)
16                    .unwrap()
17                    .last()
18                    .unwrap()
19                    .as_span()
20                    .end(),
21                $in.len()
22            );
23        };
24    }
25
26    macro_rules! assert_not_rule {
27        ($rule:expr, $in:expr) => {
28            assert!(
29                HandlebarsParser::parse($rule, $in).is_err()
30                    || HandlebarsParser::parse($rule, $in)
31                        .unwrap()
32                        .last()
33                        .unwrap()
34                        .as_span()
35                        .end()
36                        != $in.len()
37            );
38        };
39    }
40
41    macro_rules! assert_rule_match {
42        ($rule:expr, $in:expr) => {
43            assert!(HandlebarsParser::parse($rule, $in).is_ok());
44        };
45    }
46
47    #[test]
48    fn test_raw_text() {
49        let s = [
50            "<h1> helloworld </h1>    ",
51            r"hello\{{world}}",
52            r"hello\{{#if world}}nice\{{/if}}",
53            r"hello \{{{{raw}}}}hello\{{{{/raw}}}}",
54        ];
55        for i in &s {
56            assert_rule!(Rule::raw_text, i);
57        }
58
59        let s_not_escape = [r"\\{{hello}}"];
60        for i in &s_not_escape {
61            assert_not_rule!(Rule::raw_text, i);
62        }
63    }
64
65    #[test]
66    fn test_raw_block_text() {
67        let s = "<h1> {{hello}} </h1>";
68        assert_rule!(Rule::raw_block_text, s);
69    }
70
71    #[test]
72    fn test_reference() {
73        let s = vec![
74            "a",
75            "abc",
76            "../a",
77            "a.b",
78            "@abc",
79            "a.[abc]",
80            "aBc.[abc]",
81            "abc.[0].[nice]",
82            "some-name",
83            "this.[0].ok",
84            "this.[$id]",
85            "[$id]",
86            "$id",
87            "this.[null]",
88        ];
89        for i in &s {
90            assert_rule!(Rule::reference, i);
91        }
92    }
93
94    #[test]
95    fn test_name() {
96        let s = ["if", "(abc)"];
97        for i in &s {
98            assert_rule!(Rule::name, i);
99        }
100    }
101
102    #[test]
103    fn test_param() {
104        let s = ["hello", "\"json literal\"", "nullable", "truestory"];
105        for i in &s {
106            assert_rule!(Rule::helper_parameter, i);
107        }
108    }
109
110    #[test]
111    fn test_hash() {
112        let s = [
113            "hello=world",
114            "hello=\"world\"",
115            "hello=(world)",
116            "hello=(world 0)",
117        ];
118        for i in &s {
119            assert_rule!(Rule::hash, i);
120        }
121    }
122
123    #[test]
124    fn test_json_literal() {
125        let s = [
126            "\"json string\"",
127            "\"quot: \\\"\"",
128            "[]",
129            "[\"hello\"]",
130            "[1,2,3,4,true]",
131            "{\"hello\": \"world\"}",
132            "{}",
133            "{\"a\":1, \"b\":2 }",
134            "\"nullable\"",
135        ];
136        for i in &s {
137            assert_rule!(Rule::literal, i);
138        }
139    }
140
141    #[test]
142    fn test_comment() {
143        let s = ["{{!-- <hello {{ a-b c-d}} {{d-c}} ok --}}",
144                 "{{!--
145                    <li><a href=\"{{up-dir nest-count}}{{base-url}}index.html\">{{this.title}}</a></li>
146                --}}",
147                     "{{!    -- good  --}}"];
148        for i in &s {
149            assert_rule!(Rule::hbs_comment, i);
150        }
151        let s2 = ["{{! hello }}", "{{! test me }}"];
152        for i in &s2 {
153            assert_rule!(Rule::hbs_comment_compact, i);
154        }
155    }
156
157    #[test]
158    fn test_subexpression() {
159        let s = ["(sub)", "(sub 0)", "(sub a=1)"];
160        for i in &s {
161            assert_rule!(Rule::subexpression, i);
162        }
163    }
164
165    #[test]
166    fn test_expression() {
167        let s = vec![
168            "{{exp}}",
169            "{{(exp)}}",
170            "{{../exp}}",
171            "{{exp 1}}",
172            "{{exp \"literal\"}}",
173            "{{exp \"literal with space\"}}",
174            "{{exp 'literal with space'}}",
175            r#"{{exp "literal with escape \\\\"}}"#,
176            "{{exp ref}}",
177            "{{exp (sub)}}",
178            "{{exp (sub 123)}}",
179            "{{exp []}}",
180            "{{exp {}}}",
181            "{{exp key=1}}",
182            "{{exp key=ref}}",
183            "{{exp key='literal with space'}}",
184            "{{exp key=\"literal with space\"}}",
185            "{{exp key=(sub)}}",
186            "{{exp key=(sub 0)}}",
187            "{{exp key=(sub 0 key=1)}}",
188            "{{exp ns:key=0}}",
189        ];
190        for i in &s {
191            assert_rule!(Rule::expression, i);
192        }
193    }
194
195    #[test]
196    fn test_identifier_with_dash() {
197        let s = ["{{exp-foo}}"];
198        for i in &s {
199            assert_rule!(Rule::expression, i);
200        }
201    }
202
203    #[test]
204    fn test_html_expression() {
205        let s = [
206            "{{{html}}}",
207            "{{{(html)}}}",
208            "{{{(html)}}}",
209            "{{&html}}",
210            "{{{html 1}}}",
211            "{{{html p=true}}}",
212            "{{{~ html}}}",
213            "{{{html ~}}}",
214            "{{{~ html ~}}}",
215            "{{~{ html }~}}",
216            "{{~{ html }}}",
217            "{{{ html }~}}",
218        ];
219        for i in &s {
220            assert_rule!(Rule::html_expression, i);
221        }
222    }
223
224    #[test]
225    fn test_helper_start() {
226        let s = [
227            "{{#if hello}}",
228            "{{#if (hello)}}",
229            "{{#if hello=world}}",
230            "{{#if hello hello=world}}",
231            "{{#if []}}",
232            "{{#if {}}}",
233            "{{#if}}",
234            "{{~#if hello~}}",
235            "{{#each people as |person|}}",
236            "{{#each-obj obj as |val key|}}",
237            "{{#each assets}}",
238        ];
239        for i in &s {
240            assert_rule!(Rule::helper_block_start, i);
241        }
242    }
243
244    #[test]
245    fn test_helper_end() {
246        let s = ["{{/if}}", "{{~/if}}", "{{~/if ~}}", "{{/if   ~}}"];
247        for i in &s {
248            assert_rule!(Rule::helper_block_end, i);
249        }
250    }
251
252    #[test]
253    fn test_helper_block() {
254        let s = [
255            "{{#if hello}}hello{{/if}}",
256            "{{#if true}}hello{{/if}}",
257            "{{#if nice ok=1}}hello{{/if}}",
258            "{{#if}}hello{{else}}world{{/if}}",
259            "{{#if}}hello{{^}}world{{/if}}",
260            "{{#if}}{{#if}}hello{{/if}}{{/if}}",
261            "{{#if}}hello{{~else}}world{{/if}}",
262            "{{#if}}hello{{else~}}world{{/if}}",
263            "{{#if}}hello{{~^~}}world{{/if}}",
264            "{{#if}}{{/if}}",
265            "{{#if}}hello{{else if}}world{{else}}test{{/if}}",
266        ];
267        for i in &s {
268            assert_rule!(Rule::helper_block, i);
269        }
270    }
271
272    #[test]
273    fn test_raw_block() {
274        let s = [
275            "{{{{if hello}}}}good {{hello}}{{{{/if}}}}",
276            "{{{{if hello}}}}{{#if nice}}{{/if}}{{{{/if}}}}",
277        ];
278        for i in &s {
279            assert_rule!(Rule::raw_block, i);
280        }
281    }
282
283    #[test]
284    fn test_block_param() {
285        let s = ["as |person|", "as |val key|"];
286        for i in &s {
287            assert_rule!(Rule::block_param, i);
288        }
289    }
290
291    #[test]
292    fn test_path() {
293        let s = vec![
294            "a",
295            "a.b.c.d",
296            "a.[0].[1].[2]",
297            "a.[abc]",
298            "a/v/c.d.s",
299            "a.[0]/b/c/d",
300            "a.[bb c]/b/c/d",
301            "a.[0].[#hello]",
302            "../a/b.[0].[1]",
303            "this.[0]/[1]/this/a",
304            "./this_name",
305            "./goo/[/bar]",
306            "a.[你好]",
307            "a.[10].[#comment]",
308            "a.[]", // empty key
309            "./[/foo]",
310            "[foo]",
311            "@root/a/b",
312            "nullable",
313        ];
314        for i in &s {
315            assert_rule_match!(Rule::path, i);
316        }
317    }
318
319    #[test]
320    fn test_decorator_expression() {
321        let s = ["{{* ssh}}", "{{~* ssh}}"];
322        for i in &s {
323            assert_rule!(Rule::decorator_expression, i);
324        }
325    }
326
327    #[test]
328    fn test_decorator_block() {
329        let s = [
330            "{{#* inline}}something{{/inline}}",
331            "{{~#* inline}}hello{{/inline}}",
332            "{{#* inline \"partialname\"}}something{{/inline}}",
333        ];
334        for i in &s {
335            assert_rule!(Rule::decorator_block, i);
336        }
337    }
338
339    #[test]
340    fn test_partial_expression() {
341        let s = [
342            "{{> hello}}",
343            "{{> (hello)}}",
344            "{{~> hello a}}",
345            "{{> hello a=1}}",
346            "{{> (hello) a=1}}",
347            "{{> hello.world}}",
348            "{{> [a83?f4+.3]}}",
349            "{{> 'anif?.bar'}}",
350        ];
351        for i in &s {
352            assert_rule!(Rule::partial_expression, i);
353        }
354    }
355
356    #[test]
357    fn test_partial_block() {
358        let s = [
359            "{{#> hello}}nice{{/hello}}",
360            "{{#> hello}}nice{{/}}",
361            "{{#> (hello)}}nice{{/}}",
362        ];
363        for i in &s {
364            assert_rule!(Rule::partial_block, i);
365        }
366    }
367}