aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askama_shared/Cargo.toml4
-rw-r--r--askama_shared/src/generator.rs60
-rw-r--r--askama_shared/src/heritage.rs2
-rw-r--r--askama_shared/src/parser.rs247
-rw-r--r--book/src/filters.md4
-rw-r--r--testing/templates/if-coerce.html51
-rw-r--r--testing/templates/macro-short-circuit.html9
-rw-r--r--testing/tests/coerce.rs14
-rw-r--r--testing/tests/gen_ws_tests.py186
-rw-r--r--testing/tests/macro.rs10
-rw-r--r--testing/tests/matches.rs22
-rw-r--r--testing/tests/operators.rs10
-rw-r--r--testing/tests/simple.rs9
-rw-r--r--testing/tests/whitespace.rs2
-rw-r--r--testing/tests/ws.rs214
15 files changed, 760 insertions, 84 deletions
diff --git a/askama_shared/Cargo.toml b/askama_shared/Cargo.toml
index f5ac4b6..fdc5a4c 100644
--- a/askama_shared/Cargo.toml
+++ b/askama_shared/Cargo.toml
@@ -19,9 +19,7 @@ yaml = ["serde", "serde_yaml"]
[dependencies]
askama_escape = { version = "0.10", path = "../askama_escape" }
humansize = { version = "1.1.0", optional = true }
-# default for features for nom don't work result in linker errors:
-# https://github.com/rust-lang/rust/issues/62146
-nom = { version = "6", features = ["std"] }
+nom = { version = "6", features = ["std"], default-features = false }
num-traits = { version = "0.2.6", optional = true }
proc-macro2 = "1"
quote = "1"
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs
index 3b73170..146f9db 100644
--- a/askama_shared/src/generator.rs
+++ b/askama_shared/src/generator.rs
@@ -444,8 +444,8 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
Node::Cond(ref conds, ws) => {
self.write_cond(ctx, buf, conds, ws)?;
}
- Node::Match(ws1, ref expr, inter, ref arms, ws2) => {
- self.write_match(ctx, buf, ws1, expr, inter, arms, ws2)?;
+ Node::Match(ws1, ref expr, ref arms, ws2) => {
+ self.write_match(ctx, buf, ws1, expr, arms, ws2)?;
}
Node::Loop(ws1, ref var, ref iter, ref body, ws2) => {
self.write_loop(ctx, buf, ws1, var, iter, body, ws2)?;
@@ -505,8 +505,9 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
let mut has_else = false;
for (i, &(cws, ref cond, ref nodes)) in conds.iter().enumerate() {
self.handle_ws(cws);
- if arm_sizes.is_empty() {
- flushed += self.write_buf_writable(buf)?;
+ flushed += self.write_buf_writable(buf)?;
+ if i > 0 {
+ self.locals.pop();
}
let mut arm_size = 0;
@@ -518,8 +519,15 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
buf.dedent()?;
buf.write("} else if ");
}
+ // The following syntax `*(&(...) as &bool)` is used to
+ // trigger Rust's automatic dereferencing, to coerce
+ // e.g. `&&&&&bool` to `bool`. First `&(...) as &bool`
+ // coerces e.g. `&&&bool` to `&bool`. Then `*(&bool)`
+ // finally dereferences it to `bool`.
+ buf.write("*(&(");
let expr_code = self.visit_expr_root(expr)?;
buf.write(&expr_code);
+ buf.write(") as &bool)");
}
None => {
buf.dedent()?;
@@ -532,14 +540,14 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
self.locals.push();
arm_size += self.handle(ctx, nodes, buf, AstLevel::Nested)?;
- arm_size += self.write_buf_writable(buf)?;
arm_sizes.push(arm_size);
-
- self.locals.pop();
}
self.handle_ws(ws);
+ flushed += self.write_buf_writable(buf)?;
buf.writeln("}")?;
+ self.locals.pop();
+
if !has_else {
arm_sizes.push(0);
}
@@ -553,23 +561,28 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
buf: &mut Buffer,
ws1: WS,
expr: &Expr,
- inter: Option<&'a str>,
arms: &'a [When],
ws2: WS,
) -> Result<usize, CompileError> {
self.flush_ws(ws1);
let flushed = self.write_buf_writable(buf)?;
let mut arm_sizes = Vec::new();
- if let Some(inter) = inter {
- if !inter.is_empty() {
- self.next_ws = Some(inter);
- }
- }
let expr_code = self.visit_expr_root(expr)?;
buf.writeln(&format!("match &{} {{", expr_code))?;
- for arm in arms {
+
+ let mut arm_size = 0;
+ for (i, arm) in arms.iter().enumerate() {
let &(ws, ref variant, ref params, ref body) = arm;
+ self.handle_ws(ws);
+
+ if i > 0 {
+ arm_sizes.push(arm_size + self.write_buf_writable(buf)?);
+
+ buf.writeln("}")?;
+ self.locals.pop();
+ }
+
self.locals.push();
match *variant {
Some(ref param) => {
@@ -616,15 +629,17 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
}
}
buf.writeln(" => {")?;
- self.handle_ws(ws);
- let arm_size = self.handle(ctx, body, buf, AstLevel::Nested)?;
- arm_sizes.push(arm_size + self.write_buf_writable(buf)?);
- buf.writeln("}")?;
- self.locals.pop();
+
+ arm_size = self.handle(ctx, body, buf, AstLevel::Nested)?;
}
- buf.writeln("}")?;
self.handle_ws(ws2);
+ arm_sizes.push(arm_size + self.write_buf_writable(buf)?);
+ buf.writeln("}")?;
+ self.locals.pop();
+
+ buf.writeln("}")?;
+
Ok(flushed + median(&mut arm_sizes))
}
@@ -717,10 +732,11 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
}
names.write(arg);
- values.write("&");
+ values.write("&(");
values.write(&self.visit_expr_root(args.get(i).ok_or_else(|| {
CompileError::String(format!("macro '{}' takes more than {} arguments", name, i))
})?)?);
+ values.write(")");
self.locals.insert(arg);
}
@@ -944,7 +960,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
let expression = match wrapped {
Wrapped => expr_buf.buf,
Unwrapped => format!(
- "::askama::MarkupDisplay::new_unsafe(&{}, {})",
+ "::askama::MarkupDisplay::new_unsafe(&({}), {})",
expr_buf.buf, self.input.escaper
),
};
diff --git a/askama_shared/src/heritage.rs b/askama_shared/src/heritage.rs
index bc9e701..87f23e0 100644
--- a/askama_shared/src/heritage.rs
+++ b/askama_shared/src/heritage.rs
@@ -89,7 +89,7 @@ impl<'a> Context<'a> {
Node::Loop(_, _, _, nodes, _) => {
nested.push(nodes);
}
- Node::Match(_, _, _, arms, _) => {
+ Node::Match(_, _, arms, _) => {
for (_, _, _, arm) in arms {
nested.push(arm);
}
diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs
index 9344c07..a40e967 100644
--- a/askama_shared/src/parser.rs
+++ b/askama_shared/src/parser.rs
@@ -1,7 +1,7 @@
use nom::branch::alt;
use nom::bytes::complete::{escaped, is_not, tag, take_until};
use nom::character::complete::{anychar, char, digit1};
-use nom::combinator::{complete, map, opt};
+use nom::combinator::{complete, map, opt, value};
use nom::error::ParseError;
use nom::multi::{many0, many1, separated_list0, separated_list1};
use nom::sequence::{delimited, pair, tuple};
@@ -19,7 +19,7 @@ pub enum Node<'a> {
LetDecl(WS, Target<'a>),
Let(WS, Target<'a>, Expr<'a>),
Cond(Vec<(WS, Option<Expr<'a>>, Vec<Node<'a>>)>, WS),
- Match(WS, Expr<'a>, Option<&'a str>, Vec<When<'a>>, WS),
+ Match(WS, Expr<'a>, Vec<When<'a>>, WS),
Loop(WS, Target<'a>, Expr<'a>, Vec<Node<'a>>, WS),
Extends(Expr<'a>),
BlockDef(WS, &'a str, Vec<Node<'a>>, WS),
@@ -312,10 +312,12 @@ fn expr_var_call(i: &[u8]) -> IResult<&[u8], Expr> {
}
fn path(i: &[u8]) -> IResult<&[u8], Vec<&str>> {
+ let root = opt(value("", ws(tag("::"))));
let tail = separated_list1(ws(tag("::")), identifier);
- let (i, (start, _, rest)) = tuple((identifier, ws(tag("::")), tail))(i)?;
-
- let mut path = vec![start];
+ let (i, (root, start, _, rest)) = tuple((root, identifier, ws(tag("::")), tail))(i)?;
+ let mut path = Vec::new();
+ path.extend(root);
+ path.push(start);
path.extend(rest);
Ok((i, path))
}
@@ -573,36 +575,32 @@ fn expr_rust_macro(i: &[u8]) -> IResult<&[u8], Expr> {
macro_rules! expr_prec_layer {
( $name:ident, $inner:ident, $op:expr ) => {
fn $name(i: &[u8]) -> IResult<&[u8], Expr> {
- let (i, (left, op_and_right)) = tuple((
+ let (i, left) = $inner(i)?;
+ let (i, right) = many0(pair(
+ ws(tag($op)),
$inner,
- opt(pair(
- ws(tag($op)),
- expr_any,
- ))
))(i)?;
- Ok((i, match op_and_right {
- Some((op, right)) => Expr::BinOp(
- str::from_utf8(op).unwrap(), Box::new(left), Box::new(right)
- ),
- None => left,
- }))
+ Ok((
+ i,
+ right.into_iter().fold(left, |left, (op, right)| {
+ Expr::BinOp(str::from_utf8(op).unwrap(), Box::new(left), Box::new(right))
+ }),
+ ))
}
};
( $name:ident, $inner:ident, $( $op:expr ),+ ) => {
fn $name(i: &[u8]) -> IResult<&[u8], Expr> {
- let (i, (left, op_and_right)) = tuple((
+ let (i, left) = $inner(i)?;
+ let (i, right) = many0(pair(
+ ws(alt(($( tag($op) ),*,))),
$inner,
- opt(pair(
- ws(alt(($( tag($op) ),*,))),
- expr_any
- ))
))(i)?;
- Ok((i, match op_and_right {
- Some((op, right)) => Expr::BinOp(
- str::from_utf8(op).unwrap(), Box::new(left), Box::new(right)
- ),
- None => left,
- }))
+ Ok((
+ i,
+ right.into_iter().fold(left, |left, (op, right)| {
+ Expr::BinOp(str::from_utf8(op).unwrap(), Box::new(left), Box::new(right))
+ }),
+ ))
}
}
}
@@ -773,8 +771,8 @@ fn block_match<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>
arms.push(arm);
}
- let inter = match inter {
- Some(Node::Lit(lws, val, rws)) => {
+ match inter {
+ Some(Node::Lit(_, val, rws)) => {
assert!(
val.is_empty(),
"only whitespace allowed between match and first when, found {}",
@@ -785,18 +783,16 @@ fn block_match<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>
"only whitespace allowed between match and first when, found {}",
rws
);
- Some(lws)
}
- None => None,
+ None => {}
_ => panic!("only literals allowed between match and first when"),
- };
+ }
Ok((
i,
Node::Match(
WS(pws1.is_some(), nws1.is_some()),
expr,
- inter,
arms,
WS(pws2.is_some(), nws2.is_some()),
),
@@ -1078,12 +1074,13 @@ pub fn parse<'a>(src: &'a str, syntax: &'a Syntax<'a>) -> Result<Vec<Node<'a>>,
#[cfg(test)]
mod tests {
+ use super::{Expr, Node, WS};
use crate::Syntax;
fn check_ws_split(s: &str, res: &(&str, &str, &str)) {
let node = super::split_ws_parts(s.as_bytes());
match node {
- super::Node::Lit(lws, s, rws) => {
+ Node::Lit(lws, s, rws) => {
assert_eq!(lws, res.0);
assert_eq!(s, res.1);
assert_eq!(rws, res.2);
@@ -1118,12 +1115,9 @@ mod tests {
fn test_parse_var_call() {
assert_eq!(
super::parse("{{ function(\"123\", 3) }}", &Syntax::default()).unwrap(),
- vec![super::Node::Expr(
- super::WS(false, false),
- super::Expr::VarCall(
- "function",
- vec![super::Expr::StrLit("123"), super::Expr::NumLit("3")]
- ),
+ vec![Node::Expr(
+ WS(false, false),
+ Expr::VarCall("function", vec![Expr::StrLit("123"), Expr::NumLit("3")]),
)],
);
}
@@ -1132,17 +1126,36 @@ mod tests {
fn test_parse_path_call() {
assert_eq!(
super::parse("{{ self::function(\"123\", 3) }}", &Syntax::default()).unwrap(),
- vec![super::Node::Expr(
- super::WS(false, false),
- super::Expr::PathCall(
+ vec![Node::Expr(
+ WS(false, false),
+ Expr::PathCall(
vec!["self", "function"],
- vec![super::Expr::StrLit("123"), super::Expr::NumLit("3")],
+ vec![Expr::StrLit("123"), Expr::NumLit("3")],
),
)],
);
}
#[test]
+ fn test_parse_root_path() {
+ let syntax = Syntax::default();
+ assert_eq!(
+ super::parse("{{ std::string::String::new() }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ WS(false, false),
+ Expr::PathCall(vec!["std", "string", "String", "new"], vec![]),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ ::std::string::String::new() }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ WS(false, false),
+ Expr::PathCall(vec!["", "std", "string", "String", "new"], vec![]),
+ )],
+ );
+ }
+
+ #[test]
fn change_delimiters_parse_filter() {
let syntax = Syntax {
expr_start: "{~",
@@ -1152,6 +1165,152 @@ mod tests {
super::parse("{~ strvar|e ~}", &syntax).unwrap();
}
+
+ #[test]
+ fn test_precedence() {
+ use Expr::*;
+ let syntax = Syntax::default();
+ assert_eq!(
+ super::parse("{{ a + b == c }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ WS(false, false),
+ BinOp(
+ "==",
+ BinOp("+", Var("a").into(), Var("b").into()).into(),
+ Var("c").into(),
+ )
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ a + b * c - d / e }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ WS(false, false),
+ BinOp(
+ "-",
+ BinOp(
+ "+",
+ Var("a").into(),
+ BinOp("*", Var("b").into(), Var("c").into()).into(),
+ )
+ .into(),
+ BinOp("/", Var("d").into(), Var("e").into()).into(),
+ )
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ a * (b + c) / -d }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ WS(false, false),
+ BinOp(
+ "/",
+ BinOp(
+ "*",
+ Var("a").into(),
+ Group(BinOp("+", Var("b").into(), Var("c").into()).into()).into()
+ )
+ .into(),
+ Unary("-", Var("d").into()).into()
+ )
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ a || b && c || d && e }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ WS(false, false),
+ BinOp(
+ "||",
+ BinOp(
+ "||",
+ Var("a").into(),
+ BinOp("&&", Var("b").into(), Var("c").into()).into(),
+ )
+ .into(),
+ BinOp("&&", Var("d").into(), Var("e").into()).into(),
+ )
+ )],
+ );
+ }
+
+ #[test]
+ fn test_associativity() {
+ use Expr::*;
+ let syntax = Syntax::default();
+ assert_eq!(
+ super::parse("{{ a + b + c }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ WS(false, false),
+ BinOp(
+ "+",
+ BinOp("+", Var("a").into(), Var("b").into()).into(),
+ Var("c").into()
+ )
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ a * b * c }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ WS(false, false),
+ BinOp(
+ "*",
+ BinOp("*", Var("a").into(), Var("b").into()).into(),
+ Var("c").into()
+ )
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ a && b && c }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ WS(false, false),
+ BinOp(
+ "&&",
+ BinOp("&&", Var("a").into(), Var("b").into()).into(),
+ Var("c").into()
+ )
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ a + b - c + d }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ WS(false, false),
+ BinOp(
+ "+",
+ BinOp(
+ "-",
+ BinOp("+", Var("a").into(), Var("b").into()).into(),
+ Var("c").into()
+ )
+ .into(),
+ Var("d").into()
+ )
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ a == b != c > d > e == f }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ WS(false, false),
+ BinOp(
+ "==",
+ BinOp(
+ ">",
+ BinOp(
+ ">",
+ BinOp(
+ "!=",
+ BinOp("==", Var("a").into(), Var("b").into()).into(),
+ Var("c").into()
+ )
+ .into(),
+ Var("d").into()
+ )
+ .into(),
+ Var("e").into()
+ )
+ .into(),
+ Var("f").into()
+ )
+ )],
+ );
+ }
}
type ParserError<'a, T> = Result<(&'a [u8], T), nom::Err<nom::error::Error<&'a [u8]>>>;
diff --git a/book/src/filters.md b/book/src/filters.md
index 25fbbe1..47fafdb 100644
--- a/book/src/filters.md
+++ b/book/src/filters.md
@@ -258,7 +258,7 @@ fn main() {
## The `json` filter
-Enabling the `serde-json` filter will enable the use of the `json` filter.
+Enabling the `serde-json` feature will enable the use of the `json` filter.
This will output formatted JSON for any value that implements the required
`Serialize` trait.
@@ -271,7 +271,7 @@ This will output formatted JSON for any value that implements the required
## The `yaml` filter
-Enabling the `serde-yaml` filter will enable the use of the `yaml` filter.
+Enabling the `serde-yaml` feature will enable the use of the `yaml` filter.
This will output formatted JSON for any value that implements the required
`Serialize` trait.
diff --git a/testing/templates/if-coerce.html b/testing/templates/if-coerce.html
new file mode 100644
index 0000000..e32c2fe
--- /dev/null
+++ b/testing/templates/if-coerce.html
@@ -0,0 +1,51 @@
+{% macro foo(b) -%}
+ {% if b %}t{% else %}f{% endif -%}
+{% endmacro -%}
+
+{% macro bar(b) -%}
+ {%- call foo(b) -%}
+{% endmacro -%}
+
+{% macro baz(b) -%}
+ {%- call bar(b) -%}
+{% endmacro -%}
+
+{% macro qux(b) -%}
+ {%- call baz(b) -%}
+{% endmacro -%}
+
+{%- call foo(false) -%}
+{%- call bar(true) -%}
+{%- call baz(false) -%}
+{%- call qux(true) -%}
+
+{%- call qux(true && false) -%}
+{%- call qux(false || true) -%}
+
+{%- call qux(self.t) -%}
+{%- call qux(self.f) -%}
+{%- call qux(self.f || self.t) -%}
+
+{%- if false -%}
+if
+{%- else if false || true -%}
+elseif
+{%- else -%}
+else
+{%- endif -%}
+
+{%- if true && false -%}
+if
+{%- else if false -%}
+elseif
+{%- else -%}
+else
+{%- endif -%}
+
+{%- if false || true -%}
+if
+{%- else if (true && false) -%}
+elseif
+{%- else -%}
+else
+{%- endif -%}
diff --git a/testing/templates/macro-short-circuit.html b/testing/templates/macro-short-circuit.html
new file mode 100644
index 0000000..4a19b86
--- /dev/null
+++ b/testing/templates/macro-short-circuit.html
@@ -0,0 +1,9 @@
+{% macro foo(b) -%}
+ {{ b }}
+{%- endmacro -%}
+{% call foo(true) -%}
+{% call foo(true && true) -%}
+{% call foo(true && true && true) -%}
+{% call foo(false) -%}
+{% call foo(false || true) -%}
+{% call foo(false || false || true) -%}
diff --git a/testing/tests/coerce.rs b/testing/tests/coerce.rs
new file mode 100644
index 0000000..1a44781
--- /dev/null
+++ b/testing/tests/coerce.rs
@@ -0,0 +1,14 @@
+use askama::Template;
+
+#[derive(Template)]
+#[template(path = "if-coerce.html")]
+struct IfCoerceTemplate {
+ t: bool,
+ f: bool,
+}
+
+#[test]
+fn test_coerce() {
+ let t = IfCoerceTemplate { t: true, f: false };
+ assert_eq!(t.render().unwrap(), "ftftfttftelseifelseif");
+}
diff --git a/testing/tests/gen_ws_tests.py b/testing/tests/gen_ws_tests.py
new file mode 100644
index 0000000..c2e24cb
--- /dev/null
+++ b/testing/tests/gen_ws_tests.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from itertools import product, chain, zip_longest, tee
+
+
+# The amount of branches to generate
+BRANCHES = 2 # branches
+
+IF, ELSE_IF, ELSE, END_IF = 0, 1, 2, 3
+
+NL = "\\n"
+dash = lambda ws: [" ", "-"][ws]
+
+
+def trim(s, ws):
+ if ws[0]:
+ s = s.lstrip()
+ if ws[1]:
+ s = s.rstrip()
+ return s
+
+
+def cond_kind(i, n):
+ i += 1
+ if i == 1:
+ return IF # if
+ elif (i == n) and (i > 1):
+ return ELSE # else
+ elif i > n:
+ return END_IF # endif
+ else:
+ return ELSE_IF # else if
+
+
+# From: https://docs.python.org/3/library/itertools.html#itertools-recipes
+def pairwise(iterable):
+ a, b = tee(iterable)
+ next(b, None)
+ return zip(a, b)
+
+
+def write_cond(conds, active_branch):
+ n = len(conds) - 1
+
+ lits = []
+ for i in range(1, n + 2 + 1):
+ ws1 = "\\n" * i
+ ws2 = "\\r\\n" * i
+ lits.append((ws1, str(i), ws2))
+
+ conds = list(conds)
+ for i, (pws, nws) in enumerate(conds):
+ kind = cond_kind(i, n)
+ b = str(i == active_branch).lower()
+ cond = [f"if {b}", f"else if {b}", "else", "endif"][kind]
+ cond = f"{{%{dash(pws)} {cond} {dash(nws)}%}}"
+ conds[i] = cond
+
+ it = map("".join, lits)
+ it = filter(None, chain.from_iterable(zip_longest(it, conds)))
+ code = "".join(it)
+
+ expected = f"{lits[0][0]}{lits[0][1]}"
+ for i, (cond, (before, after)) in enumerate(zip(conds, pairwise(lits))):
+ kind = cond_kind(i, n)
+ pws = cond.startswith("{%-")
+ nws = cond.endswith("-%}")
+
+ cond = i == active_branch
+ prev_cond = i == (active_branch + 1)
+
+ if prev_cond or (kind == IF):
+ expected += before[2] * (not pws)
+ if cond or (kind == END_IF):
+ expected += after[0] * (not nws)
+ expected += after[1]
+
+ # FIXME: Askama does not include whitespace before eof
+ # expected += lits[-1][2]
+
+ return code, expected
+
+
+def write_match(contents, arms, match_ws):
+ before, expr, after = contents
+ code = before
+
+ pws, nws = match_ws[0]
+ code += f"{{%{dash(pws)} match {expr} {dash(nws)}%}}"
+
+ for (arm, expr), (pws, nws) in zip(arms, match_ws[1:-1]):
+ code += f"{{%{dash(pws)} when {arm} {dash(nws)}%}}{expr}"
+
+ pws, nws = match_ws[-1]
+ code += f"{{%{dash(pws)} endmatch {dash(nws)}%}}"
+ code += after
+
+ return code
+
+
+def write_match_result(active_arm, contents, arms, match_ws):
+ before, expr, after = contents
+ expected = ""
+
+ expected += trim(before, (False, match_ws[0][0]))
+ expected += trim(arms[active_arm][1], (match_ws[1:][active_arm][1], match_ws[1:][active_arm+1][0]))
+ expected += trim(after, (match_ws[-1][1], False))
+ return expected
+
+
+def write_cond_tests(f):
+ f.write("""
+macro_rules! test_template {
+ ($source:literal, $rendered:expr) => {{
+ #[derive(Template)]
+ #[template(source = $source, ext = "txt")]
+ struct CondWS;
+
+ assert_eq!(CondWS.render().unwrap(), $rendered);
+ }};
+}
+
+#[rustfmt::skip]
+#[test]
+fn test_cond_ws() {
+""")
+
+ for branches in range(1, BRANCHES + 1):
+ for x in product([False, True], repeat=(branches+1)*2):
+ # it = iter(x)
+ # conds = list(zip(it, it))
+ conds = list(zip(x[::2], x[1::2]))
+
+ for i in range(branches):
+ code, expected = write_cond(conds, i)
+ f.write(f' test_template!("{code}", "{expected}");\n')
+
+ if branches != BRANCHES:
+ f.write("\n")
+ f.write("}\n")
+
+
+def write_match_tests(f):
+ f.write("""
+#[rustfmt::skip]
+macro_rules! test_match {
+ ($source:literal, $some_rendered:expr, $none_rendered:expr) => {{
+ #[derive(Template)]
+ #[template(source = $source, ext = "txt")]
+ struct MatchWS {
+ item: Option<&'static str>,
+ }
+
+ assert_eq!(MatchWS { item: Some("foo") }.render().unwrap(), $some_rendered);
+ assert_eq!(MatchWS { item: None }.render().unwrap(), $none_rendered);
+ }};
+}
+
+#[rustfmt::skip]
+#[test]
+fn test_match_ws() {
+""")
+
+ contents = "before ", "item", " after"
+ arms = [("Some with (item)", " foo "), ("None", " bar ")]
+
+ for x in product([False, True], repeat=len(arms)*2+1):
+ x = [False, False, *x, False]
+ arms_ws = list(zip(x[::2], x[1::2]))
+
+ code = write_match(contents, arms, arms_ws)
+ some_expected = write_match_result(0, contents, arms, arms_ws)
+ none_expected = write_match_result(1, contents, arms, arms_ws)
+
+ f.write(f' test_match!("{code}", "{some_expected}", "{none_expected}");\n')
+
+ f.write("}\n")
+
+
+if __name__ == "__main__":
+ with open("ws.rs", "w") as f:
+ f.write("// This file is auto generated by gen_ws_tests.py\n\n")
+ f.write("use askama::Template;\n")
+ write_cond_tests(f)
+ write_match_tests(f)
diff --git a/testing/tests/macro.rs b/testing/tests/macro.rs
index 51fbcdc..459b1e2 100644
--- a/testing/tests/macro.rs
+++ b/testing/tests/macro.rs
@@ -43,3 +43,13 @@ fn test_deep_import() {
let t = DeepImportTemplate;
assert_eq!(t.render().unwrap(), "foo");
}
+
+#[derive(Template)]
+#[template(path = "macro-short-circuit.html")]
+struct ShortCircuitTemplate {}
+
+#[test]
+fn test_short_circuit() {
+ let t = ShortCircuitTemplate {};
+ assert_eq!(t.render().unwrap(), "truetruetruefalsetruetrue");
+}
diff --git a/testing/tests/matches.rs b/testing/tests/matches.rs
index 811f3ce..d75a6c4 100644
--- a/testing/tests/matches.rs
+++ b/testing/tests/matches.rs
@@ -17,19 +17,19 @@ struct MatchOptRefTemplate<'a> {
#[test]
fn test_match_option() {
let s = MatchOptTemplate { item: Some("foo") };
- assert_eq!(s.render().unwrap(), "\n\nFound literal foo\n");
+ assert_eq!(s.render().unwrap(), "\nFound literal foo\n");
let s = MatchOptTemplate { item: Some("bar") };
- assert_eq!(s.render().unwrap(), "\n\nFound bar\n");
+ assert_eq!(s.render().unwrap(), "\nFound bar\n");
let s = MatchOptTemplate { item: None };
- assert_eq!(s.render().unwrap(), "\n\nNot Found\n");
+ assert_eq!(s.render().unwrap(), "\nNot Found\n");
}
#[test]
fn test_match_ref_deref() {
let s = MatchOptRefTemplate { item: &Some("foo") };
- assert_eq!(s.render().unwrap(), "\n\nFound literal foo\n");
+ assert_eq!(s.render().unwrap(), "\nFound literal foo\n");
}
#[derive(Template)]
@@ -41,10 +41,10 @@ struct MatchLitTemplate<'a> {
#[test]
fn test_match_literal() {
let s = MatchLitTemplate { item: "bar" };
- assert_eq!(s.render().unwrap(), "\n\nFound literal bar\n");
+ assert_eq!(s.render().unwrap(), "\nFound literal bar\n");
let s = MatchLitTemplate { item: "qux" };
- assert_eq!(s.render().unwrap(), "\n\nElse found qux\n");
+ assert_eq!(s.render().unwrap(), "\nElse found qux\n");
}
#[derive(Template)]
@@ -56,10 +56,10 @@ struct MatchLitCharTemplate {
#[test]
fn test_match_literal_char() {
let s = MatchLitCharTemplate { item: 'b' };
- assert_eq!(s.render().unwrap(), "\n\nFound literal b\n");
+ assert_eq!(s.render().unwrap(), "\nFound literal b\n");
let s = MatchLitCharTemplate { item: 'c' };
- assert_eq!(s.render().unwrap(), "\n\nElse found c\n");
+ assert_eq!(s.render().unwrap(), "\nElse found c\n");
}
#[derive(Template)]
@@ -71,10 +71,10 @@ struct MatchLitNumTemplate {
#[test]
fn test_match_literal_num() {
let s = MatchLitNumTemplate { item: 42 };
- assert_eq!(s.render().unwrap(), "\n\nFound answer to everything\n");
+ assert_eq!(s.render().unwrap(), "\nFound answer to everything\n");
let s = MatchLitNumTemplate { item: 23 };
- assert_eq!(s.render().unwrap(), "\n\nElse found 23\n");
+ assert_eq!(s.render().unwrap(), "\nElse found 23\n");
}
#[allow(dead_code)]
@@ -99,7 +99,7 @@ fn test_match_custom_enum() {
b: 255,
},
};
- assert_eq!(s.render().unwrap(), "\n\nColorful: #A000FF\n");
+ assert_eq!(s.render().unwrap(), "\nColorful: #A000FF\n");
}
#[derive(Template)]
diff --git a/testing/tests/operators.rs b/testing/tests/operators.rs
index 99a2a4c..05c1dab 100644
--- a/testing/tests/operators.rs
+++ b/testing/tests/operators.rs
@@ -53,3 +53,13 @@ fn test_ranges() {
};
assert_eq!(t.render().unwrap(), "abcd\nbcd\n\na\nab");
}
+
+#[derive(Template)]
+#[template(source = "{{ true && true }}{{ false || true }}", ext = "txt")]
+struct ShortCircuitTemplate {}
+
+#[test]
+fn test_short_circuit() {
+ let t = ShortCircuitTemplate {};
+ assert_eq!(t.render().unwrap(), "truetrue");
+}
diff --git a/testing/tests/simple.rs b/testing/tests/simple.rs
index 7241005..5ef03b1 100644
--- a/testing/tests/simple.rs
+++ b/testing/tests/simple.rs
@@ -291,6 +291,15 @@ fn test_path_func_call() {
}
#[derive(Template)]
+#[template(source = "{{ ::std::string::ToString::to_string(123) }}", ext = "txt")]
+struct RootPathFunctionTemplate;
+
+#[test]
+fn test_root_path_func_call() {
+ assert_eq!(RootPathFunctionTemplate.render().unwrap(), "123");
+}
+
+#[derive(Template)]
#[template(source = "Hello, {{ Self::world3(self, \"123\", 4) }}!", ext = "txt")]
struct FunctionTemplate;
diff --git a/testing/tests/whitespace.rs b/testing/tests/whitespace.rs
index 93c601e..ca72b23 100644
--- a/testing/tests/whitespace.rs
+++ b/testing/tests/whitespace.rs
@@ -37,5 +37,5 @@ fn test_extra_whitespace() {
let mut template = AllowWhitespaces::default();
template.nested_1.nested_2.array = &["a0", "a1", "a2", "a3"];
template.nested_1.nested_2.hash.insert("key", "value");
- assert_eq!(template.render().unwrap(), "\n0\n0\n0\n0\n\n\n\n0\n0\n0\n0\n0\n\na0\na1\nvalue\n\n\n\n\n\n[\n \"a0\",\n \"a1\",\n \"a2\",\n \"a3\"\n]\n[\n \"a0\",\n \"a1\",\n \"a2\",\n \"a3\"\n][\n \"a0\",\n \"a1\",\n \"a2\",\n \"a3\"\n]\n[\n \"a1\"\n][\n \"a1\"\n]\n[\n \"a1\",\n \"a2\"\n][\n \"a1\",\n \"a2\"\n]\n[\n \"a1\"\n][\n \"a1\"\n]1-1-1\n3333 3\n2222 2\n0000 0\n3333 3\n\ntruefalse\nfalsefalsefalse\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
+ assert_eq!(template.render().unwrap(), "\n0\n0\n0\n0\n\n\n\n0\n0\n0\n0\n0\n\na0\na1\nvalue\n\n\n\n\n\n[\n \"a0\",\n \"a1\",\n \"a2\",\n \"a3\"\n]\n[\n \"a0\",\n \"a1\",\n \"a2\",\n \"a3\"\n][\n \"a0\",\n \"a1\",\n \"a2\",\n \"a3\"\n]\n[\n \"a1\"\n][\n \"a1\"\n]\n[\n \"a1\",\n \"a2\"\n][\n \"a1\",\n \"a2\"\n]\n[\n \"a1\"\n][\n \"a1\"\n]1-1-1\n3333 3\n2222 2\n0000 0\n3333 3\n\ntruefalse\nfalsefalsefalse\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
}
diff --git a/testing/tests/ws.rs b/testing/tests/ws.rs
new file mode 100644
index 0000000..3f62da5
--- /dev/null
+++ b/testing/tests/ws.rs
@@ -0,0 +1,214 @@
+// This file is auto generated by gen_ws_tests.py
+
+use askama::Template;
+
+macro_rules! test_template {
+ ($source:literal, $rendered:expr) => {{
+ #[derive(Template)]
+ #[template(source = $source, ext = "txt")]
+ struct CondWS;
+
+ assert_eq!(CondWS.render().unwrap(), $rendered);
+ }};
+}
+
+#[rustfmt::skip]
+#[test]
+fn test_cond_ws() {
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% endif %}\n\n\n3\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n\n\n\n3");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% endif -%}\n\n\n3\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n3");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- endif %}\n\n\n3\r\n\r\n\r\n", "\n1\r\n\n\n2\n\n\n3");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- endif -%}\n\n\n3\r\n\r\n\r\n", "\n1\r\n\n\n23");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% endif %}\n\n\n3\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n\n\n\n3");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% endif -%}\n\n\n3\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n3");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- endif %}\n\n\n3\r\n\r\n\r\n", "\n1\r\n2\n\n\n3");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- endif -%}\n\n\n3\r\n\r\n\r\n", "\n1\r\n23");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% endif %}\n\n\n3\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n\n\n\n3");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% endif -%}\n\n\n3\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n3");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- endif %}\n\n\n3\r\n\r\n\r\n", "\n1\n\n2\n\n\n3");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- endif -%}\n\n\n3\r\n\r\n\r\n", "\n1\n\n23");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% endif %}\n\n\n3\r\n\r\n\r\n", "\n12\r\n\r\n\n\n\n3");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% endif -%}\n\n\n3\r\n\r\n\r\n", "\n12\r\n\r\n3");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- endif %}\n\n\n3\r\n\r\n\r\n", "\n12\n\n\n3");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- endif -%}\n\n\n3\r\n\r\n\r\n", "\n123");
+
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\n\n\n\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n34");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\n\n\n\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n34");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\n\n\n\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n24");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\n\n\n\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\n\n\n\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n24");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n34");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\n\n\n\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n24");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\n\n\n\n4");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\n\n\n\n4");
+ test_template!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n24");
+ test_template!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n34");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\n\n\n\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n34");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\n\n\n\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n34");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\n\n\n\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n24");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\n\n\n\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\n\n\n\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n24");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n34");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\n\n\n\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n24");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\n\n\n\n4");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\n\n\n\n4");
+ test_template!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n24");
+ test_template!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n34");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n34");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n24");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n24");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n34");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n24");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n24");
+ test_template!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n34");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n34");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\n\n\n\n4");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\n\n\n\n4");
+ test_template!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124");
+ test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134");
+}
+
+#[rustfmt::skip]
+macro_rules! test_match {
+ ($source:literal, $some_rendered:expr, $none_rendered:expr) => {{
+ #[derive(Template)]
+ #[template(source = $source, ext = "txt")]
+ struct MatchWS {
+ item: Option<&'static str>,
+ }
+
+ assert_eq!(MatchWS { item: Some("foo") }.render().unwrap(), $some_rendered);
+ assert_eq!(MatchWS { item: None }.render().unwrap(), $none_rendered);
+ }};
+}
+
+#[rustfmt::skip]
+#[test]
+fn test_match_ws() {
+ test_match!("before {% match item %}{% when Some with (item) %} foo {% when None %} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) %} foo {% when None %} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) %} foo {% when None -%} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) %} foo {% when None -%} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) %} foo {%- when None %} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) %} foo {%- when None %} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) %} foo {%- when None -%} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) %} foo {%- when None -%} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) -%} foo {% when None %} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) -%} foo {% when None %} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) -%} foo {% when None -%} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) -%} foo {% when None -%} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) -%} foo {%- when None %} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) -%} foo {%- when None %} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) -%} foo {%- when None -%} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{% when Some with (item) -%} foo {%- when None -%} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) %} foo {% when None %} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) %} foo {% when None %} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) %} foo {% when None -%} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) %} foo {% when None -%} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) %} foo {%- when None %} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) %} foo {%- when None %} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) %} foo {%- when None -%} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) %} foo {%- when None -%} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) -%} foo {% when None %} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) -%} foo {% when None %} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) -%} foo {% when None -%} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) -%} foo {% when None -%} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) -%} foo {%- when None %} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) -%} foo {%- when None %} bar {%- endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) -%} foo {%- when None -%} bar {% endmatch %} after", "before foo after", "before bar after");
+ test_match!("before {% match item %}{%- when Some with (item) -%} foo {%- when None -%} bar {%- endmatch %} after", "before foo after", "before bar after");
+}