Skip to content

Commit

Permalink
Add {% break %} and {% continue %}
Browse files Browse the repository at this point in the history
This PR adds `{% break %}` and `{% continue %}` statements to break out
of a loop, or continue with the next element of the iterator.
  • Loading branch information
Kijewski committed Jul 18, 2021
1 parent 7e22790 commit ea9a554
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 0 deletions.
10 changes: 10 additions & 0 deletions askama_shared/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,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;")?;
}
}
}

Expand Down
16 changes: 16 additions & 0 deletions askama_shared/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -1119,6 +1121,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),
Expand All @@ -1134,6 +1148,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,
)),
|i| tag_block_end(i, s),
));
Expand Down
11 changes: 11 additions & 0 deletions testing/templates/for-break-continue.html
Original file line number Diff line number Diff line change
@@ -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 -%}
68 changes: 68 additions & 0 deletions testing/tests/loops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

0 comments on commit ea9a554

Please sign in to comment.