1 | // const _GRAMMAR: &'static str = include_str!("grammar.pest"); |
2 | |
3 | #[derive (Parser)] |
4 | #[grammar = "grammar.pest" ] |
5 | pub struct HandlebarsParser; |
6 | |
7 | #[cfg (test)] |
8 | mod 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 | |