diff options
author | René Kijewski <kijewski@library.vetmed.fu-berlin.de> | 2021-07-30 17:04:30 +0200 |
---|---|---|
committer | Dirkjan Ochtman <dirkjan@ochtman.nl> | 2021-08-30 22:54:32 +0200 |
commit | 726ca1cc3356f7aa59d7c2730c9f3b3588236cb0 (patch) | |
tree | 6ebdfeb9a0b4af290af3faa42b3a09abcb71440d | |
parent | ed2e640dbdff88fe118d943fa0ea9ae00eb11555 (diff) | |
download | askama-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.rs | 10 | ||||
-rw-r--r-- | askama_shared/src/parser.rs | 16 | ||||
-rw-r--r-- | testing/templates/for-break-continue.html | 11 | ||||
-rw-r--r-- | testing/tests/loops.rs | 68 |
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"); +} |