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
81fn 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
125fn 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
151fn 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
171fn 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
226fn 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
246pub struct TagBlock<'a: 'b, 'b> {
248 start_tag: &'b str,
249 end_tag: &'b str,
250 iter: &'b mut dyn Iterator<Item = Pair<'a>>,
251 closed: bool,
252}
253
254impl<'a, 'b> TagBlock<'a, 'b> {
255 fn new(
256 start_tag: &'b str,
257 end_tag: &'b str,
258 next_elements: &'b mut dyn Iterator<Item = Pair<'a>>,
259 ) -> Self {
260 TagBlock {
261 start_tag,
262 end_tag,
263 iter: next_elements,
264 closed: false,
265 }
266 }
267
268 #[allow(clippy::should_implement_trait)]
273 pub fn next(&mut self) -> Result<Option<BlockElement<'a>>> {
274 if self.closed {
275 return Ok(None);
276 }
277
278 let element = self.iter.next().expect("File shouldn't end before EOI.");
279
280 if element.as_rule() == Rule::EOI {
281 return error_from_pair(
282 element,
283 format!("Unclosed block. {{% {} %}} tag expected.", self.end_tag),
284 )
285 .into_err();
286 }
287
288 if element.as_rule() == Rule::Tag {
290 let as_str = element.as_str();
291 let mut tag = element
292 .into_inner()
293 .next()
294 .expect("Unwrapping TagInner")
295 .into_inner();
296 let name = tag.next().expect("Tags start by their identifier.");
297 let name_str = name.as_str();
298
299 if name_str == self.end_tag {
301 if let Some(token) = tag.next() {
305 return TagToken::from(token).raise_error().into_err();
306 }
307
308 self.closed = true;
309 return Ok(None);
310 } else {
311 let tokens = TagTokenIter::new(&name, tag);
313 return Ok(Some(BlockElement::Tag(Tag {
314 name,
315 tokens,
316 as_str,
317 })));
318 }
319 }
320 Ok(Some(element.into()))
321 }
322
323 pub fn escape_liquid(&mut self, allow_nesting: bool) -> Result<&'a str> {
338 if self.closed {
339 panic!("`escape_liquid` must be used in an open tag.")
340 }
341
342 let mut nesting_level = 1;
343
344 let mut start_pos = None;
346 let mut end_pos = None;
347
348 #[allow(clippy::while_let_on_iterator)]
349 while let Some(element) = self.iter.next() {
350 let element_as_span = element.as_span();
351 if start_pos.is_none() {
352 start_pos = Some(element_as_span.start_pos());
353 }
354
355 if element.as_rule() == Rule::EOI {
356 return error_from_pair(
357 element,
358 format!("Unclosed block. {{% {} %}} tag expected.", self.end_tag),
359 )
360 .into_err();
361 }
362
363 if element.as_rule() == Rule::Tag {
365 let mut tag = element
366 .into_inner()
367 .next()
368 .expect("Unwrapping TagInner")
369 .into_inner();
370 let name = tag.next().expect("Tags start by their identifier.");
371 let name_str = name.as_str();
372
373 if name_str == self.end_tag {
375 if tag.next().is_none() {
378 nesting_level -= 1;
379 if nesting_level == 0 {
380 self.closed = true;
381 let start_pos = start_pos.expect("Will be `Some` inside this loop.");
382 let output = match end_pos {
383 Some(end_pos) => start_pos.span(&end_pos).as_str(),
384 None => "",
385 };
386
387 return Ok(output);
388 }
389 }
390 } else if name_str == self.start_tag && allow_nesting {
391 nesting_level += 1;
393 }
394 }
395
396 end_pos = Some(element_as_span.end_pos());
397 }
398
399 panic!("Function must eventually find either a Rule::EOI or a closing tag.")
400 }
401
402 pub fn parse_all(&mut self, options: &Language) -> Result<Vec<Box<dyn Renderable>>> {
404 let mut renderables = Vec::new();
405 while let Some(r) = self.parse_next(options)? {
406 renderables.push(r);
407 }
408 Ok(renderables)
409 }
410
411 pub fn parse_next(&mut self, options: &Language) -> Result<Option<Box<dyn Renderable>>> {
415 match self.next()? {
416 None => Ok(None),
417 Some(element) => Ok(Some(element.parse(self, options)?)),
418 }
419 }
420
421 pub fn assert_empty(self) {
426 assert!(
427 self.closed,
428 "Block {{% {} %}} doesn't exhaust its iterator of elements.",
429 self.start_tag
430 )
431 }
432}
433
434pub struct Raw<'a> {
436 text: &'a str,
437}
438impl<'a> From<Pair<'a>> for Raw<'a> {
439 fn from(element: Pair<'a>) -> Self {
440 if element.as_rule() != Rule::Raw {
441 panic!("Only rule Raw can be converted to Raw.");
442 }
443 Raw {
444 text: element.as_str(),
445 }
446 }
447}
448#[allow(clippy::from_over_into)]
449impl<'a> Into<&'a str> for Raw<'a> {
450 fn into(self) -> &'a str {
451 self.as_str()
452 }
453}
454impl<'a> Raw<'a> {
455 pub fn into_renderable(self) -> Box<dyn Renderable> {
457 Box::new(Text::new(self.as_str()))
458 }
459
460 pub fn as_str(&self) -> &'a str {
462 self.text
463 }
464}
465
466pub struct Tag<'a> {
468 name: Pair<'a>,
469 tokens: TagTokenIter<'a>,
470 as_str: &'a str,
471}
472
473impl<'a> From<Pair<'a>> for Tag<'a> {
474 fn from(element: Pair<'a>) -> Self {
475 if element.as_rule() != Rule::Tag {
476 panic!("Only rule Tag can be converted to Tag.");
477 }
478 let as_str = element.as_str();
479 let mut tag = element
480 .into_inner()
481 .next()
482 .expect("Unwrapping TagInner.")
483 .into_inner();
484 let name = tag.next().expect("A tag starts with an identifier.");
485 let tokens = TagTokenIter::new(&name, tag);
486
487 Tag {
488 name,
489 tokens,
490 as_str,
491 }
492 }
493}
494
495impl<'a> Tag<'a> {
496 pub fn new(text: &'a str) -> Result<Self> {
500 let tag = LiquidParser::parse(Rule::Tag, text)
501 .map_err(convert_pest_error)?
502 .next()
503 .ok_or_else(|| Error::with_msg("Tried to create a Tag from an invalid string."))?;
504
505 Ok(tag.into())
506 }
507
508 pub fn name(&self) -> &str {
510 self.name.as_str()
511 }
512
513 pub fn tokens(&mut self) -> &mut TagTokenIter<'a> {
515 &mut self.tokens
516 }
517
518 pub fn into_tokens(self) -> TagTokenIter<'a> {
520 self.tokens
521 }
522
523 pub fn as_str(&self) -> &str {
525 self.as_str
526 }
527
528 pub fn parse(
530 self,
531 tag_block: &mut TagBlock,
532 options: &Language,
533 ) -> Result<Box<dyn Renderable>> {
534 self.parse_pair(&mut tag_block.iter, options)
535 }
536
537 fn parse_pair(
539 self,
540 next_elements: &mut dyn Iterator<Item = Pair>,
541 options: &Language,
542 ) -> Result<Box<dyn Renderable>> {
543 let (name, tokens) = (self.name, self.tokens);
544 let position = name.as_span();
545 let name = name.as_str();
546
547 if let Some(plugin) = options.tags.get(name) {
548 plugin.parse(tokens, options)
549 } else if let Some(plugin) = options.blocks.get(name) {
550 let reflection = plugin.reflection();
551 let block = TagBlock::new(reflection.start_tag(), reflection.end_tag(), next_elements);
552 let renderables = plugin.parse(tokens, block, options)?;
553 Ok(renderables)
554 } else {
555 let pest_error = ::pest::error::Error::new_from_span(
556 ::pest::error::ErrorVariant::CustomError {
557 message: "Unknown tag.".to_string(),
558 },
559 position,
560 );
561 let mut all_tags: Vec<_> = options.tags.plugin_names().collect();
562 all_tags.sort_unstable();
563 let all_tags = itertools::join(all_tags, ", ");
564 let mut all_blocks: Vec<_> = options.blocks.plugin_names().collect();
565 all_blocks.sort_unstable();
566 let all_blocks = itertools::join(all_blocks, ", ");
567 let error = convert_pest_error(pest_error)
568 .context("requested", name.to_owned())
569 .context("available tags", all_tags)
570 .context("available blocks", all_blocks);
571 Err(error)
572 }
573 }
574}
575
576pub struct Exp<'a> {
578 element: Pair<'a>,
579}
580
581impl<'a> From<Pair<'a>> for Exp<'a> {
582 fn from(element: Pair<'a>) -> Self {
583 if element.as_rule() != Rule::Expression {
584 panic!("Only rule Expression can be converted to Expression.");
585 }
586 Exp { element }
587 }
588}
589
590impl<'a> Exp<'a> {
591 pub fn parse(self, options: &Language) -> Result<Box<dyn Renderable>> {
593 let filter_chain = self
594 .element
595 .into_inner()
596 .next()
597 .expect("Unwrapping ExpressionInner")
598 .into_inner()
599 .next()
600 .expect("An expression consists of one filterchain.");
601
602 let filter_chain = parse_filter_chain(filter_chain, options)?;
603 Ok(Box::new(filter_chain))
604 }
605
606 pub fn as_str(&self) -> &str {
608 self.element.as_str()
609 }
610}
611
612pub struct InvalidLiquidToken<'a> {
615 element: Pair<'a>,
616}
617impl<'a> InvalidLiquidToken<'a> {
618 pub fn as_str(&self) -> &str {
621 self.element.as_str()
622 }
623
624 pub fn parse(self, tag_block: &mut TagBlock) -> Result<Box<dyn Renderable>> {
627 self.parse_pair(&mut tag_block.iter)
628 }
629
630 fn parse_pair(
633 self,
634 next_elements: &mut dyn Iterator<Item = Pair>,
635 ) -> Result<Box<dyn Renderable>> {
636 use pest::error::LineColLocation;
637
638 let invalid_token_span = self.element.as_span();
639 let invalid_token_position = invalid_token_span.start_pos();
640 let (offset_l, offset_c) = invalid_token_position.line_col();
641 let offset_l = offset_l - 1;
642 let offset_c = offset_c - 1;
643
644 let end_position = match next_elements.last() {
645 Some(element) => element.as_span().end_pos(),
646 None => invalid_token_span.end_pos(),
647 };
648
649 let mut text = String::from(&invalid_token_position.line_of()[..offset_c]);
650 text.push_str(invalid_token_position.span(&end_position).as_str());
651
652 let mut error = match LiquidParser::parse(Rule::LiquidFile, &text) {
655 Ok(_) => panic!("`LiquidParser::parse` should fail in InvalidLiquidTokens."),
656 Err(error) => error,
657 };
658
659 error.line_col = match error.line_col {
663 LineColLocation::Span((ls, cs), (le, ce)) => {
664 LineColLocation::Span((ls + offset_l, cs), (le + offset_l, ce))
665 }
666 LineColLocation::Pos((ls, cs)) => LineColLocation::Pos((ls + offset_l, cs)),
667 };
668
669 Err(convert_pest_error(error))
670 }
671}
672impl<'a> From<Pair<'a>> for InvalidLiquidToken<'a> {
673 fn from(element: Pair<'a>) -> Self {
674 if element.as_rule() != Rule::InvalidLiquid {
675 panic!("Tried to parse a valid liquid token as invalid.");
676 }
677 InvalidLiquidToken { element }
678 }
679}
680
681pub enum BlockElement<'a> {
685 Raw(Raw<'a>),
686 Tag(Tag<'a>),
687 Expression(Exp<'a>),
688 Invalid(InvalidLiquidToken<'a>),
689}
690impl<'a> From<Pair<'a>> for BlockElement<'a> {
691 fn from(element: Pair<'a>) -> Self {
692 match element.as_rule() {
693 Rule::Raw => BlockElement::Raw(element.into()),
694 Rule::Tag => BlockElement::Tag(element.into()),
695 Rule::Expression => BlockElement::Expression(element.into()),
696 Rule::InvalidLiquid => BlockElement::Invalid(element.into()),
697 _ => panic!(
698 "Only rules Raw | Tag | Expression can be converted to BlockElement. Found {:?}",
699 element.as_rule()
700 ),
701 }
702 }
703}
704
705impl<'a> BlockElement<'a> {
706 pub fn parse(
708 self,
709 block: &mut TagBlock<'a, '_>,
710 options: &Language,
711 ) -> Result<Box<dyn Renderable>> {
712 match self {
713 BlockElement::Raw(raw) => Ok(raw.into_renderable()),
714 BlockElement::Tag(tag) => tag.parse(block, options),
715 BlockElement::Expression(exp) => exp.parse(options),
716 BlockElement::Invalid(invalid) => invalid.parse(block),
717 }
718 }
719
720 fn parse_pair(
722 self,
723 next_elements: &mut dyn Iterator<Item = Pair>,
724 options: &Language,
725 ) -> Result<Box<dyn Renderable>> {
726 match self {
727 BlockElement::Raw(raw) => Ok(raw.into_renderable()),
728 BlockElement::Tag(tag) => tag.parse_pair(next_elements, options),
729 BlockElement::Expression(exp) => exp.parse(options),
730 BlockElement::Invalid(invalid) => invalid.parse_pair(next_elements),
731 }
732 }
733
734 pub fn as_str(&self) -> &str {
736 match self {
737 BlockElement::Raw(raw) => raw.as_str(),
738 BlockElement::Tag(tag) => tag.as_str(),
739 BlockElement::Expression(exp) => exp.as_str(),
740 BlockElement::Invalid(invalid) => invalid.as_str(),
741 }
742 }
743}
744
745pub struct TagTokenIter<'a> {
749 iter: Box<dyn Iterator<Item = TagToken<'a>> + 'a>,
750 position: ::pest::Position<'a>,
751}
752impl<'a> Iterator for TagTokenIter<'a> {
753 type Item = TagToken<'a>;
754 fn next(&mut self) -> Option<Self::Item> {
755 self.iter.next().map(|next| {
756 self.position = next.token.as_span().end_pos();
757 next
758 })
759 }
760}
761impl<'a> TagTokenIter<'a> {
762 fn new(name: &Pair<'a>, tokens: Pairs<'a>) -> Self {
763 TagTokenIter {
764 iter: Box::new(tokens.map(TagToken::from)),
765 position: name.as_span().end_pos(),
766 }
767 }
768
769 pub fn raise_error(&mut self, error_msg: &str) -> Error {
772 let pest_error = ::pest::error::Error::new_from_pos(
773 ::pest::error::ErrorVariant::CustomError {
774 message: error_msg.to_string(),
775 },
776 self.position.to_owned(),
777 );
778 convert_pest_error(pest_error)
779 }
780
781 pub fn expect_next(&mut self, error_msg: &str) -> Result<TagToken<'a>> {
783 self.next().ok_or_else(|| self.raise_error(error_msg))
784 }
785
786 pub fn expect_nothing(&mut self) -> Result<()> {
788 if let Some(token) = self.next() {
789 Err(token.raise_error())
790 } else {
791 Ok(())
792 }
793 }
794}
795
796pub enum TryMatchToken<'a, T> {
801 Matches(T),
802 Fails(TagToken<'a>),
803}
804
805impl<'a, T> TryMatchToken<'a, T> {
806 pub fn into_result(self) -> Result<T> {
807 match self {
808 TryMatchToken::Matches(t) => Ok(t),
809 TryMatchToken::Fails(t) => Err(t.raise_error()),
810 }
811 }
812
813 pub fn into_result_custom_msg(self, msg: &str) -> Result<T> {
814 match self {
815 TryMatchToken::Matches(t) => Ok(t),
816 TryMatchToken::Fails(t) => Err(t.raise_custom_error(msg)),
817 }
818 }
819}
820
821pub struct TagToken<'a> {
823 token: Pair<'a>,
824 expected: Vec<Rule>,
825}
826
827impl<'a> From<Pair<'a>> for TagToken<'a> {
828 fn from(token: Pair<'a>) -> Self {
829 TagToken {
830 token,
831 expected: Vec::new(),
832 }
833 }
834}
835
836impl<'a> TagToken<'a> {
837 pub fn raise_error(self) -> Error {
847 let pest_error = ::pest::error::Error::new_from_span(
848 ::pest::error::ErrorVariant::ParsingError {
849 positives: self.expected,
850 negatives: vec![self.token.as_rule()],
851 },
852 self.token.as_span(),
853 );
854 convert_pest_error(pest_error)
855 }
856
857 pub fn raise_custom_error(self, msg: &str) -> Error {
861 let pest_error = ::pest::error::Error::new_from_span(
862 ::pest::error::ErrorVariant::CustomError {
863 message: msg.to_string(),
864 },
865 self.token.as_span(),
866 );
867 convert_pest_error(pest_error)
868 }
869
870 fn unwrap_filter_chain(&mut self) -> std::result::Result<Pair<'a>, ()> {
871 let token = self.token.clone();
872
873 if token.as_rule() != Rule::FilterChain {
874 return Err(());
875 }
876
877 Ok(token)
878 }
879
880 fn unwrap_value(&mut self) -> std::result::Result<Pair<'a>, ()> {
881 let filterchain = self.unwrap_filter_chain()?;
882
883 let mut chain = filterchain.into_inner();
884 let value = chain.next().expect("Unwrapping value out of Filterchain.");
885 if chain.next().is_some() {
886 return Err(());
888 }
889
890 Ok(value)
891 }
892
893 fn unwrap_variable(&mut self) -> std::result::Result<Pair<'a>, ()> {
894 let value = self.unwrap_value()?;
895
896 let variable = value
897 .into_inner()
898 .next()
899 .expect("A value is made of one token.");
900
901 if variable.as_rule() != Rule::Variable {
902 return Err(());
903 }
904
905 Ok(variable)
906 }
907
908 fn unwrap_identifier(&mut self) -> std::result::Result<Pair<'a>, ()> {
909 let variable = self.unwrap_variable()?;
910
911 let mut indexes = variable.into_inner();
912 let identifier = indexes
913 .next()
914 .expect("Unwrapping identifier out of variable.");
915 if indexes.next().is_some() {
916 return Err(());
918 }
919
920 Ok(identifier)
921 }
922
923 fn unwrap_literal(&mut self) -> std::result::Result<Pair<'a>, ()> {
924 let value = self.unwrap_value()?;
925
926 let literal = value
927 .into_inner()
928 .next()
929 .expect("A value is made of one token.");
930
931 if literal.as_rule() != Rule::Literal {
932 return Err(());
933 }
934
935 Ok(literal)
936 }
937
938 pub fn expect_filter_chain(mut self, options: &Language) -> TryMatchToken<'a, FilterChain> {
940 match self.expect_filter_chain_err(options) {
941 Ok(t) => TryMatchToken::Matches(t),
942 Err(_) => {
943 self.expected.push(Rule::FilterChain);
944 TryMatchToken::Fails(self)
945 }
946 }
947 }
948
949 fn expect_filter_chain_err(&mut self, options: &Language) -> Result<FilterChain> {
950 let t = self
951 .unwrap_filter_chain()
952 .map_err(|_| Error::with_msg("failed to parse"))?;
953 let f = parse_filter_chain(t, options)?;
954 Ok(f)
955 }
956
957 pub fn expect_value(mut self) -> TryMatchToken<'a, Expression> {
962 match self.unwrap_value() {
963 Ok(t) => TryMatchToken::Matches(parse_value(t)),
964 Err(_) => {
965 self.expected.push(Rule::Value);
966 TryMatchToken::Fails(self)
967 }
968 }
969 }
970
971 pub fn expect_variable(mut self) -> TryMatchToken<'a, Variable> {
973 match self.unwrap_variable() {
974 Ok(t) => TryMatchToken::Matches(parse_variable(t)),
975 Err(_) => {
976 self.expected.push(Rule::Variable);
977 TryMatchToken::Fails(self)
978 }
979 }
980 }
981
982 pub fn expect_identifier(mut self) -> TryMatchToken<'a, &'a str> {
986 match self.unwrap_identifier() {
987 Ok(t) => TryMatchToken::Matches(t.as_str()),
988 Err(_) => {
989 self.expected.push(Rule::Identifier);
990 TryMatchToken::Fails(self)
991 }
992 }
993 }
994
995 pub fn expect_literal(mut self) -> TryMatchToken<'a, Value> {
999 match self.unwrap_literal() {
1000 Ok(t) => TryMatchToken::Matches(parse_literal(t)),
1001 Err(_) => {
1002 self.expected.push(Rule::Literal);
1003 TryMatchToken::Fails(self)
1004 }
1005 }
1006 }
1007 pub fn expect_range(mut self) -> TryMatchToken<'a, (Expression, Expression)> {
1011 let token = self.token.clone();
1012
1013 if token.as_rule() != Rule::Range {
1014 self.expected.push(Rule::Range);
1015 return TryMatchToken::Fails(self);
1016 }
1017
1018 let mut range = token.into_inner();
1019 TryMatchToken::Matches((
1020 parse_value(range.next().expect("start")),
1021 parse_value(range.next().expect("end")),
1022 ))
1023 }
1024
1025 pub fn expect_str(self, expected: &str) -> TryMatchToken<'a, ()> {
1027 if self.as_str() == expected {
1028 TryMatchToken::Matches(())
1029 } else {
1030 TryMatchToken::Fails(self)
1032 }
1033 }
1034
1035 pub fn as_str(&self) -> &str {
1037 self.token.as_str().trim()
1038 }
1039}
1040
1041#[cfg(test)]
1042mod test {
1043 use super::*;
1044 use crate::runtime::{Runtime, RuntimeBuilder, Template};
1045
1046 #[test]
1047 fn test_parse_literal() {
1048 let nil = LiquidParser::parse(Rule::Literal, "nil")
1049 .unwrap()
1050 .next()
1051 .unwrap();
1052 assert_eq!(parse_literal(nil), Value::Nil);
1053 let nil = LiquidParser::parse(Rule::Literal, "null")
1054 .unwrap()
1055 .next()
1056 .unwrap();
1057 assert_eq!(parse_literal(nil), Value::Nil);
1058
1059 let blank = LiquidParser::parse(Rule::Literal, "blank")
1060 .unwrap()
1061 .next()
1062 .unwrap();
1063 assert_eq!(
1064 parse_literal(blank),
1065 Value::State(crate::model::State::Blank)
1066 );
1067
1068 let empty = LiquidParser::parse(Rule::Literal, "empty")
1069 .unwrap()
1070 .next()
1071 .unwrap();
1072 assert_eq!(
1073 parse_literal(empty),
1074 Value::State(crate::model::State::Empty)
1075 );
1076
1077 let integer = LiquidParser::parse(Rule::Literal, "42")
1078 .unwrap()
1079 .next()
1080 .unwrap();
1081 assert_eq!(parse_literal(integer), Value::scalar(42));
1082
1083 let negative_int = LiquidParser::parse(Rule::Literal, "-42")
1084 .unwrap()
1085 .next()
1086 .unwrap();
1087 assert_eq!(parse_literal(negative_int), Value::scalar(-42));
1088
1089 let float = LiquidParser::parse(Rule::Literal, "4321.032")
1090 .unwrap()
1091 .next()
1092 .unwrap();
1093 assert_eq!(parse_literal(float), Value::scalar(4321.032));
1094
1095 let negative_float = LiquidParser::parse(Rule::Literal, "-4321.032")
1096 .unwrap()
1097 .next()
1098 .unwrap();
1099 assert_eq!(parse_literal(negative_float), Value::scalar(-4321.032));
1100
1101 let boolean = LiquidParser::parse(Rule::Literal, "true")
1102 .unwrap()
1103 .next()
1104 .unwrap();
1105 assert_eq!(parse_literal(boolean), Value::scalar(true));
1106
1107 let string_double_quotes = LiquidParser::parse(Rule::Literal, "\"Hello world!\"")
1108 .unwrap()
1109 .next()
1110 .unwrap();
1111 assert_eq!(
1112 parse_literal(string_double_quotes),
1113 Value::scalar("Hello world!")
1114 );
1115
1116 let string_single_quotes = LiquidParser::parse(Rule::Literal, "'Liquid'")
1117 .unwrap()
1118 .next()
1119 .unwrap();
1120 assert_eq!(parse_literal(string_single_quotes), Value::scalar("Liquid"));
1121 }
1122
1123 #[test]
1124 fn test_parse_variable() {
1125 let variable = LiquidParser::parse(Rule::Variable, "foo[0].bar.baz[foo.bar]")
1126 .unwrap()
1127 .next()
1128 .unwrap();
1129
1130 let indexes = vec![
1131 Expression::Literal(Value::scalar(0)),
1132 Expression::Literal(Value::scalar("bar")),
1133 Expression::Literal(Value::scalar("baz")),
1134 Expression::Variable(Variable::with_literal("foo").push_literal("bar")),
1135 ];
1136
1137 let mut expected = Variable::with_literal("foo");
1138 expected.extend(indexes);
1139
1140 assert_eq!(parse_variable(variable), expected);
1141 }
1142
1143 #[test]
1144 fn test_whitespace_control() {
1145 let options = Language::default();
1146
1147 let runtime = RuntimeBuilder::new().build();
1148 runtime.set_global("exp".into(), Value::scalar(5));
1149
1150 let text = " \n {{ exp }} \n ";
1151 let template = parse(text, &options).map(Template::new).unwrap();
1152 let output = template.render(&runtime).unwrap();
1153
1154 assert_eq!(output, " \n 5 \n ");
1155
1156 let text = " \n {{- exp }} \n ";
1157 let template = parse(text, &options).map(Template::new).unwrap();
1158 let output = template.render(&runtime).unwrap();
1159
1160 assert_eq!(output, "5 \n ");
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");
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");
1173 }
1174
1175 macro_rules! test_custom_block_tags_impl {
1177 ($start_tag:expr, $end_tag:expr) => {{
1178 use crate::error::ResultLiquidReplaceExt;
1179 use crate::{BlockReflection, ParseBlock};
1180 use std::io::Write;
1181
1182 #[derive(Debug, Default, Copy, Clone)]
1183 struct CustomBlock;
1184 #[derive(Debug)]
1185 struct Custom {
1186 inside: Template,
1187 }
1188
1189 impl BlockReflection for CustomBlock {
1190 fn start_tag(&self) -> &str {
1191 $start_tag
1192 }
1193
1194 fn end_tag(&self) -> &str {
1195 $end_tag
1196 }
1197
1198 fn description(&self) -> &str {
1199 "I am a description"
1200 }
1201 }
1202
1203 impl ParseBlock for CustomBlock {
1204 fn parse(
1205 &self,
1206 mut arguments: TagTokenIter,
1207 mut block: TagBlock,
1208 options: &Language,
1209 ) -> Result<Box<dyn Renderable>> {
1210 arguments.expect_nothing()?;
1211
1212 let inside = block.parse_all(options).map(Template::new)?;
1213
1214 Ok(Box::new(Custom { inside }))
1215 }
1216
1217 fn reflection(&self) -> &dyn BlockReflection {
1218 self
1219 }
1220 }
1221
1222 impl Renderable for Custom {
1223 fn render_to(&self, writer: &mut dyn Write, runtime: &dyn Runtime) -> Result<()> {
1224 write!(writer, "<pre>").replace("Failed to render")?;
1225 self.inside.render_to(writer, runtime)?;
1226 write!(writer, "</pre>").replace("Failed to render")?;
1227
1228 Ok(())
1229 }
1230 }
1231
1232 let mut options = Language::default();
1233 options
1234 .blocks
1235 .register(CustomBlock.start_tag().to_string(), Box::new(CustomBlock));
1236
1237 let runtime = RuntimeBuilder::new().build();
1238
1239 let text = concat!("{% ", $start_tag, " %}Hello Liquid!{% ", $end_tag, " %}");
1240 let template = parse(text, &options).map(Template::new).unwrap();
1241 let output = template.render(&runtime).unwrap();
1242
1243 assert_eq!(output, "<pre>Hello Liquid!</pre>");
1244 }};
1245 }
1246
1247 #[test]
1249 fn test_custom_block_tags() {
1250 test_custom_block_tags_impl!("custom", "endcustom");
1252
1253 test_custom_block_tags_impl!("startcustom", "stopcustom");
1255 }
1256}