1#[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.[]", "./[/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}