1use crate::error::{Error, Result, ResultLiquidExt};
7use crate::model::Value;
8use crate::runtime::Expression;
9use crate::runtime::Renderable;
10use crate::runtime::Variable;
11
12use super::Language;
13use super::Text;
14use super::{Filter, FilterArguments, FilterChain};
15
16use pest::Parser;
17
18mod inner {
19 #[derive(Parser)]
20 #[grammar = "parser/grammar.pest"]
21 pub struct LiquidParser;
22}
23
24use self::inner::*;
25
26type Pair<'a> = ::pest::iterators::Pair<'a, Rule>;
27type Pairs<'a> = ::pest::iterators::Pairs<'a, Rule>;
28
29fn 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
47fn 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
57pub 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
81pub fn parse_variable(text: &str) -> Result<Variable> {
83 let variable = LiquidParser::parse(Rule::Variable, text)
84 .map_err(convert_pest_error)?
85 .next()
86 .expect("Parsing a variable failed.");
87
88 Ok(parse_variable_pair(variable))
89}
90
91fn parse_literal(literal: Pair) -> Value {
94 if literal.as_rule() != Rule::Literal {
95 panic!("Expected literal.");
96 }
97
98 let literal = literal
99 .into_inner()
100 .next()
101 .expect("Get into the rule inside literal.");
102
103 match literal.as_rule() {
104 Rule::NilLiteral => Value::Nil,
105 Rule::EmptyLiteral => Value::State(crate::model::State::Empty),
106 Rule::BlankLiteral => Value::State(crate::model::State::Blank),
107 Rule::StringLiteral => {
108 let literal = literal.as_str();
109 let trim_quotes = &literal[1..literal.len() - 1];
110
111 Value::scalar(trim_quotes.to_owned())
112 }
113 Rule::IntegerLiteral => Value::scalar(
114 literal
115 .as_str()
116 .parse::<i64>()
117 .expect("Grammar ensures matches are parseable as integers."),
118 ),
119 Rule::FloatLiteral => Value::scalar(
120 literal
121 .as_str()
122 .parse::<f64>()
123 .expect("Grammar ensures matches are parseable as floats."),
124 ),
125 Rule::BooleanLiteral => Value::scalar(
126 literal
127 .as_str()
128 .parse::<bool>()
129 .expect("Grammar ensures matches are parseable as bools."),
130 ),
131 _ => unreachable!(),
132 }
133}
134
135fn parse_variable_pair(variable: Pair) -> Variable {
138 if variable.as_rule() != Rule::Variable {
139 panic!("Expected variable.");
140 }
141
142 let mut indexes = variable.into_inner();
143
144 let first_identifier = indexes
145 .next()
146 .expect("A variable starts with an identifier.")
147 .as_str()
148 .to_owned();
149 let mut variable = Variable::with_literal(first_identifier);
150
151 let indexes = indexes.map(|index| match index.as_rule() {
152 Rule::Identifier => Expression::with_literal(index.as_str().to_owned()),
153 Rule::Value => parse_value(index),
154 _ => unreachable!(),
155 });
156
157 variable.extend(indexes);
158 variable
159}
160
161fn parse_value(value: Pair) -> Expression {
168 if value.as_rule() != Rule::Value {
169 panic!("Expected value.");
170 }
171
172 let value = value.into_inner().next().expect("Get inside the value.");
173
174 match value.as_rule() {
175 Rule::Literal => Expression::Literal(parse_literal(value)),
176 Rule::Variable => Expression::Variable(parse_variable_pair(value)),
177 _ => unreachable!(),
178 }
179}
180
181fn parse_filter(filter: Pair, options: &Language) -> Result<Box<dyn Filter>> {
184 if filter.as_rule() != Rule::Filter {
185 panic!("Expected a filter.");
186 }
187
188 let filter_str = filter.as_str();
189 let mut filter = filter.into_inner();
190 let name = filter.next().expect("A filter always has a name.").as_str();
191
192 let mut keyword_args = Vec::new();
193 let mut positional_args = Vec::new();
194
195 for arg in filter {
196 match arg.as_rule() {
197 Rule::PositionalFilterArgument => {
198 let value = arg.into_inner().next().expect("Rule ensures value.");
199 let value = parse_value(value);
200 positional_args.push(value);
201 }
202 Rule::KeywordFilterArgument => {
203 let mut arg = arg.into_inner();
204 let key = arg.next().expect("Rule ensures identifier.").as_str();
205 let value = arg.next().expect("Rule ensures value.");
206 let value = parse_value(value);
207 keyword_args.push((key, value));
208 }
209 _ => unreachable!(),
210 }
211 }
212
213 let args = FilterArguments {
214 positional: Box::new(positional_args.into_iter()),
215 keyword: Box::new(keyword_args.into_iter()),
216 };
217
218 let f = options.filters.get(name).ok_or_else(|| {
219 let mut available: Vec<_> = options.filters.plugin_names().collect();
220 available.sort_unstable();
221 let available = itertools::join(available, ", ");
222 Error::with_msg("Unknown filter")
223 .context("requested filter", name.to_owned())
224 .context("available filters", available)
225 })?;
226
227 let f = f
228 .parse(args)
229 .trace("Filter parsing error")
230 .context_key("filter")
231 .value_with(|| filter_str.to_string().into())?;
232
233 Ok(f)
234}
235
236fn parse_filter_chain(chain: Pair, options: &Language) -> Result<FilterChain> {
239 if chain.as_rule() != Rule::FilterChain {
240 panic!("Expected an expression with filters.");
241 }
242
243 let mut chain = chain.into_inner();
244 let entry = parse_value(
245 chain
246 .next()
247 .expect("A filterchain always has starts by a value."),
248 );
249 let filters: Result<Vec<_>> = chain.map(|f| parse_filter(f, options)).collect();
250 let filters = filters?;
251
252 let filters = FilterChain::new(entry, filters);
253 Ok(filters)
254}
255
256pub struct TagBlock<'a: 'b, 'b> {
258 start_tag: &'b str,
259 end_tag: &'b str,
260 iter: &'b mut dyn Iterator<Item = Pair<'a>>,
261 closed: bool,
262}
263
264impl<'a, 'b> TagBlock<'a, 'b> {
265 fn new(
266 start_tag: &'b str,
267 end_tag: &'b str,
268 next_elements: &'b mut dyn Iterator<Item = Pair<'a>>,
269 ) -> Self {
270 TagBlock {
271 start_tag,
272 end_tag,
273 iter: next_elements,
274 closed: false,
275 }
276 }
277
278 #[allow(clippy::should_implement_trait)]
283 pub fn next(&mut self) -> Result<Option<BlockElement<'a>>> {
284 if self.closed {
285 return Ok(None);
286 }
287
288 let element = self.iter.next().expect("File shouldn't end before EOI.");
289
290 if element.as_rule() == Rule::EOI {
291 return error_from_pair(
292 element,
293 format!("Unclosed block. {{% {} %}} tag expected.", self.end_tag),
294 )
295 .into_err();
296 }
297
298 if element.as_rule() == Rule::Tag {
300 let as_str = element.as_str();
301 let mut tag = element
302 .into_inner()
303 .next()
304 .expect("Unwrapping TagInner")
305 .into_inner();
306 let name = tag.next().expect("Tags start by their identifier.");
307 let name_str = name.as_str();
308
309 if name_str == self.end_tag {
311 if let Some(token) = tag.next() {
315 return TagToken::from(token).raise_error().into_err();
316 }
317
318 self.closed = true;
319 return Ok(None);
320 } else {
321 let tokens = TagTokenIter::new(&name, tag);
323 return Ok(Some(BlockElement::Tag(Tag {
324 name,
325 tokens,
326 as_str,
327 })));
328 }
329 }
330 Ok(Some(element.into()))
331 }
332
333 pub fn escape_liquid(&mut self, allow_nesting: bool) -> Result<&'a str> {
348 if self.closed {
349 panic!("`escape_liquid` must be used in an open tag.")
350 }
351
352 let mut nesting_level = 1;
353
354 let mut start_pos = None;
356 let mut end_pos = None;
357
358 #[allow(clippy::while_let_on_iterator)]
359 while let Some(element) = self.iter.next() {
360 let element_as_span = element.as_span();
361 if start_pos.is_none() {
362 start_pos = Some(element_as_span.start_pos());
363 }
364
365 if element.as_rule() == Rule::EOI {
366 return error_from_pair(
367 element,
368 format!("Unclosed block. {{% {} %}} tag expected.", self.end_tag),
369 )
370 .into_err();
371 }
372
373 if element.as_rule() == Rule::Tag {
375 let mut tag = element
376 .into_inner()
377 .next()
378 .expect("Unwrapping TagInner")
379 .into_inner();
380 let name = tag.next().expect("Tags start by their identifier.");
381 let name_str = name.as_str();
382
383 if name_str == self.end_tag {
385 if tag.next().is_none() {
388 nesting_level -= 1;
389 if nesting_level == 0 {
390 self.closed = true;
391 let start_pos = start_pos.expect("Will be `Some` inside this loop.");
392 let output = match end_pos {
393 Some(end_pos) => start_pos.span(&end_pos).as_str(),
394 None => "",
395 };
396
397 return Ok(output);
398 }
399 }
400 } else if name_str == self.start_tag && allow_nesting {
401 nesting_level += 1;
403 }
404 }
405
406 end_pos = Some(element_as_span.end_pos());
407 }
408
409 panic!("Function must eventually find either a Rule::EOI or a closing tag.")
410 }
411
412 pub fn parse_all(&mut self, options: &Language) -> Result<Vec<Box<dyn Renderable>>> {
414 let mut renderables = Vec::new();
415 while let Some(r) = self.parse_next(options)? {
416 renderables.push(r);
417 }
418 Ok(renderables)
419 }
420
421 pub fn parse_next(&mut self, options: &Language) -> Result<Option<Box<dyn Renderable>>> {
425 match self.next()? {
426 None => Ok(None),
427 Some(element) => Ok(Some(element.parse(self, options)?)),
428 }
429 }
430
431 pub fn assert_empty(self) {
436 assert!(
437 self.closed,
438 "Block {{% {} %}} doesn't exhaust its iterator of elements.",
439 self.start_tag
440 )
441 }
442}
443
444pub struct Raw<'a> {
446 text: &'a str,
447}
448impl<'a> From<Pair<'a>> for Raw<'a> {
449 fn from(element: Pair<'a>) -> Self {
450 if element.as_rule() != Rule::Raw {
451 panic!("Only rule Raw can be converted to Raw.");
452 }
453 Raw {
454 text: element.as_str(),
455 }
456 }
457}
458#[allow(clippy::from_over_into)]
459impl<'a> Into<&'a str> for Raw<'a> {
460 fn into(self) -> &'a str {
461 self.as_str()
462 }
463}
464impl<'a> Raw<'a> {
465 pub fn into_renderable(self) -> Box<dyn Renderable> {
467 Box::new(Text::new(self.as_str()))
468 }
469
470 pub fn as_str(&self) -> &'a str {
472 self.text
473 }
474}
475
476pub struct Tag<'a> {
478 name: Pair<'a>,
479 tokens: TagTokenIter<'a>,
480 as_str: &'a str,
481}
482
483impl<'a> From<Pair<'a>> for Tag<'a> {
484 fn from(element: Pair<'a>) -> Self {
485 if element.as_rule() != Rule::Tag {
486 panic!("Only rule Tag can be converted to Tag.");
487 }
488 let as_str = element.as_str();
489 let mut tag = element
490 .into_inner()
491 .next()
492 .expect("Unwrapping TagInner.")
493 .into_inner();
494 let name = tag.next().expect("A tag starts with an identifier.");
495 let tokens = TagTokenIter::new(&name, tag);
496
497 Tag {
498 name,
499 tokens,
500 as_str,
501 }
502 }
503}
504
505impl<'a> Tag<'a> {
506 pub fn new(text: &'a str) -> Result<Self> {
510 let tag = LiquidParser::parse(Rule::Tag, text)
511 .map_err(convert_pest_error)?
512 .next()
513 .ok_or_else(|| Error::with_msg("Tried to create a Tag from an invalid string."))?;
514
515 Ok(tag.into())
516 }
517
518 pub fn name(&self) -> &str {
520 self.name.as_str()
521 }
522
523 pub fn tokens(&mut self) -> &mut TagTokenIter<'a> {
525 &mut self.tokens
526 }
527
528 pub fn into_tokens(self) -> TagTokenIter<'a> {
530 self.tokens
531 }
532
533 pub fn as_str(&self) -> &str {
535 self.as_str
536 }
537
538 pub fn parse(
540 self,
541 tag_block: &mut TagBlock,
542 options: &Language,
543 ) -> Result<Box<dyn Renderable>> {
544 self.parse_pair(&mut tag_block.iter, options)
545 }
546
547 fn parse_pair(
549 self,
550 next_elements: &mut dyn Iterator<Item = Pair>,
551 options: &Language,
552 ) -> Result<Box<dyn Renderable>> {
553 let (name, tokens) = (self.name, self.tokens);
554 let position = name.as_span();
555 let name = name.as_str();
556
557 if let Some(plugin) = options.tags.get(name) {
558 plugin.parse(tokens, options)
559 } else if let Some(plugin) = options.blocks.get(name) {
560 let reflection = plugin.reflection();
561 let block = TagBlock::new(reflection.start_tag(), reflection.end_tag(), next_elements);
562 let renderables = plugin.parse(tokens, block, options)?;
563 Ok(renderables)
564 } else {
565 let pest_error = ::pest::error::Error::new_from_span(
566 ::pest::error::ErrorVariant::CustomError {
567 message: "Unknown tag.".to_string(),
568 },
569 position,
570 );
571 let mut all_tags: Vec<_> = options.tags.plugin_names().collect();
572 all_tags.sort_unstable();
573 let all_tags = itertools::join(all_tags, ", ");
574 let mut all_blocks: Vec<_> = options.blocks.plugin_names().collect();
575 all_blocks.sort_unstable();
576 let all_blocks = itertools::join(all_blocks, ", ");
577 let error = convert_pest_error(pest_error)
578 .context("requested", name.to_owned())
579 .context("available tags", all_tags)
580 .context("available blocks", all_blocks);
581 Err(error)
582 }
583 }
584}
585
586pub struct Exp<'a> {
588 element: Pair<'a>,
589}
590
591impl<'a> From<Pair<'a>> for Exp<'a> {
592 fn from(element: Pair<'a>) -> Self {
593 if element.as_rule() != Rule::Expression {
594 panic!("Only rule Expression can be converted to Expression.");
595 }
596 Exp { element }
597 }
598}
599
600impl Exp<'_> {
601 pub fn parse(self, options: &Language) -> Result<Box<dyn Renderable>> {
603 let filter_chain = self
604 .element
605 .into_inner()
606 .next()
607 .expect("Unwrapping ExpressionInner")
608 .into_inner()
609 .next()
610 .expect("An expression consists of one filterchain.");
611
612 let filter_chain = parse_filter_chain(filter_chain, options)?;
613 Ok(Box::new(filter_chain))
614 }
615
616 pub fn as_str(&self) -> &str {
618 self.element.as_str()
619 }
620}
621
622pub struct InvalidLiquidToken<'a> {
625 element: Pair<'a>,
626}
627impl InvalidLiquidToken<'_> {
628 pub fn as_str(&self) -> &str {
631 self.element.as_str()
632 }
633
634 pub fn parse(self, tag_block: &mut TagBlock) -> Result<Box<dyn Renderable>> {
637 self.parse_pair(&mut tag_block.iter)
638 }
639
640 fn parse_pair(
643 self,
644 next_elements: &mut dyn Iterator<Item = Pair>,
645 ) -> Result<Box<dyn Renderable>> {
646 use pest::error::LineColLocation;
647
648 let invalid_token_span = self.element.as_span();
649 let invalid_token_position = invalid_token_span.start_pos();
650 let (offset_l, offset_c) = invalid_token_position.line_col();
651 let offset_l = offset_l - 1;
652 let offset_c = (0..offset_c)
653 .rev()
654 .find(|i| invalid_token_position.line_of().is_char_boundary(*i))
655 .unwrap_or(0);
656
657 let end_position = match next_elements.last() {
658 Some(element) => element.as_span().end_pos(),
659 None => invalid_token_span.end_pos(),
660 };
661
662 let mut text = String::from(&invalid_token_position.line_of()[..offset_c]);
663 text.push_str(invalid_token_position.span(&end_position).as_str());
664
665 let mut error = match LiquidParser::parse(Rule::LiquidFile, &text) {
668 Ok(_) => panic!("`LiquidParser::parse` should fail in InvalidLiquidTokens."),
669 Err(error) => error,
670 };
671
672 error.line_col = match error.line_col {
676 LineColLocation::Span((ls, cs), (le, ce)) => {
677 LineColLocation::Span((ls + offset_l, cs), (le + offset_l, ce))
678 }
679 LineColLocation::Pos((ls, cs)) => LineColLocation::Pos((ls + offset_l, cs)),
680 };
681
682 Err(convert_pest_error(error))
683 }
684}
685impl<'a> From<Pair<'a>> for InvalidLiquidToken<'a> {
686 fn from(element: Pair<'a>) -> Self {
687 if element.as_rule() != Rule::InvalidLiquid {
688 panic!("Tried to parse a valid liquid token as invalid.");
689 }
690 InvalidLiquidToken { element }
691 }
692}
693
694pub enum BlockElement<'a> {
698 Raw(Raw<'a>),
699 Tag(Tag<'a>),
700 Expression(Exp<'a>),
701 Invalid(InvalidLiquidToken<'a>),
702}
703impl<'a> From<Pair<'a>> for BlockElement<'a> {
704 fn from(element: Pair<'a>) -> Self {
705 match element.as_rule() {
706 Rule::Raw => BlockElement::Raw(element.into()),
707 Rule::Tag => BlockElement::Tag(element.into()),
708 Rule::Expression => BlockElement::Expression(element.into()),
709 Rule::InvalidLiquid => BlockElement::Invalid(element.into()),
710 _ => panic!(
711 "Only rules Raw | Tag | Expression can be converted to BlockElement. Found {:?}",
712 element.as_rule()
713 ),
714 }
715 }
716}
717
718impl<'a> BlockElement<'a> {
719 pub fn parse(
721 self,
722 block: &mut TagBlock<'a, '_>,
723 options: &Language,
724 ) -> Result<Box<dyn Renderable>> {
725 match self {
726 BlockElement::Raw(raw) => Ok(raw.into_renderable()),
727 BlockElement::Tag(tag) => tag.parse(block, options),
728 BlockElement::Expression(exp) => exp.parse(options),
729 BlockElement::Invalid(invalid) => invalid.parse(block),
730 }
731 }
732
733 fn parse_pair(
735 self,
736 next_elements: &mut dyn Iterator<Item = Pair>,
737 options: &Language,
738 ) -> Result<Box<dyn Renderable>> {
739 match self {
740 BlockElement::Raw(raw) => Ok(raw.into_renderable()),
741 BlockElement::Tag(tag) => tag.parse_pair(next_elements, options),
742 BlockElement::Expression(exp) => exp.parse(options),
743 BlockElement::Invalid(invalid) => invalid.parse_pair(next_elements),
744 }
745 }
746
747 pub fn as_str(&self) -> &str {
749 match self {
750 BlockElement::Raw(raw) => raw.as_str(),
751 BlockElement::Tag(tag) => tag.as_str(),
752 BlockElement::Expression(exp) => exp.as_str(),
753 BlockElement::Invalid(invalid) => invalid.as_str(),
754 }
755 }
756}
757
758pub struct TagTokenIter<'a> {
762 iter: Box<dyn Iterator<Item = TagToken<'a>> + 'a>,
763 position: ::pest::Position<'a>,
764}
765impl<'a> Iterator for TagTokenIter<'a> {
766 type Item = TagToken<'a>;
767 fn next(&mut self) -> Option<Self::Item> {
768 self.iter.next().inspect(|next| {
769 self.position = next.token.as_span().end_pos();
770 })
771 }
772}
773impl<'a> TagTokenIter<'a> {
774 fn new(name: &Pair<'a>, tokens: Pairs<'a>) -> Self {
775 TagTokenIter {
776 iter: Box::new(tokens.map(TagToken::from)),
777 position: name.as_span().end_pos(),
778 }
779 }
780
781 pub fn raise_error(&mut self, error_msg: &str) -> Error {
784 let pest_error = ::pest::error::Error::new_from_pos(
785 ::pest::error::ErrorVariant::CustomError {
786 message: error_msg.to_string(),
787 },
788 self.position,
789 );
790 convert_pest_error(pest_error)
791 }
792
793 pub fn expect_next(&mut self, error_msg: &str) -> Result<TagToken<'a>> {
795 self.next().ok_or_else(|| self.raise_error(error_msg))
796 }
797
798 pub fn expect_nothing(&mut self) -> Result<()> {
800 if let Some(token) = self.next() {
801 Err(token.raise_error())
802 } else {
803 Ok(())
804 }
805 }
806}
807
808pub enum TryMatchToken<'a, T> {
813 Matches(T),
814 Fails(TagToken<'a>),
815}
816
817impl<T> TryMatchToken<'_, T> {
818 pub fn into_result(self) -> Result<T> {
819 match self {
820 TryMatchToken::Matches(t) => Ok(t),
821 TryMatchToken::Fails(t) => Err(t.raise_error()),
822 }
823 }
824
825 pub fn into_result_custom_msg(self, msg: &str) -> Result<T> {
826 match self {
827 TryMatchToken::Matches(t) => Ok(t),
828 TryMatchToken::Fails(t) => Err(t.raise_custom_error(msg)),
829 }
830 }
831}
832
833pub struct TagToken<'a> {
835 token: Pair<'a>,
836 expected: Vec<Rule>,
837}
838
839impl<'a> From<Pair<'a>> for TagToken<'a> {
840 fn from(token: Pair<'a>) -> Self {
841 TagToken {
842 token,
843 expected: Vec::new(),
844 }
845 }
846}
847
848impl<'a> TagToken<'a> {
849 pub fn raise_error(self) -> Error {
859 let pest_error = ::pest::error::Error::new_from_span(
860 ::pest::error::ErrorVariant::ParsingError {
861 positives: self.expected,
862 negatives: vec![self.token.as_rule()],
863 },
864 self.token.as_span(),
865 );
866 convert_pest_error(pest_error)
867 }
868
869 pub fn raise_custom_error(self, msg: &str) -> Error {
873 let pest_error = ::pest::error::Error::new_from_span(
874 ::pest::error::ErrorVariant::CustomError {
875 message: msg.to_string(),
876 },
877 self.token.as_span(),
878 );
879 convert_pest_error(pest_error)
880 }
881
882 fn unwrap_filter_chain(&mut self) -> std::result::Result<Pair<'a>, ()> {
883 let token = self.token.clone();
884
885 if token.as_rule() != Rule::FilterChain {
886 return Err(());
887 }
888
889 Ok(token)
890 }
891
892 fn unwrap_value(&mut self) -> std::result::Result<Pair<'a>, ()> {
893 let filterchain = self.unwrap_filter_chain()?;
894
895 let mut chain = filterchain.into_inner();
896 let value = chain.next().expect("Unwrapping value out of Filterchain.");
897 if chain.next().is_some() {
898 return Err(());
900 }
901
902 Ok(value)
903 }
904
905 fn unwrap_variable(&mut self) -> std::result::Result<Pair<'a>, ()> {
906 let value = self.unwrap_value()?;
907
908 let variable = value
909 .into_inner()
910 .next()
911 .expect("A value is made of one token.");
912
913 if variable.as_rule() != Rule::Variable {
914 return Err(());
915 }
916
917 Ok(variable)
918 }
919
920 fn unwrap_identifier(&mut self) -> std::result::Result<Pair<'a>, ()> {
921 let variable = self.unwrap_variable()?;
922
923 let mut indexes = variable.into_inner();
924 let identifier = indexes
925 .next()
926 .expect("Unwrapping identifier out of variable.");
927 if indexes.next().is_some() {
928 return Err(());
930 }
931
932 Ok(identifier)
933 }
934
935 fn unwrap_literal(&mut self) -> std::result::Result<Pair<'a>, ()> {
936 let value = self.unwrap_value()?;
937
938 let literal = value
939 .into_inner()
940 .next()
941 .expect("A value is made of one token.");
942
943 if literal.as_rule() != Rule::Literal {
944 return Err(());
945 }
946
947 Ok(literal)
948 }
949
950 pub fn expect_filter_chain(mut self, options: &Language) -> TryMatchToken<'a, FilterChain> {
952 match self.expect_filter_chain_err(options) {
953 Ok(t) => TryMatchToken::Matches(t),
954 Err(_) => {
955 self.expected.push(Rule::FilterChain);
956 TryMatchToken::Fails(self)
957 }
958 }
959 }
960
961 fn expect_filter_chain_err(&mut self, options: &Language) -> Result<FilterChain> {
962 let t = self
963 .unwrap_filter_chain()
964 .map_err(|_| Error::with_msg("failed to parse"))?;
965 let f = parse_filter_chain(t, options)?;
966 Ok(f)
967 }
968
969 pub fn expect_value(mut self) -> TryMatchToken<'a, Expression> {
974 match self.unwrap_value() {
975 Ok(t) => TryMatchToken::Matches(parse_value(t)),
976 Err(_) => {
977 self.expected.push(Rule::Value);
978 TryMatchToken::Fails(self)
979 }
980 }
981 }
982
983 pub fn expect_variable(mut self) -> TryMatchToken<'a, Variable> {
985 match self.unwrap_variable() {
986 Ok(t) => TryMatchToken::Matches(parse_variable_pair(t)),
987 Err(_) => {
988 self.expected.push(Rule::Variable);
989 TryMatchToken::Fails(self)
990 }
991 }
992 }
993
994 pub fn expect_identifier(mut self) -> TryMatchToken<'a, &'a str> {
998 match self.unwrap_identifier() {
999 Ok(t) => TryMatchToken::Matches(t.as_str()),
1000 Err(_) => {
1001 self.expected.push(Rule::Identifier);
1002 TryMatchToken::Fails(self)
1003 }
1004 }
1005 }
1006
1007 pub fn expect_literal(mut self) -> TryMatchToken<'a, Value> {
1011 match self.unwrap_literal() {
1012 Ok(t) => TryMatchToken::Matches(parse_literal(t)),
1013 Err(_) => {
1014 self.expected.push(Rule::Literal);
1015 TryMatchToken::Fails(self)
1016 }
1017 }
1018 }
1019 pub fn expect_range(mut self) -> TryMatchToken<'a, (Expression, Expression)> {
1023 let token = self.token.clone();
1024
1025 if token.as_rule() != Rule::Range {
1026 self.expected.push(Rule::Range);
1027 return TryMatchToken::Fails(self);
1028 }
1029
1030 let mut range = token.into_inner();
1031 TryMatchToken::Matches((
1032 parse_value(range.next().expect("start")),
1033 parse_value(range.next().expect("end")),
1034 ))
1035 }
1036
1037 pub fn expect_str(self, expected: &str) -> TryMatchToken<'a, ()> {
1039 if self.as_str() == expected {
1040 TryMatchToken::Matches(())
1041 } else {
1042 TryMatchToken::Fails(self)
1044 }
1045 }
1046
1047 pub fn as_str(&self) -> &str {
1049 self.token.as_str().trim()
1050 }
1051}
1052
1053#[cfg(test)]
1054mod test {
1055 use super::*;
1056 use crate::runtime::{Runtime, RuntimeBuilder, Template};
1057
1058 #[test]
1059 fn test_parse_literal() {
1060 let nil = LiquidParser::parse(Rule::Literal, "nil")
1061 .unwrap()
1062 .next()
1063 .unwrap();
1064 assert_eq!(parse_literal(nil), Value::Nil);
1065 let nil = LiquidParser::parse(Rule::Literal, "null")
1066 .unwrap()
1067 .next()
1068 .unwrap();
1069 assert_eq!(parse_literal(nil), Value::Nil);
1070
1071 let blank = LiquidParser::parse(Rule::Literal, "blank")
1072 .unwrap()
1073 .next()
1074 .unwrap();
1075 assert_eq!(
1076 parse_literal(blank),
1077 Value::State(crate::model::State::Blank)
1078 );
1079
1080 let empty = LiquidParser::parse(Rule::Literal, "empty")
1081 .unwrap()
1082 .next()
1083 .unwrap();
1084 assert_eq!(
1085 parse_literal(empty),
1086 Value::State(crate::model::State::Empty)
1087 );
1088
1089 let integer = LiquidParser::parse(Rule::Literal, "42")
1090 .unwrap()
1091 .next()
1092 .unwrap();
1093 assert_eq!(parse_literal(integer), Value::scalar(42));
1094
1095 let negative_int = LiquidParser::parse(Rule::Literal, "-42")
1096 .unwrap()
1097 .next()
1098 .unwrap();
1099 assert_eq!(parse_literal(negative_int), Value::scalar(-42));
1100
1101 let float = LiquidParser::parse(Rule::Literal, "4321.032")
1102 .unwrap()
1103 .next()
1104 .unwrap();
1105 assert_eq!(parse_literal(float), Value::scalar(4321.032));
1106
1107 let negative_float = LiquidParser::parse(Rule::Literal, "-4321.032")
1108 .unwrap()
1109 .next()
1110 .unwrap();
1111 assert_eq!(parse_literal(negative_float), Value::scalar(-4321.032));
1112
1113 let boolean = LiquidParser::parse(Rule::Literal, "true")
1114 .unwrap()
1115 .next()
1116 .unwrap();
1117 assert_eq!(parse_literal(boolean), Value::scalar(true));
1118
1119 let string_double_quotes = LiquidParser::parse(Rule::Literal, "\"Hello world!\"")
1120 .unwrap()
1121 .next()
1122 .unwrap();
1123 assert_eq!(
1124 parse_literal(string_double_quotes),
1125 Value::scalar("Hello world!")
1126 );
1127
1128 let string_single_quotes = LiquidParser::parse(Rule::Literal, "'Liquid'")
1129 .unwrap()
1130 .next()
1131 .unwrap();
1132 assert_eq!(parse_literal(string_single_quotes), Value::scalar("Liquid"));
1133 }
1134
1135 #[test]
1136 fn test_parse_variable_pair() {
1137 let variable = LiquidParser::parse(Rule::Variable, "foo[0].bar.baz[foo.bar]")
1138 .unwrap()
1139 .next()
1140 .unwrap();
1141
1142 let indexes = vec![
1143 Expression::Literal(Value::scalar(0)),
1144 Expression::Literal(Value::scalar("bar")),
1145 Expression::Literal(Value::scalar("baz")),
1146 Expression::Variable(Variable::with_literal("foo").push_literal("bar")),
1147 ];
1148
1149 let mut expected = Variable::with_literal("foo");
1150 expected.extend(indexes);
1151
1152 assert_eq!(parse_variable_pair(variable), expected);
1153 }
1154
1155 #[test]
1156 fn test_whitespace_control() {
1157 let options = Language::default();
1158
1159 let runtime = RuntimeBuilder::new().build();
1160 runtime.set_global("exp".into(), Value::scalar(5));
1161
1162 let text = " \n {{ exp }} \n ";
1163 let template = parse(text, &options).map(Template::new).unwrap();
1164 let output = template.render(&runtime).unwrap();
1165
1166 assert_eq!(output, " \n 5 \n ");
1167
1168 let text = " \n {{- exp }} \n ";
1169 let template = parse(text, &options).map(Template::new).unwrap();
1170 let output = template.render(&runtime).unwrap();
1171
1172 assert_eq!(output, "5 \n ");
1173
1174 let text = " \n {{ exp -}} \n ";
1175 let template = parse(text, &options).map(Template::new).unwrap();
1176 let output = template.render(&runtime).unwrap();
1177
1178 assert_eq!(output, " \n 5");
1179
1180 let text = " \n {{- exp -}} \n ";
1181 let template = parse(text, &options).map(Template::new).unwrap();
1182 let output = template.render(&runtime).unwrap();
1183
1184 assert_eq!(output, "5");
1185 }
1186
1187 macro_rules! test_custom_block_tags_impl {
1189 ($start_tag:expr, $end_tag:expr) => {{
1190 use crate::error::ResultLiquidReplaceExt;
1191 use crate::{BlockReflection, ParseBlock};
1192 use std::io::Write;
1193
1194 #[derive(Debug, Default, Copy, Clone)]
1195 struct CustomBlock;
1196 #[derive(Debug)]
1197 struct Custom {
1198 inside: Template,
1199 }
1200
1201 impl BlockReflection for CustomBlock {
1202 fn start_tag(&self) -> &str {
1203 $start_tag
1204 }
1205
1206 fn end_tag(&self) -> &str {
1207 $end_tag
1208 }
1209
1210 fn description(&self) -> &str {
1211 "I am a description"
1212 }
1213 }
1214
1215 impl ParseBlock for CustomBlock {
1216 fn parse(
1217 &self,
1218 mut arguments: TagTokenIter,
1219 mut block: TagBlock,
1220 options: &Language,
1221 ) -> Result<Box<dyn Renderable>> {
1222 arguments.expect_nothing()?;
1223
1224 let inside = block.parse_all(options).map(Template::new)?;
1225
1226 Ok(Box::new(Custom { inside }))
1227 }
1228
1229 fn reflection(&self) -> &dyn BlockReflection {
1230 self
1231 }
1232 }
1233
1234 impl Renderable for Custom {
1235 fn render_to(&self, writer: &mut dyn Write, runtime: &dyn Runtime) -> Result<()> {
1236 write!(writer, "<pre>").replace("Failed to render")?;
1237 self.inside.render_to(writer, runtime)?;
1238 write!(writer, "</pre>").replace("Failed to render")?;
1239
1240 Ok(())
1241 }
1242 }
1243
1244 let mut options = Language::default();
1245 options
1246 .blocks
1247 .register(CustomBlock.start_tag().to_string(), Box::new(CustomBlock));
1248
1249 let runtime = RuntimeBuilder::new().build();
1250
1251 let text = concat!("{% ", $start_tag, " %}Hello Liquid!{% ", $end_tag, " %}");
1252 let template = parse(text, &options).map(Template::new).unwrap();
1253 let output = template.render(&runtime).unwrap();
1254
1255 assert_eq!(output, "<pre>Hello Liquid!</pre>");
1256 }};
1257 }
1258
1259 #[test]
1261 fn test_custom_block_tags() {
1262 test_custom_block_tags_impl!("custom", "endcustom");
1264
1265 test_custom_block_tags_impl!("startcustom", "stopcustom");
1267 }
1268}