1 //! Parser
2 //!
3 //! This module contains functions than can be used for writing plugins
4 //! but should be ignored for simple usage.
5 
6 use crate::error::{Error, Result, ResultLiquidExt};
7 use crate::model::Value;
8 use crate::runtime::Expression;
9 use crate::runtime::Renderable;
10 use crate::runtime::Variable;
11 
12 use super::Language;
13 use super::Text;
14 use super::{Filter, FilterArguments, FilterChain};
15 
16 use pest::Parser;
17 
18 mod inner {
19     #[derive(Parser)]
20     #[grammar = "parser/grammar.pest"]
21     pub struct LiquidParser;
22 }
23 
24 use self::inner::*;
25 
26 type Pair<'a> = ::pest::iterators::Pair<'a, Rule>;
27 type Pairs<'a> = ::pest::iterators::Pairs<'a, Rule>;
28 
29 /// Converts a `pest::Error` into a `liquid::Error`.
convert_pest_error(err: ::pest::error::Error<Rule>) -> Error30 fn convert_pest_error(err: ::pest::error::Error<Rule>) -> Error {
31     let err = err.renamed_rules(|&rule| match rule {
32         Rule::LesserThan => "\"<\"".to_string(),
33         Rule::GreaterThan => "\">\"".to_string(),
34         Rule::LesserThanEquals => "\"<=\"".to_string(),
35         Rule::GreaterThanEquals => "\">=\"".to_string(),
36         Rule::Equals => "\"==\"".to_string(),
37         Rule::NotEquals => "\"!=\"".to_string(),
38         Rule::LesserThanGreaterThan => "\"<>\"".to_string(),
39         Rule::Assign => "\"=\"".to_string(),
40         Rule::Comma => "\",\"".to_string(),
41         Rule::Colon => "\":\"".to_string(),
42         other => format!("{:?}", other),
43     });
44     Error::with_msg(err.to_string())
45 }
46 
47 /// Generates a `liquid::Error` with the given message pointing to
48 /// the pest
error_from_pair(pair: Pair, msg: String) -> Error49 fn error_from_pair(pair: Pair, msg: String) -> Error {
50     let pest_error = ::pest::error::Error::new_from_span(
51         ::pest::error::ErrorVariant::CustomError { message: msg },
52         pair.as_span(),
53     );
54     convert_pest_error(pest_error)
55 }
56 
57 /// Parses the provided &str into a number of Renderable items.
parse(text: &str, options: &Language) -> Result<Vec<Box<dyn Renderable>>>58 pub fn parse(text: &str, options: &Language) -> Result<Vec<Box<dyn Renderable>>> {
59     let mut liquid = LiquidParser::parse(Rule::LaxLiquidFile, text)
60         .expect("Parsing with Rule::LaxLiquidFile should not raise errors, but InvalidLiquid tokens instead.")
61         .next()
62         .expect("Unwrapping LiquidFile to access the elements.")
63         .into_inner();
64 
65     let mut renderables = Vec::new();
66 
67     while let Some(element) = liquid.next() {
68         if element.as_rule() == Rule::EOI {
69             break;
70         }
71 
72         renderables.push(BlockElement::parse_pair(
73             element.into(),
74             &mut liquid,
75             options,
76         )?);
77     }
78     Ok(renderables)
79 }
80 
81 /// Parses a `Scalar` from a `Pair` with a literal value.
82 /// This `Pair` must be `Rule::Literal`.
parse_literal(literal: Pair) -> Value83 fn parse_literal(literal: Pair) -> Value {
84     if literal.as_rule() != Rule::Literal {
85         panic!("Expected literal.");
86     }
87 
88     let literal = literal
89         .into_inner()
90         .next()
91         .expect("Get into the rule inside literal.");
92 
93     match literal.as_rule() {
94         Rule::NilLiteral => Value::Nil,
95         Rule::EmptyLiteral => Value::State(crate::model::State::Empty),
96         Rule::BlankLiteral => Value::State(crate::model::State::Blank),
97         Rule::StringLiteral => {
98             let literal = literal.as_str();
99             let trim_quotes = &literal[1..literal.len() - 1];
100 
101             Value::scalar(trim_quotes.to_owned())
102         }
103         Rule::IntegerLiteral => Value::scalar(
104             literal
105                 .as_str()
106                 .parse::<i64>()
107                 .expect("Grammar ensures matches are parseable as integers."),
108         ),
109         Rule::FloatLiteral => Value::scalar(
110             literal
111                 .as_str()
112                 .parse::<f64>()
113                 .expect("Grammar ensures matches are parseable as floats."),
114         ),
115         Rule::BooleanLiteral => Value::scalar(
116             literal
117                 .as_str()
118                 .parse::<bool>()
119                 .expect("Grammar ensures matches are parseable as bools."),
120         ),
121         _ => unreachable!(),
122     }
123 }
124 
125 /// Parses a `Variable` from a `Pair` with a variable.
126 /// This `Pair` must be `Rule::Variable`.
parse_variable(variable: Pair) -> Variable127 fn parse_variable(variable: Pair) -> Variable {
128     if variable.as_rule() != Rule::Variable {
129         panic!("Expected variable.");
130     }
131 
132     let mut indexes = variable.into_inner();
133 
134     let first_identifier = indexes
135         .next()
136         .expect("A variable starts with an identifier.")
137         .as_str()
138         .to_owned();
139     let mut variable = Variable::with_literal(first_identifier);
140 
141     let indexes = indexes.map(|index| match index.as_rule() {
142         Rule::Identifier => Expression::with_literal(index.as_str().to_owned()),
143         Rule::Value => parse_value(index),
144         _ => unreachable!(),
145     });
146 
147     variable.extend(indexes);
148     variable
149 }
150 
151 /// Parses an `Expression` from a `Pair` with a value.
152 ///
153 /// Do not confuse this value with `liquid-value`'s `Value`.
154 /// In this runtime, value refers to either a literal value or a variable.
155 ///
156 /// This `Pair` must be `Rule::Value`.
parse_value(value: Pair) -> Expression157 fn parse_value(value: Pair) -> Expression {
158     if value.as_rule() != Rule::Value {
159         panic!("Expected value.");
160     }
161 
162     let value = value.into_inner().next().expect("Get inside the value.");
163 
164     match value.as_rule() {
165         Rule::Literal => Expression::Literal(parse_literal(value)),
166         Rule::Variable => Expression::Variable(parse_variable(value)),
167         _ => unreachable!(),
168     }
169 }
170 
171 /// Parses a `FilterCall` from a `Pair` with a filter.
172 /// This `Pair` must be `Rule::Filter`.
parse_filter(filter: Pair, options: &Language) -> Result<Box<dyn Filter>>173 fn parse_filter(filter: Pair, options: &Language) -> Result<Box<dyn Filter>> {
174     if filter.as_rule() != Rule::Filter {
175         panic!("Expected a filter.");
176     }
177 
178     let filter_str = filter.as_str();
179     let mut filter = filter.into_inner();
180     let name = filter.next().expect("A filter always has a name.").as_str();
181 
182     let mut keyword_args = Vec::new();
183     let mut positional_args = Vec::new();
184 
185     for arg in filter {
186         match arg.as_rule() {
187             Rule::PositionalFilterArgument => {
188                 let value = arg.into_inner().next().expect("Rule ensures value.");
189                 let value = parse_value(value);
190                 positional_args.push(value);
191             }
192             Rule::KeywordFilterArgument => {
193                 let mut arg = arg.into_inner();
194                 let key = arg.next().expect("Rule ensures identifier.").as_str();
195                 let value = arg.next().expect("Rule ensures value.");
196                 let value = parse_value(value);
197                 keyword_args.push((key, value));
198             }
199             _ => unreachable!(),
200         }
201     }
202 
203     let args = FilterArguments {
204         positional: Box::new(positional_args.into_iter()),
205         keyword: Box::new(keyword_args.into_iter()),
206     };
207 
208     let f = options.filters.get(name).ok_or_else(|| {
209         let mut available: Vec<_> = options.filters.plugin_names().collect();
210         available.sort_unstable();
211         let available = itertools::join(available, ", ");
212         Error::with_msg("Unknown filter")
213             .context("requested filter", name.to_owned())
214             .context("available filters", available)
215     })?;
216 
217     let f = f
218         .parse(args)
219         .trace("Filter parsing error")
220         .context_key("filter")
221         .value_with(|| filter_str.to_string().into())?;
222 
223     Ok(f)
224 }
225 
226 /// Parses a `FilterChain` from a `Pair` with a filter chain.
227 /// This `Pair` must be `Rule::FilterChain`.
parse_filter_chain(chain: Pair, options: &Language) -> Result<FilterChain>228 fn parse_filter_chain(chain: Pair, options: &Language) -> Result<FilterChain> {
229     if chain.as_rule() != Rule::FilterChain {
230         panic!("Expected an expression with filters.");
231     }
232 
233     let mut chain = chain.into_inner();
234     let entry = parse_value(
235         chain
236             .next()
237             .expect("A filterchain always has starts by a value."),
238     );
239     let filters: Result<Vec<_>> = chain.map(|f| parse_filter(f, options)).collect();
240     let filters = filters?;
241 
242     let filters = FilterChain::new(entry, filters);
243     Ok(filters)
244 }
245 
246 /// An interface to access elements inside a block.
247 pub struct TagBlock<'a: 'b, 'b> {
248     name: &'b str,
249     iter: &'b mut dyn Iterator<Item = Pair<'a>>,
250     closed: bool,
251 }
252 
253 impl<'a, 'b> TagBlock<'a, 'b> {
new(name: &'b str, next_elements: &'b mut dyn Iterator<Item = Pair<'a>>) -> Self254     fn new(name: &'b str, next_elements: &'b mut dyn Iterator<Item = Pair<'a>>) -> Self {
255         TagBlock {
256             name,
257             iter: next_elements,
258             closed: false,
259         }
260     }
261 
262     /// Returns the next element of the block, if any, similarly to an iterator.
263     ///
264     /// However, if the input text reaches its end and the block is not closed,
265     /// an error is returned instead.
266     #[allow(clippy::should_implement_trait)]
next(&mut self) -> Result<Option<BlockElement<'a>>>267     pub fn next(&mut self) -> Result<Option<BlockElement<'a>>> {
268         if self.closed {
269             return Ok(None);
270         }
271 
272         let element = self.iter.next().expect("File shouldn't end before EOI.");
273 
274         if element.as_rule() == Rule::EOI {
275             return error_from_pair(
276                 element,
277                 format!("Unclosed block. {{% end{} %}} tag expected.", self.name),
278             )
279             .into_err();
280         }
281 
282         // Tags are treated separately so as to check for a possible `{% endtag %}`
283         if element.as_rule() == Rule::Tag {
284             let as_str = element.as_str();
285             let mut tag = element
286                 .into_inner()
287                 .next()
288                 .expect("Unwrapping TagInner")
289                 .into_inner();
290             let name = tag.next().expect("Tags start by their identifier.");
291             let name_str = name.as_str();
292 
293             // The name of the closing tag is "end" followed by the tag's name.
294             if name_str.len() > 3 && &name_str[0..3] == "end" && &name_str[3..] == self.name {
295                 // Then this is a block ending tag and will close the block.
296 
297                 // no more arguments should be supplied, trying to supply them is an error
298                 if let Some(token) = tag.next() {
299                     return TagToken::from(token).raise_error().into_err();
300                 }
301 
302                 self.closed = true;
303                 return Ok(None);
304             } else {
305                 // Then this is a regular tag
306                 let tokens = TagTokenIter::new(&name, tag);
307                 return Ok(Some(BlockElement::Tag(Tag {
308                     name,
309                     tokens,
310                     as_str,
311                 })));
312             }
313         }
314         Ok(Some(element.into()))
315     }
316 
317     /// Retrieves all the content of this block as a String, regardless of
318     /// being valid liquid or not.
319     ///
320     /// Do not use this method in a block you already called `.next()` on.
321     ///
322     /// Set the parameter `allow_nesting` of this function to true if you
323     /// still want these tags to nest (so the number of `{% name %}` must
324     /// be equal to the number of `{% endname %}`) of false if you don't
325     /// (only the first `{% name %}` is parsed, a single `{% endname %}`
326     /// will always close the tag).
327     ///
328     /// # Panics
329     ///
330     /// Will panic if used in a closed block.
escape_liquid(&mut self, allow_nesting: bool) -> Result<&'a str>331     pub fn escape_liquid(&mut self, allow_nesting: bool) -> Result<&'a str> {
332         if self.closed {
333             panic!("`escape_liquid` must be used in an open tag.")
334         }
335 
336         let mut nesting_level = 1;
337 
338         // Working with pest positions allows returning a `&str` instead of a `String`
339         let mut start_pos = None;
340         let mut end_pos = None;
341 
342         while let Some(element) = self.iter.next() {
343             let element_as_span = element.as_span();
344             if start_pos.is_none() {
345                 start_pos = Some(element_as_span.start_pos());
346             }
347 
348             if element.as_rule() == Rule::EOI {
349                 return error_from_pair(
350                     element,
351                     format!("Unclosed block. {{% end{} %}} tag expected.", self.name),
352                 )
353                 .into_err();
354             }
355 
356             // Tags are potentially `{% endtag %}`
357             if element.as_rule() == Rule::Tag {
358                 let mut tag = element
359                     .into_inner()
360                     .next()
361                     .expect("Unwrapping TagInner")
362                     .into_inner();
363                 let name = tag.next().expect("Tags start by their identifier.");
364                 let name_str = name.as_str();
365 
366                 // The name of the closing tag is "end" followed by the tag's name.
367                 if name_str.len() > 3 && &name_str[0..3] == "end" && &name_str[3..] == self.name {
368                     // No more arguments should be supplied. If they are, it is
369                     // assumed not to be a tag closer.
370                     if tag.next().is_none() {
371                         nesting_level -= 1;
372                         if nesting_level == 0 {
373                             self.closed = true;
374                             let start_pos = start_pos.expect("Will be `Some` inside this loop.");
375                             let output = match end_pos {
376                                 Some(end_pos) => start_pos.span(&end_pos).as_str(),
377                                 None => "",
378                             };
379 
380                             return Ok(output);
381                         }
382                     }
383                 } else if name_str == self.name && allow_nesting {
384                     // Going deeper in the nested blocks.
385                     nesting_level += 1;
386                 }
387             }
388 
389             end_pos = Some(element_as_span.end_pos());
390         }
391 
392         panic!("Function must eventually find either a Rule::EOI or a closing tag.")
393     }
394 
395     /// A convenient method that parses every element remaining in the block.
parse_all(&mut self, options: &Language) -> Result<Vec<Box<dyn Renderable>>>396     pub fn parse_all(&mut self, options: &Language) -> Result<Vec<Box<dyn Renderable>>> {
397         let mut renderables = Vec::new();
398         while let Some(r) = self.parse_next(options)? {
399             renderables.push(r);
400         }
401         Ok(renderables)
402     }
403 
404     /// Parses the next element in the block just as if it weren't inside any block.
405     ///
406     /// Returns none if no element is left and raises the same errors as `next()`.
parse_next(&mut self, options: &Language) -> Result<Option<Box<dyn Renderable>>>407     pub fn parse_next(&mut self, options: &Language) -> Result<Option<Box<dyn Renderable>>> {
408         match self.next()? {
409             None => Ok(None),
410             Some(element) => Ok(Some(element.parse(self, options)?)),
411         }
412     }
413 
414     /// Checks whether the block was fully parsed its elements.
415     ///
416     /// This must be added at the end of every block right before returning, so as
417     /// to ensure that it doesn't leave any unparsed element by accident.
assert_empty(self)418     pub fn assert_empty(self) {
419         assert!(
420             self.closed,
421             "Block {{% {} %}} doesn't exhaust its iterator of elements.",
422             self.name
423         )
424     }
425 }
426 
427 /// An element that is raw text.
428 pub struct Raw<'a> {
429     text: &'a str,
430 }
431 impl<'a> From<Pair<'a>> for Raw<'a> {
from(element: Pair<'a>) -> Self432     fn from(element: Pair<'a>) -> Self {
433         if element.as_rule() != Rule::Raw {
434             panic!("Only rule Raw can be converted to Raw.");
435         }
436         Raw {
437             text: element.as_str(),
438         }
439     }
440 }
441 impl<'a> Into<&'a str> for Raw<'a> {
into(self) -> &'a str442     fn into(self) -> &'a str {
443         self.as_str()
444     }
445 }
446 impl<'a> Raw<'a> {
447     /// Turns the text into a Renderable.
into_renderable(self) -> Box<dyn Renderable>448     pub fn into_renderable(self) -> Box<dyn Renderable> {
449         Box::new(Text::new(self.as_str()))
450     }
451 
452     /// Returns the text as a str.
as_str(&self) -> &'a str453     pub fn as_str(&self) -> &'a str {
454         self.text
455     }
456 }
457 
458 /// An element that is a tag.
459 pub struct Tag<'a> {
460     name: Pair<'a>,
461     tokens: TagTokenIter<'a>,
462     as_str: &'a str,
463 }
464 
465 impl<'a> From<Pair<'a>> for Tag<'a> {
from(element: Pair<'a>) -> Self466     fn from(element: Pair<'a>) -> Self {
467         if element.as_rule() != Rule::Tag {
468             panic!("Only rule Tag can be converted to Tag.");
469         }
470         let as_str = element.as_str();
471         let mut tag = element
472             .into_inner()
473             .next()
474             .expect("Unwrapping TagInner.")
475             .into_inner();
476         let name = tag.next().expect("A tag starts with an identifier.");
477         let tokens = TagTokenIter::new(&name, tag);
478 
479         Tag {
480             name,
481             tokens,
482             as_str,
483         }
484     }
485 }
486 
487 impl<'a> Tag<'a> {
488     /// Creates a new tag from a string such as "{% tagname tagtoken1 tagtoken2 ... %}".
489     ///
490     /// This is used as a debug tool. It allows to easily build tags in unit tests.
new(text: &'a str) -> Result<Self>491     pub fn new(text: &'a str) -> Result<Self> {
492         let tag = LiquidParser::parse(Rule::Tag, text)
493             .map_err(convert_pest_error)?
494             .next()
495             .ok_or_else(|| Error::with_msg("Tried to create a Tag from an invalid string."))?;
496 
497         Ok(tag.into())
498     }
499 
500     /// Returns the name of this tag.
name(&self) -> &str501     pub fn name(&self) -> &str {
502         self.name.as_str()
503     }
504 
505     /// Returns the tokens of this tag.
tokens(&mut self) -> &mut TagTokenIter<'a>506     pub fn tokens(&mut self) -> &mut TagTokenIter<'a> {
507         &mut self.tokens
508     }
509 
510     /// Consumes this structure to obtain ownership over its tokens.
into_tokens(self) -> TagTokenIter<'a>511     pub fn into_tokens(self) -> TagTokenIter<'a> {
512         self.tokens
513     }
514 
515     /// Returns the tag as a str.
as_str(&self) -> &str516     pub fn as_str(&self) -> &str {
517         self.as_str
518     }
519 
520     /// Parses the tag just as if it weren't inside any block.
parse( self, tag_block: &mut TagBlock, options: &Language, ) -> Result<Box<dyn Renderable>>521     pub fn parse(
522         self,
523         tag_block: &mut TagBlock,
524         options: &Language,
525     ) -> Result<Box<dyn Renderable>> {
526         self.parse_pair(&mut tag_block.iter, options)
527     }
528 
529     /// The same as `parse`, but directly takes an iterator over `Pair`s instead of a TagBlock.
parse_pair( self, next_elements: &mut dyn Iterator<Item = Pair>, options: &Language, ) -> Result<Box<dyn Renderable>>530     fn parse_pair(
531         self,
532         next_elements: &mut dyn Iterator<Item = Pair>,
533         options: &Language,
534     ) -> Result<Box<dyn Renderable>> {
535         let (name, tokens) = (self.name, self.tokens);
536         let position = name.as_span();
537         let name = name.as_str();
538 
539         if let Some(plugin) = options.tags.get(name) {
540             plugin.parse(tokens, options)
541         } else if let Some(plugin) = options.blocks.get(name) {
542             let block = TagBlock::new(name, next_elements);
543             let renderables = plugin.parse(tokens, block, options)?;
544             Ok(renderables)
545         } else {
546             let pest_error = ::pest::error::Error::new_from_span(
547                 ::pest::error::ErrorVariant::CustomError {
548                     message: "Unknown tag.".to_string(),
549                 },
550                 position,
551             );
552             let mut all_tags: Vec<_> = options.tags.plugin_names().collect();
553             all_tags.sort_unstable();
554             let all_tags = itertools::join(all_tags, ", ");
555             let mut all_blocks: Vec<_> = options.blocks.plugin_names().collect();
556             all_blocks.sort_unstable();
557             let all_blocks = itertools::join(all_blocks, ", ");
558             let error = convert_pest_error(pest_error)
559                 .context("requested", name.to_owned())
560                 .context("available tags", all_tags)
561                 .context("available blocks", all_blocks);
562             Err(error)
563         }
564     }
565 }
566 
567 /// An element that is an expression.
568 pub struct Exp<'a> {
569     element: Pair<'a>,
570 }
571 
572 impl<'a> From<Pair<'a>> for Exp<'a> {
from(element: Pair<'a>) -> Self573     fn from(element: Pair<'a>) -> Self {
574         if element.as_rule() != Rule::Expression {
575             panic!("Only rule Expression can be converted to Expression.");
576         }
577         Exp { element }
578     }
579 }
580 
581 impl<'a> Exp<'a> {
582     /// Parses the expression just as if it weren't inside any block.
parse(self, options: &Language) -> Result<Box<dyn Renderable>>583     pub fn parse(self, options: &Language) -> Result<Box<dyn Renderable>> {
584         let filter_chain = self
585             .element
586             .into_inner()
587             .next()
588             .expect("Unwrapping ExpressionInner")
589             .into_inner()
590             .next()
591             .expect("An expression consists of one filterchain.");
592 
593         let filter_chain = parse_filter_chain(filter_chain, options)?;
594         Ok(Box::new(filter_chain))
595     }
596 
597     /// Returns the expression as a str.
as_str(&self) -> &str598     pub fn as_str(&self) -> &str {
599         self.element.as_str()
600     }
601 }
602 
603 /// This token could not be recognized as valid liquid.
604 /// If parsed, will raise an error.
605 pub struct InvalidLiquidToken<'a> {
606     element: Pair<'a>,
607 }
608 impl<'a> InvalidLiquidToken<'a> {
609     /// Returns the expression as a str.
610     // TODO consider removing this
as_str(&self) -> &str611     pub fn as_str(&self) -> &str {
612         self.element.as_str()
613     }
614 
615     /// Tries to parse this as valid liquid, which will inevitably raise an error.
616     /// This is needed in order to raise the right error message.
parse(self, tag_block: &mut TagBlock) -> Result<Box<dyn Renderable>>617     pub fn parse(self, tag_block: &mut TagBlock) -> Result<Box<dyn Renderable>> {
618         self.parse_pair(&mut tag_block.iter)
619     }
620 
621     /// Tries to parse this as valid liquid, which will inevitably raise an error.
622     /// This is needed in order to raise the correct error message.
parse_pair( self, next_elements: &mut dyn Iterator<Item = Pair>, ) -> Result<Box<dyn Renderable>>623     fn parse_pair(
624         self,
625         next_elements: &mut dyn Iterator<Item = Pair>,
626     ) -> Result<Box<dyn Renderable>> {
627         use pest::error::LineColLocation;
628 
629         let invalid_token_span = self.element.as_span();
630         let invalid_token_position = invalid_token_span.start_pos();
631         let (offset_l, offset_c) = invalid_token_position.line_col();
632         let offset_l = offset_l - 1;
633         let offset_c = offset_c - 1;
634 
635         let end_position = match next_elements.last() {
636             Some(element) => element.as_span().end_pos(),
637             None => invalid_token_span.end_pos(),
638         };
639 
640         let mut text = String::from(&invalid_token_position.line_of()[..offset_c]);
641         text.push_str(invalid_token_position.span(&end_position).as_str());
642 
643         // Reparses from the line where invalid liquid started, in order
644         // to raise the error.
645         let mut error = match LiquidParser::parse(Rule::LiquidFile, &text) {
646             Ok(_) => panic!("`LiquidParser::parse` should fail in InvalidLiquidTokens."),
647             Err(error) => error,
648         };
649 
650         // Adds an offset to the line of the error, in order to show the right line
651         // TODO when liquid::error is able to handle line/col information by itself
652         // make this operation on the liquid Error type instead.
653         error.line_col = match error.line_col {
654             LineColLocation::Span((ls, cs), (le, ce)) => {
655                 LineColLocation::Span((ls + offset_l, cs), (le + offset_l, ce))
656             }
657             LineColLocation::Pos((ls, cs)) => LineColLocation::Pos((ls + offset_l, cs)),
658         };
659 
660         Err(convert_pest_error(error))
661     }
662 }
663 impl<'a> From<Pair<'a>> for InvalidLiquidToken<'a> {
from(element: Pair<'a>) -> Self664     fn from(element: Pair<'a>) -> Self {
665         if element.as_rule() != Rule::InvalidLiquid {
666             panic!("Tried to parse a valid liquid token as invalid.");
667         }
668         InvalidLiquidToken { element }
669     }
670 }
671 
672 /// An element that can be raw text, a tag, or an expression.
673 ///
674 /// This is the result of calling `next()` on a `TagBlock`.
675 pub enum BlockElement<'a> {
676     Raw(Raw<'a>),
677     Tag(Tag<'a>),
678     Expression(Exp<'a>),
679     Invalid(InvalidLiquidToken<'a>),
680 }
681 impl<'a> From<Pair<'a>> for BlockElement<'a> {
from(element: Pair<'a>) -> Self682     fn from(element: Pair<'a>) -> Self {
683         match element.as_rule() {
684             Rule::Raw => BlockElement::Raw(element.into()),
685             Rule::Tag => BlockElement::Tag(element.into()),
686             Rule::Expression => BlockElement::Expression(element.into()),
687             Rule::InvalidLiquid => BlockElement::Invalid(element.into()),
688             _ => panic!(
689                 "Only rules Raw | Tag | Expression can be converted to BlockElement. Found {:?}",
690                 element.as_rule()
691             ),
692         }
693     }
694 }
695 
696 impl<'a> BlockElement<'a> {
697     /// Parses the element in the block just as if it weren't inside any block.
parse( self, block: &mut TagBlock<'a, '_>, options: &Language, ) -> Result<Box<dyn Renderable>>698     pub fn parse(
699         self,
700         block: &mut TagBlock<'a, '_>,
701         options: &Language,
702     ) -> Result<Box<dyn Renderable>> {
703         match self {
704             BlockElement::Raw(raw) => Ok(raw.into_renderable()),
705             BlockElement::Tag(tag) => tag.parse(block, options),
706             BlockElement::Expression(exp) => exp.parse(options),
707             BlockElement::Invalid(invalid) => invalid.parse(block),
708         }
709     }
710 
711     /// The same as `parse`, but directly takes an iterator over `Pair`s instead of a TagBlock.
parse_pair( self, next_elements: &mut dyn Iterator<Item = Pair>, options: &Language, ) -> Result<Box<dyn Renderable>>712     fn parse_pair(
713         self,
714         next_elements: &mut dyn Iterator<Item = Pair>,
715         options: &Language,
716     ) -> Result<Box<dyn Renderable>> {
717         match self {
718             BlockElement::Raw(raw) => Ok(raw.into_renderable()),
719             BlockElement::Tag(tag) => tag.parse_pair(next_elements, options),
720             BlockElement::Expression(exp) => exp.parse(options),
721             BlockElement::Invalid(invalid) => invalid.parse_pair(next_elements),
722         }
723     }
724 
725     /// Returns the element as a str.
as_str(&self) -> &str726     pub fn as_str(&self) -> &str {
727         match self {
728             BlockElement::Raw(raw) => raw.as_str(),
729             BlockElement::Tag(tag) => tag.as_str(),
730             BlockElement::Expression(exp) => exp.as_str(),
731             BlockElement::Invalid(invalid) => invalid.as_str(),
732         }
733     }
734 }
735 
736 /// An iterator over `TagToken`s that is aware of their position in the file.
737 ///
738 /// The awareness of the position allows more precise error messages.
739 pub struct TagTokenIter<'a> {
740     iter: Box<dyn Iterator<Item = TagToken<'a>> + 'a>,
741     position: ::pest::Position<'a>,
742 }
743 impl<'a> Iterator for TagTokenIter<'a> {
744     type Item = TagToken<'a>;
next(&mut self) -> Option<Self::Item>745     fn next(&mut self) -> Option<Self::Item> {
746         self.iter.next().map(|next| {
747             self.position = next.token.as_span().end_pos();
748             next
749         })
750     }
751 }
752 impl<'a> TagTokenIter<'a> {
new(name: &Pair<'a>, tokens: Pairs<'a>) -> Self753     fn new(name: &Pair<'a>, tokens: Pairs<'a>) -> Self {
754         TagTokenIter {
755             iter: Box::new(tokens.map(TagToken::from)),
756             position: name.as_span().end_pos(),
757         }
758     }
759 
760     /// Creates an error with the given message pointing at the current
761     /// position of the iterator.
raise_error(&mut self, error_msg: &str) -> Error762     pub fn raise_error(&mut self, error_msg: &str) -> Error {
763         let pest_error = ::pest::error::Error::new_from_pos(
764             ::pest::error::ErrorVariant::CustomError {
765                 message: error_msg.to_string(),
766             },
767             self.position.clone(),
768         );
769         convert_pest_error(pest_error)
770     }
771 
772     /// Returns the next tag token or raises an error if there is none.
expect_next(&mut self, error_msg: &str) -> Result<TagToken<'a>>773     pub fn expect_next(&mut self, error_msg: &str) -> Result<TagToken<'a>> {
774         self.next().ok_or_else(|| self.raise_error(error_msg))
775     }
776 
777     /// Returns `Ok` if the iterator is empty, an error otherwise
expect_nothing(&mut self) -> Result<()>778     pub fn expect_nothing(&mut self) -> Result<()> {
779         if let Some(token) = self.next() {
780             Err(token.raise_error())
781         } else {
782             Ok(())
783         }
784     }
785 }
786 
787 /// The result of calling `TagToken`'s `try`.
788 ///
789 /// If the token is successfuly matched, the match is returned;
790 /// otherwise, the TagToken is returned back.
791 pub enum TryMatchToken<'a, T> {
792     Matches(T),
793     Fails(TagToken<'a>),
794 }
795 
796 impl<'a, T> TryMatchToken<'a, T> {
into_result(self) -> Result<T>797     pub fn into_result(self) -> Result<T> {
798         match self {
799             TryMatchToken::Matches(t) => Ok(t),
800             TryMatchToken::Fails(t) => Err(t.raise_error()),
801         }
802     }
803 
into_result_custom_msg(self, msg: &str) -> Result<T>804     pub fn into_result_custom_msg(self, msg: &str) -> Result<T> {
805         match self {
806             TryMatchToken::Matches(t) => Ok(t),
807             TryMatchToken::Fails(t) => Err(t.raise_custom_error(msg)),
808         }
809     }
810 }
811 
812 /// An interface to access tokens inside a tag.
813 pub struct TagToken<'a> {
814     token: Pair<'a>,
815     expected: Vec<Rule>,
816 }
817 
818 impl<'a> From<Pair<'a>> for TagToken<'a> {
from(token: Pair<'a>) -> Self819     fn from(token: Pair<'a>) -> Self {
820         TagToken {
821             token,
822             expected: Vec::new(),
823         }
824     }
825 }
826 
827 impl<'a> TagToken<'a> {
828     /// Raises an error from this TagToken.
829     ///
830     /// The error message will be based on the expected tokens,
831     /// which this structure tracks when using the methods starting
832     /// with 'expect'.
833     ///
834     /// For example, if one calls `expect_value` and that function fails
835     /// to give an `Ok` value, calling this would show `Expected Value`
836     /// on the error message.
raise_error(self) -> Error837     pub fn raise_error(self) -> Error {
838         let pest_error = ::pest::error::Error::new_from_span(
839             ::pest::error::ErrorVariant::ParsingError {
840                 positives: self.expected,
841                 negatives: vec![self.token.as_rule()],
842             },
843             self.token.as_span(),
844         );
845         convert_pest_error(pest_error)
846     }
847 
848     /// Raises an error from this TagToken.
849     ///
850     /// The error will have the given error message.
raise_custom_error(self, msg: &str) -> Error851     pub fn raise_custom_error(self, msg: &str) -> Error {
852         let pest_error = ::pest::error::Error::new_from_span(
853             ::pest::error::ErrorVariant::CustomError {
854                 message: msg.to_string(),
855             },
856             self.token.as_span(),
857         );
858         convert_pest_error(pest_error)
859     }
860 
unwrap_filter_chain(&mut self) -> std::result::Result<Pair<'a>, ()>861     fn unwrap_filter_chain(&mut self) -> std::result::Result<Pair<'a>, ()> {
862         let token = self.token.clone();
863 
864         if token.as_rule() != Rule::FilterChain {
865             return Err(());
866         }
867 
868         Ok(token)
869     }
870 
unwrap_value(&mut self) -> std::result::Result<Pair<'a>, ()>871     fn unwrap_value(&mut self) -> std::result::Result<Pair<'a>, ()> {
872         let filterchain = self.unwrap_filter_chain()?;
873 
874         let mut chain = filterchain.into_inner();
875         let value = chain.next().expect("Unwrapping value out of Filterchain.");
876         if chain.next().is_some() {
877             // There are filters: it can't be a value
878             return Err(());
879         }
880 
881         Ok(value)
882     }
883 
unwrap_variable(&mut self) -> std::result::Result<Pair<'a>, ()>884     fn unwrap_variable(&mut self) -> std::result::Result<Pair<'a>, ()> {
885         let value = self.unwrap_value()?;
886 
887         let variable = value
888             .into_inner()
889             .next()
890             .expect("A value is made of one token.");
891 
892         if variable.as_rule() != Rule::Variable {
893             return Err(());
894         }
895 
896         Ok(variable)
897     }
898 
unwrap_identifier(&mut self) -> std::result::Result<Pair<'a>, ()>899     fn unwrap_identifier(&mut self) -> std::result::Result<Pair<'a>, ()> {
900         let variable = self.unwrap_variable()?;
901 
902         let mut indexes = variable.into_inner();
903         let identifier = indexes
904             .next()
905             .expect("Unwrapping identifier out of variable.");
906         if indexes.next().is_some() {
907             // There are indexes: it can't be a value
908             return Err(());
909         }
910 
911         Ok(identifier)
912     }
913 
unwrap_literal(&mut self) -> std::result::Result<Pair<'a>, ()>914     fn unwrap_literal(&mut self) -> std::result::Result<Pair<'a>, ()> {
915         let value = self.unwrap_value()?;
916 
917         let literal = value
918             .into_inner()
919             .next()
920             .expect("A value is made of one token.");
921 
922         if literal.as_rule() != Rule::Literal {
923             return Err(());
924         }
925 
926         Ok(literal)
927     }
928 
929     /// Tries to obtain a `FilterChain` from this token.
expect_filter_chain(mut self, options: &Language) -> TryMatchToken<'a, FilterChain>930     pub fn expect_filter_chain(mut self, options: &Language) -> TryMatchToken<'a, FilterChain> {
931         match self.expect_filter_chain_err(options) {
932             Ok(t) => TryMatchToken::Matches(t),
933             Err(_) => {
934                 self.expected.push(Rule::FilterChain);
935                 TryMatchToken::Fails(self)
936             }
937         }
938     }
939 
expect_filter_chain_err(&mut self, options: &Language) -> Result<FilterChain>940     fn expect_filter_chain_err(&mut self, options: &Language) -> Result<FilterChain> {
941         let t = self
942             .unwrap_filter_chain()
943             .map_err(|_| Error::with_msg("failed to parse"))?;
944         let f = parse_filter_chain(t, options)?;
945         Ok(f)
946     }
947 
948     /// Tries to obtain a value from this token.
949     ///
950     /// Do not confuse this value with `liquid-value`'s `Value`.
951     /// In this runtime, value refers to either a literal value or a variable.
expect_value(mut self) -> TryMatchToken<'a, Expression>952     pub fn expect_value(mut self) -> TryMatchToken<'a, Expression> {
953         match self.unwrap_value() {
954             Ok(t) => TryMatchToken::Matches(parse_value(t)),
955             Err(_) => {
956                 self.expected.push(Rule::Value);
957                 TryMatchToken::Fails(self)
958             }
959         }
960     }
961 
962     /// Tries to obtain a `Variable` from this token.
expect_variable(mut self) -> TryMatchToken<'a, Variable>963     pub fn expect_variable(mut self) -> TryMatchToken<'a, Variable> {
964         match self.unwrap_variable() {
965             Ok(t) => TryMatchToken::Matches(parse_variable(t)),
966             Err(_) => {
967                 self.expected.push(Rule::Variable);
968                 TryMatchToken::Fails(self)
969             }
970         }
971     }
972 
973     /// Tries to obtain an identifier from this token.
974     ///
975     /// The identifier is returned as a str.
expect_identifier(mut self) -> TryMatchToken<'a, &'a str>976     pub fn expect_identifier(mut self) -> TryMatchToken<'a, &'a str> {
977         match self.unwrap_identifier() {
978             Ok(t) => TryMatchToken::Matches(t.as_str()),
979             Err(_) => {
980                 self.expected.push(Rule::Identifier);
981                 TryMatchToken::Fails(self)
982             }
983         }
984     }
985 
986     /// Tries to obtain a literal value from this token.
987     ///
988     /// The value is returned as a `Value`.
expect_literal(mut self) -> TryMatchToken<'a, Value>989     pub fn expect_literal(mut self) -> TryMatchToken<'a, Value> {
990         match self.unwrap_literal() {
991             Ok(t) => TryMatchToken::Matches(parse_literal(t)),
992             Err(_) => {
993                 self.expected.push(Rule::Literal);
994                 TryMatchToken::Fails(self)
995             }
996         }
997     }
998     /// Tries to obtain a range from this token.
999     ///
1000     /// The range is returned as a pair `(Expression, Expression)`.
expect_range(mut self) -> TryMatchToken<'a, (Expression, Expression)>1001     pub fn expect_range(mut self) -> TryMatchToken<'a, (Expression, Expression)> {
1002         let token = self.token.clone();
1003 
1004         if token.as_rule() != Rule::Range {
1005             self.expected.push(Rule::Range);
1006             return TryMatchToken::Fails(self);
1007         }
1008 
1009         let mut range = token.into_inner();
1010         TryMatchToken::Matches((
1011             parse_value(range.next().expect("start")),
1012             parse_value(range.next().expect("end")),
1013         ))
1014     }
1015 
1016     /// Returns `Ok` if and only if the tokens' str is equal to the given str.
expect_str(self, expected: &str) -> TryMatchToken<'a, ()>1017     pub fn expect_str(self, expected: &str) -> TryMatchToken<'a, ()> {
1018         if self.as_str() == expected {
1019             TryMatchToken::Matches(())
1020         } else {
1021             // TODO change `self`'s state to be aware that `expected` was expected.
1022             TryMatchToken::Fails(self)
1023         }
1024     }
1025 
1026     /// Returns token as a str.
as_str(&self) -> &str1027     pub fn as_str(&self) -> &str {
1028         self.token.as_str().trim()
1029     }
1030 }
1031 
1032 #[cfg(test)]
1033 mod test {
1034     use super::*;
1035     use crate::runtime::{Runtime, RuntimeBuilder, Template};
1036 
1037     #[test]
test_parse_literal()1038     fn test_parse_literal() {
1039         let nil = LiquidParser::parse(Rule::Literal, "nil")
1040             .unwrap()
1041             .next()
1042             .unwrap();
1043         assert_eq!(parse_literal(nil), Value::Nil);
1044         let nil = LiquidParser::parse(Rule::Literal, "null")
1045             .unwrap()
1046             .next()
1047             .unwrap();
1048         assert_eq!(parse_literal(nil), Value::Nil);
1049 
1050         let blank = LiquidParser::parse(Rule::Literal, "blank")
1051             .unwrap()
1052             .next()
1053             .unwrap();
1054         assert_eq!(
1055             parse_literal(blank),
1056             Value::State(crate::model::State::Blank)
1057         );
1058 
1059         let empty = LiquidParser::parse(Rule::Literal, "empty")
1060             .unwrap()
1061             .next()
1062             .unwrap();
1063         assert_eq!(
1064             parse_literal(empty),
1065             Value::State(crate::model::State::Empty)
1066         );
1067 
1068         let integer = LiquidParser::parse(Rule::Literal, "42")
1069             .unwrap()
1070             .next()
1071             .unwrap();
1072         assert_eq!(parse_literal(integer), Value::scalar(42));
1073 
1074         let negative_int = LiquidParser::parse(Rule::Literal, "-42")
1075             .unwrap()
1076             .next()
1077             .unwrap();
1078         assert_eq!(parse_literal(negative_int), Value::scalar(-42));
1079 
1080         let float = LiquidParser::parse(Rule::Literal, "4321.032")
1081             .unwrap()
1082             .next()
1083             .unwrap();
1084         assert_eq!(parse_literal(float), Value::scalar(4321.032));
1085 
1086         let negative_float = LiquidParser::parse(Rule::Literal, "-4321.032")
1087             .unwrap()
1088             .next()
1089             .unwrap();
1090         assert_eq!(parse_literal(negative_float), Value::scalar(-4321.032));
1091 
1092         let boolean = LiquidParser::parse(Rule::Literal, "true")
1093             .unwrap()
1094             .next()
1095             .unwrap();
1096         assert_eq!(parse_literal(boolean), Value::scalar(true));
1097 
1098         let string_double_quotes = LiquidParser::parse(Rule::Literal, "\"Hello world!\"")
1099             .unwrap()
1100             .next()
1101             .unwrap();
1102         assert_eq!(
1103             parse_literal(string_double_quotes),
1104             Value::scalar("Hello world!")
1105         );
1106 
1107         let string_single_quotes = LiquidParser::parse(Rule::Literal, "'Liquid'")
1108             .unwrap()
1109             .next()
1110             .unwrap();
1111         assert_eq!(parse_literal(string_single_quotes), Value::scalar("Liquid"));
1112     }
1113 
1114     #[test]
test_parse_variable()1115     fn test_parse_variable() {
1116         let variable = LiquidParser::parse(Rule::Variable, "foo[0].bar.baz[foo.bar]")
1117             .unwrap()
1118             .next()
1119             .unwrap();
1120 
1121         let indexes = vec![
1122             Expression::Literal(Value::scalar(0)),
1123             Expression::Literal(Value::scalar("bar")),
1124             Expression::Literal(Value::scalar("baz")),
1125             Expression::Variable(Variable::with_literal("foo").push_literal("bar")),
1126         ];
1127 
1128         let mut expected = Variable::with_literal("foo");
1129         expected.extend(indexes);
1130 
1131         assert_eq!(parse_variable(variable), expected);
1132     }
1133 
1134     #[test]
test_whitespace_control()1135     fn test_whitespace_control() {
1136         let options = Language::default();
1137 
1138         let runtime = RuntimeBuilder::new().build();
1139         runtime.set_global("exp".into(), Value::scalar(5));
1140 
1141         let text = "    \n    {{ exp }}    \n    ";
1142         let template = parse(text, &options).map(Template::new).unwrap();
1143         let output = template.render(&runtime).unwrap();
1144 
1145         assert_eq!(output, "    \n    5    \n    ");
1146 
1147         let text = "    \n    {{- exp }}    \n    ";
1148         let template = parse(text, &options).map(Template::new).unwrap();
1149         let output = template.render(&runtime).unwrap();
1150 
1151         assert_eq!(output, "5    \n    ");
1152 
1153         let text = "    \n    {{ exp -}}    \n    ";
1154         let template = parse(text, &options).map(Template::new).unwrap();
1155         let output = template.render(&runtime).unwrap();
1156 
1157         assert_eq!(output, "    \n    5");
1158 
1159         let text = "    \n    {{- exp -}}    \n    ";
1160         let template = parse(text, &options).map(Template::new).unwrap();
1161         let output = template.render(&runtime).unwrap();
1162 
1163         assert_eq!(output, "5");
1164     }
1165 }
1166