aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar René Kijewski <kijewski@library.vetmed.fu-berlin.de>2021-07-30 17:04:30 +0200
committerLibravatar Dirkjan Ochtman <dirkjan@ochtman.nl>2021-08-30 22:54:32 +0200
commit726ca1cc3356f7aa59d7c2730c9f3b3588236cb0 (patch)
tree6ebdfeb9a0b4af290af3faa42b3a09abcb71440d
parented2e640dbdff88fe118d943fa0ea9ae00eb11555 (diff)
downloadaskama-726ca1cc3356f7aa59d7c2730c9f3b3588236cb0.tar.gz
askama-726ca1cc3356f7aa59d7c2730c9f3b3588236cb0.tar.bz2
askama-726ca1cc3356f7aa59d7c2730c9f3b3588236cb0.zip
Add {% break %} and {% continue %}
This PR adds `{% break %}` and `{% continue %}` statements to break out of a loop, or continue with the next element of the iterator.
-rw-r--r--askama_shared/src/generator.rs10
-rw-r--r--askama_shared/src/parser.rs16
-rw-r--r--testing/templates/for-break-continue.html11
-rw-r--r--testing/tests/loops.rs68
4 files changed, 105 insertions, 0 deletions
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs
index ec468e5..3bcb5d9 100644
--- a/askama_shared/src/generator.rs
+++ b/askama_shared/src/generator.rs
@@ -459,6 +459,16 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
// No whitespace handling: child template top-level is not used,
// except for the blocks defined in it.
}
+ Node::Break(ws) => {
+ self.handle_ws(ws);
+ self.write_buf_writable(buf)?;
+ buf.writeln("break;")?;
+ }
+ Node::Continue(ws) => {
+ self.handle_ws(ws);
+ self.write_buf_writable(buf)?;
+ buf.writeln("continue;")?;
+ }
}
}
diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs
index 66f2ec1..910c942 100644
--- a/askama_shared/src/parser.rs
+++ b/askama_shared/src/parser.rs
@@ -27,6 +27,8 @@ pub enum Node<'a> {
Import(Ws, &'a str, &'a str),
Macro(&'a str, Macro<'a>),
Raw(Ws, &'a str, Ws),
+ Break(Ws),
+ Continue(Ws),
}
#[derive(Debug, PartialEq)]
@@ -1068,6 +1070,18 @@ fn block_raw<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>>
))
}
+fn break_statement(i: &[u8]) -> IResult<&[u8], Node<'_>> {
+ let mut p = tuple((opt(tag("-")), ws(tag("break")), opt(tag("-"))));
+ let (i, (pws, _, nws)) = p(i)?;
+ Ok((i, Node::Break(Ws(pws.is_some(), nws.is_some()))))
+}
+
+fn continue_statement(i: &[u8]) -> IResult<&[u8], Node<'_>> {
+ let mut p = tuple((opt(tag("-")), ws(tag("continue")), opt(tag("-"))));
+ let (i, (pws, _, nws)) = p(i)?;
+ Ok((i, Node::Continue(Ws(pws.is_some(), nws.is_some()))))
+}
+
fn block_node<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> {
let mut p = tuple((
|i| tag_block_start(i, s),
@@ -1083,6 +1097,8 @@ fn block_node<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>>
|i| block_block(i, s),
|i| block_macro(i, s),
|i| block_raw(i, s),
+ break_statement,
+ continue_statement,
)),
cut(|i| tag_block_end(i, s)),
));
diff --git a/testing/templates/for-break-continue.html b/testing/templates/for-break-continue.html
new file mode 100644
index 0000000..c7a948f
--- /dev/null
+++ b/testing/templates/for-break-continue.html
@@ -0,0 +1,11 @@
+{%- for v in values -%}
+ x {{- v -}}
+ {%- if matches!(v, x if *x > 9) -%}
+ {%- if matches!(v, x if *x % 2 == 0) -%}
+ {%- break -%}
+ {%- else -%}
+ {%- continue -%}
+ {%- endif -%}
+ {%- endif -%}
+ y
+{%- endfor -%}
diff --git a/testing/tests/loops.rs b/testing/tests/loops.rs
index fffb39d..49c37e0 100644
--- a/testing/tests/loops.rs
+++ b/testing/tests/loops.rs
@@ -304,3 +304,71 @@ fn test_for_enumerate() {
};
assert_eq!(t.render().unwrap(), "0=hello-1=world-2=!-");
}
+
+#[derive(Template)]
+#[template(
+ source = "{% for v in values.iter() %}x{{v}}{% if matches!(v, x if *x==3) %}{% break %}{% endif %}y{% endfor %}",
+ ext = "txt"
+)]
+struct Break<'a> {
+ values: &'a [i32],
+}
+
+#[test]
+fn test_loop_break() {
+ let t = Break {
+ values: &[1, 2, 3, 4, 5],
+ };
+ assert_eq!(t.render().unwrap(), "x1yx2yx3");
+
+ let t = Break {
+ values: &[1, 2, 4, 5],
+ };
+ assert_eq!(t.render().unwrap(), "x1yx2yx4yx5y");
+}
+
+#[derive(Template)]
+#[template(
+ source = "{% for v in values %}x{{v}}{% if matches!(v, x if *x==3) %}{% continue %}{% endif %}y{% endfor %}",
+ ext = "txt"
+)]
+struct Continue<'a> {
+ values: &'a [i32],
+}
+
+#[test]
+fn test_loop_continue() {
+ let t = Continue {
+ values: &[1, 2, 3, 4, 5],
+ };
+ assert_eq!(t.render().unwrap(), "x1yx2yx3x4yx5y");
+
+ let t = Continue {
+ values: &[1, 2, 4, 5],
+ };
+ assert_eq!(t.render().unwrap(), "x1yx2yx4yx5y");
+}
+
+#[derive(Template)]
+#[template(path = "for-break-continue.html")]
+struct BreakContinue<'a> {
+ values: &'a [i32],
+}
+
+#[test]
+fn test_loop_break_continue() {
+ let t = BreakContinue {
+ values: &[1, 2, 3, 4, 5],
+ };
+ assert_eq!(t.render().unwrap(), "x1yx2yx3yx4yx5y");
+
+ let t = BreakContinue {
+ values: &[1, 2, 3, 10, 4, 5],
+ };
+ assert_eq!(t.render().unwrap(), "x1yx2yx3yx10");
+
+ let t = BreakContinue {
+ values: &[1, 2, 3, 11, 4, 5],
+ };
+ assert_eq!(t.render().unwrap(), "x1yx2yx3yx11x4yx5y");
+}