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 = vec![
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.iter() {
56 assert_rule!(Rule::raw_text, i);
57 }
58
59 let s_not_escape = vec![r"\\{{hello}}"];
60 for i in s_not_escape.iter() {
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.iter() {
90 assert_rule!(Rule::reference, i);
91 }
92 }
93
94 #[test]
95 fn test_name() {
96 let s = vec!["if", "(abc)"];
97 for i in s.iter() {
98 assert_rule!(Rule::name, i);
99 }
100 }
101
102 #[test]
103 fn test_param() {
104 let s = vec!["hello", "\"json literal\"", "nullable", "truestory"];
105 for i in s.iter() {
106 assert_rule!(Rule::param, i);
107 }
108 }
109
110 #[test]
111 fn test_hash() {
112 let s = vec![
113 "hello=world",
114 "hello=\"world\"",
115 "hello=(world)",
116 "hello=(world 0)",
117 ];
118 for i in s.iter() {
119 assert_rule!(Rule::hash, i);
120 }
121 }
122
123 #[test]
124 fn test_json_literal() {
125 let s = vec![
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.iter() {
137 assert_rule!(Rule::literal, i);
138 }
139 }
140
141 #[test]
142 fn test_comment() {
143 let s = vec!["{{!-- <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.iter() {
149 assert_rule!(Rule::hbs_comment, i);
150 }
151 let s2 = vec!["{{! hello }}", "{{! test me }}"];
152 for i in s2.iter() {
153 assert_rule!(Rule::hbs_comment_compact, i);
154 }
155 }
156
157 #[test]
158 fn test_subexpression() {
159 let s = vec!["(sub)", "(sub 0)", "(sub a=1)"];
160 for i in s.iter() {
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 ];
189 for i in s.iter() {
190 assert_rule!(Rule::expression, i);
191 }
192 }
193
194 #[test]
195 fn test_identifier_with_dash() {
196 let s = vec!["{{exp-foo}}"];
197 for i in s.iter() {
198 assert_rule!(Rule::expression, i);
199 }
200 }
201
202 #[test]
203 fn test_html_expression() {
204 let s = vec![
205 "{{{html}}}",
206 "{{{(html)}}}",
207 "{{{(html)}}}",
208 "{{&html}}",
209 "{{{html 1}}}",
210 "{{{html p=true}}}",
211 "{{{~ html}}}",
212 "{{{html ~}}}",
213 "{{{~ html ~}}}",
214 "{{~{ html }~}}",
215 "{{~{ html }}}",
216 "{{{ html }~}}",
217 ];
218 for i in s.iter() {
219 assert_rule!(Rule::html_expression, i);
220 }
221 }
222
223 #[test]
224 fn test_helper_start() {
225 let s = vec![
226 "{{#if hello}}",
227 "{{#if (hello)}}",
228 "{{#if hello=world}}",
229 "{{#if hello hello=world}}",
230 "{{#if []}}",
231 "{{#if {}}}",
232 "{{#if}}",
233 "{{~#if hello~}}",
234 "{{#each people as |person|}}",
235 "{{#each-obj obj as |val key|}}",
236 "{{#each assets}}",
237 ];
238 for i in s.iter() {
239 assert_rule!(Rule::helper_block_start, i);
240 }
241 }
242
243 #[test]
244 fn test_helper_end() {
245 let s = vec!["{{/if}}", "{{~/if}}", "{{~/if ~}}", "{{/if ~}}"];
246 for i in s.iter() {
247 assert_rule!(Rule::helper_block_end, i);
248 }
249 }
250
251 #[test]
252 fn test_helper_block() {
253 let s = vec![
254 "{{#if hello}}hello{{/if}}",
255 "{{#if true}}hello{{/if}}",
256 "{{#if nice ok=1}}hello{{/if}}",
257 "{{#if}}hello{{else}}world{{/if}}",
258 "{{#if}}hello{{^}}world{{/if}}",
259 "{{#if}}{{#if}}hello{{/if}}{{/if}}",
260 "{{#if}}hello{{~else}}world{{/if}}",
261 "{{#if}}hello{{else~}}world{{/if}}",
262 "{{#if}}hello{{~^~}}world{{/if}}",
263 "{{#if}}{{/if}}",
264 ];
265 for i in s.iter() {
266 assert_rule!(Rule::helper_block, i);
267 }
268 }
269
270 #[test]
271 fn test_raw_block() {
272 let s = vec![
273 "{{{{if hello}}}}good {{hello}}{{{{/if}}}}",
274 "{{{{if hello}}}}{{#if nice}}{{/if}}{{{{/if}}}}",
275 ];
276 for i in s.iter() {
277 assert_rule!(Rule::raw_block, i);
278 }
279 }
280
281 #[test]
282 fn test_block_param() {
283 let s = vec!["as |person|", "as |val key|"];
284 for i in s.iter() {
285 assert_rule!(Rule::block_param, i);
286 }
287 }
288
289 #[test]
290 fn test_path() {
291 let s = vec![
292 "a",
293 "a.b.c.d",
294 "a.[0].[1].[2]",
295 "a.[abc]",
296 "a/v/c.d.s",
297 "a.[0]/b/c/d",
298 "a.[bb c]/b/c/d",
299 "a.[0].[#hello]",
300 "../a/b.[0].[1]",
301 "this.[0]/[1]/this/a",
302 "./this_name",
303 "./goo/[/bar]",
304 "a.[你好]",
305 "a.[10].[#comment]",
306 "a.[]", // empty key
307 "./[/foo]",
308 "[foo]",
309 "@root/a/b",
310 "nullable",
311 ];
312 for i in s.iter() {
313 assert_rule_match!(Rule::path, i);
314 }
315 }
316
317 #[test]
318 fn test_decorator_expression() {
319 let s = vec!["{{* ssh}}", "{{~* ssh}}"];
320 for i in s.iter() {
321 assert_rule!(Rule::decorator_expression, i);
322 }
323 }
324
325 #[test]
326 fn test_decorator_block() {
327 let s = vec![
328 "{{#* inline}}something{{/inline}}",
329 "{{~#* inline}}hello{{/inline}}",
330 "{{#* inline \"partialname\"}}something{{/inline}}",
331 ];
332 for i in s.iter() {
333 assert_rule!(Rule::decorator_block, i);
334 }
335 }
336
337 #[test]
338 fn test_partial_expression() {
339 let s = vec![
340 "{{> hello}}",
341 "{{> (hello)}}",
342 "{{~> hello a}}",
343 "{{> hello a=1}}",
344 "{{> (hello) a=1}}",
345 "{{> hello.world}}",
346 "{{> [a83?f4+.3]}}",
347 "{{> 'anif?.bar'}}",
348 ];
349 for i in s.iter() {
350 assert_rule!(Rule::partial_expression, i);
351 }
352 }
353
354 #[test]
355 fn test_partial_block() {
356 let s = vec!["{{#> hello}}nice{{/hello}}"];
357 for i in s.iter() {
358 assert_rule!(Rule::partial_block, i);
359 }
360 }
361}
362