Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 06d2eee

Browse files
Kijewskidjc
authored andcommitted
Implement "if let" statement
1 parent 5afabbc commit 06d2eee

File tree

2 files changed

+54
-17
lines changed

2 files changed

+54
-17
lines changed

askama_shared/src/generator.rs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use crate::filters;
33
use crate::heritage::{Context, Heritage};
44
use crate::input::{Source, TemplateInput};
55
use crate::parser::{
6-
parse, Cond, Expr, MatchParameter, MatchParameters, MatchVariant, Node, Target, When, Ws,
6+
parse, Cond, CondTest, Expr, MatchParameter, MatchParameters, MatchVariant, Node, Target, When,
7+
Ws,
78
};
89

910
use proc_macro2::Span;
@@ -483,31 +484,45 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
483484
self.locals.pop();
484485
}
485486

487+
self.locals.push();
486488
let mut arm_size = 0;
487-
if let Some(expr) = cond {
489+
if let Some(CondTest { target, expr }) = cond {
488490
if i == 0 {
489491
buf.write("if ");
490492
} else {
491493
buf.dedent()?;
492494
buf.write("} else if ");
493495
}
494-
// The following syntax `*(&(...) as &bool)` is used to
495-
// trigger Rust's automatic dereferencing, to coerce
496-
// e.g. `&&&&&bool` to `bool`. First `&(...) as &bool`
497-
// coerces e.g. `&&&bool` to `&bool`. Then `*(&bool)`
498-
// finally dereferences it to `bool`.
499-
buf.write("*(&(");
500-
let expr_code = self.visit_expr_root(expr)?;
501-
buf.write(&expr_code);
502-
buf.write(") as &bool)");
496+
497+
if let Some((variant, params)) = target {
498+
let mut expr_buf = Buffer::new(0);
499+
self.visit_expr(&mut expr_buf, expr)?;
500+
buf.write("let ");
501+
self.visit_match_variant(buf, variant);
502+
if let Some(params) = params {
503+
self.visit_match_params(buf, params);
504+
}
505+
buf.write(" = &(");
506+
buf.write(&expr_buf.buf);
507+
buf.write(")");
508+
} else {
509+
// The following syntax `*(&(...) as &bool)` is used to
510+
// trigger Rust's automatic dereferencing, to coerce
511+
// e.g. `&&&&&bool` to `bool`. First `&(...) as &bool`
512+
// coerces e.g. `&&&bool` to `&bool`. Then `*(&bool)`
513+
// finally dereferences it to `bool`.
514+
buf.write("*(&(");
515+
let expr_code = self.visit_expr_root(expr)?;
516+
buf.write(&expr_code);
517+
buf.write(") as &bool)");
518+
}
503519
} else {
504520
buf.dedent()?;
505521
buf.write("} else");
506522
has_else = true;
507523
}
508524

509525
buf.writeln(" {")?;
510-
self.locals.push();
511526

512527
arm_size += self.handle(ctx, nodes, buf, AstLevel::Nested)?;
513528
arm_sizes.push(arm_size);

askama_shared/src/parser.rs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub enum Node<'a> {
1818
Call(Ws, Option<&'a str>, &'a str, Vec<Expr<'a>>),
1919
LetDecl(Ws, Target<'a>),
2020
Let(Ws, Target<'a>, Expr<'a>),
21-
Cond(Vec<(Ws, Option<Expr<'a>>, Vec<Node<'a>>)>, Ws),
21+
Cond(Vec<Cond<'a>>, Ws),
2222
Match(Ws, Expr<'a>, Vec<When<'a>>, Ws),
2323
Loop(Ws, Target<'a>, Expr<'a>, Vec<Node<'a>>, Ws),
2424
Extends(Expr<'a>),
@@ -144,7 +144,13 @@ pub enum Target<'a> {
144144
#[derive(Clone, Copy, Debug, PartialEq)]
145145
pub struct Ws(pub bool, pub bool);
146146

147-
pub type Cond<'a> = (Ws, Option<Expr<'a>>, Vec<Node<'a>>);
147+
pub type Cond<'a> = (Ws, Option<CondTest<'a>>, Vec<Node<'a>>);
148+
149+
#[derive(Debug, PartialEq)]
150+
pub struct CondTest<'a> {
151+
pub target: Option<(MatchVariant<'a>, Option<MatchParameters<'a>>)>,
152+
pub expr: Expr<'a>,
153+
}
148154

149155
fn ws<F, I, O, E>(mut inner: F) -> impl FnMut(I) -> IResult<I, O, E>
150156
where
@@ -724,9 +730,25 @@ fn block_call(i: &[u8]) -> IResult<&[u8], Node> {
724730
))
725731
}
726732

727-
fn cond_if(i: &[u8]) -> IResult<&[u8], Expr> {
728-
let (i, (_, cond)) = tuple((ws(tag("if")), ws(expr_any)))(i)?;
729-
Ok((i, cond))
733+
fn cond_if(i: &[u8]) -> IResult<&[u8], CondTest> {
734+
let mut p = tuple((
735+
ws(tag("if")),
736+
opt(tuple((
737+
ws(alt((tag("let"), tag("set")))),
738+
ws(match_variant),
739+
opt(alt((match_simple_parameters, match_named_parameters))),
740+
ws(tag("=")),
741+
))),
742+
ws(expr_any),
743+
));
744+
let (i, (_, dest, expr)) = p(i)?;
745+
Ok((
746+
i,
747+
CondTest {
748+
target: dest.map(|(_, variant, params, _)| (variant, params)),
749+
expr,
750+
},
751+
))
730752
}
731753

732754
fn cond_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Cond<'a>> {

0 commit comments

Comments
 (0)