mirror of
https://github.com/askama-rs/askama.git
synced 2025-10-02 07:20:55 +00:00
Add full support for if let chain feature
This commit is contained in:
parent
25593f249b
commit
5cc7cb2121
@ -1,5 +1,6 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use parser::node::CondTest;
|
||||
use parser::{
|
||||
CharLit, CharPrefix, Expr, Filter, IntKind, Num, Span, StrLit, StrPrefix, Target, WithSpan,
|
||||
};
|
||||
@ -59,6 +60,7 @@ impl<'a> Generator<'a, '_> {
|
||||
Expr::IsNotDefined(var_name) => self.visit_is_defined(buf, false, var_name)?,
|
||||
Expr::As(ref expr, target) => self.visit_as(ctx, buf, expr, target)?,
|
||||
Expr::Concat(ref exprs) => self.visit_concat(ctx, buf, exprs)?,
|
||||
Expr::LetCond(_) => unreachable!("should only be called in visit_condition"),
|
||||
})
|
||||
}
|
||||
|
||||
@ -66,7 +68,7 @@ impl<'a> Generator<'a, '_> {
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
expr: &WithSpan<'_, Expr<'_>>,
|
||||
expr: &WithSpan<'_, Expr<'a>>,
|
||||
) -> Result<(), CompileError> {
|
||||
match &**expr {
|
||||
Expr::BoolLit(_) | Expr::IsDefined(_) | Expr::IsNotDefined(_) => {
|
||||
@ -86,6 +88,9 @@ impl<'a> Generator<'a, '_> {
|
||||
self.visit_condition(ctx, buf, expr)?;
|
||||
buf.write(')');
|
||||
}
|
||||
Expr::LetCond(cond) => {
|
||||
self.visit_let_cond(ctx, buf, cond)?;
|
||||
}
|
||||
_ => {
|
||||
buf.write("rinja::helpers::as_bool(&(");
|
||||
self.visit_expr(ctx, buf, expr)?;
|
||||
@ -144,6 +149,20 @@ impl<'a> Generator<'a, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_let_cond(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
cond: &WithSpan<'_, CondTest<'a>>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
buf.write(" let ");
|
||||
if let Some(ref target) = cond.target {
|
||||
self.visit_target(buf, true, true, target);
|
||||
}
|
||||
buf.write("= &");
|
||||
self.visit_expr(ctx, buf, &cond.expr)
|
||||
}
|
||||
|
||||
fn visit_try(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
|
@ -205,7 +205,8 @@ impl<'a> Generator<'a, '_> {
|
||||
| Expr::NamedArgument(_, _)
|
||||
| Expr::FilterSource
|
||||
| Expr::As(_, _)
|
||||
| Expr::Concat(_) => {
|
||||
| Expr::Concat(_)
|
||||
| Expr::LetCond(_) => {
|
||||
*only_contains_is_defined = false;
|
||||
(EvaluatedResult::Unknown, WithSpan::new(expr, span))
|
||||
}
|
||||
@ -1466,6 +1467,8 @@ fn is_cacheable(expr: &WithSpan<'_, Expr<'_>>) -> bool {
|
||||
Expr::As(expr, _) => is_cacheable(expr),
|
||||
Expr::Try(expr) => is_cacheable(expr),
|
||||
Expr::Concat(args) => args.iter().all(is_cacheable),
|
||||
// Doesn't make sense in this context.
|
||||
Expr::LetCond(_) => false,
|
||||
// We have too little information to tell if the expression is pure:
|
||||
Expr::Call(_, _) => false,
|
||||
Expr::RustMacro(_, _) => false,
|
||||
|
@ -9,6 +9,7 @@ use winnow::combinator::{
|
||||
use winnow::error::{ErrorKind, ParserError as _};
|
||||
use winnow::stream::Stream as _;
|
||||
|
||||
use crate::node::CondTest;
|
||||
use crate::{
|
||||
CharLit, ErrorContext, Level, Num, ParseErr, ParseResult, PathOrIdentifier, Span, StrLit,
|
||||
WithSpan, char_lit, filter, identifier, keyword, num_lit, path_or_identifier, skip_ws0,
|
||||
@ -61,7 +62,8 @@ fn check_expr<'a>(
|
||||
| Expr::Var(_)
|
||||
| Expr::RustMacro(_, _)
|
||||
| Expr::Try(_)
|
||||
| Expr::FilterSource => Ok(()),
|
||||
| Expr::FilterSource
|
||||
| Expr::LetCond(_) => Ok(()),
|
||||
Expr::Array(elems) | Expr::Tuple(elems) | Expr::Concat(elems) => {
|
||||
for elem in elems {
|
||||
check_expr(elem, allow_underscore)?;
|
||||
@ -127,6 +129,8 @@ pub enum Expr<'a> {
|
||||
IsDefined(&'a str),
|
||||
IsNotDefined(&'a str),
|
||||
Concat(Vec<WithSpan<'a, Expr<'a>>>),
|
||||
/// If you have `&& let Some(y)`, this variant handles it.
|
||||
LetCond(Box<WithSpan<'a, CondTest<'a>>>),
|
||||
}
|
||||
|
||||
impl<'a> Expr<'a> {
|
||||
@ -506,7 +510,8 @@ impl<'a> Expr<'a> {
|
||||
| Self::Array(_)
|
||||
| Self::BinOp(_, _, _)
|
||||
| Self::Path(_)
|
||||
| Self::Concat(_) => false,
|
||||
| Self::Concat(_)
|
||||
| Self::LetCond(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -367,23 +367,21 @@ fn skip_till<'a, 'b, O>(
|
||||
end: impl Parser<&'a str, O, ErrorContext<'a>>,
|
||||
) -> impl Parser<&'a str, (&'a str, O), ErrorContext<'a>> {
|
||||
let mut next = alt((end.map(Some), any.map(|_| None)));
|
||||
move |i: &mut &'a str| {
|
||||
loop {
|
||||
*i = match candidate_finder.split(i) {
|
||||
Some((_, i)) => i,
|
||||
None => {
|
||||
return Err(winnow::error::ErrMode::Backtrack(ErrorContext::new(
|
||||
"`end` not found`",
|
||||
*i,
|
||||
)));
|
||||
}
|
||||
};
|
||||
let exclusive = *i;
|
||||
if let Some(lookahead) = next.parse_next(i)? {
|
||||
let inclusive = *i;
|
||||
*i = exclusive;
|
||||
return Ok((inclusive, lookahead));
|
||||
move |i: &mut &'a str| loop {
|
||||
*i = match candidate_finder.split(i) {
|
||||
Some((_, i)) => i,
|
||||
None => {
|
||||
return Err(winnow::error::ErrMode::Backtrack(ErrorContext::new(
|
||||
"`end` not found`",
|
||||
*i,
|
||||
)));
|
||||
}
|
||||
};
|
||||
let exclusive = *i;
|
||||
if let Some(lookahead) = next.parse_next(i)? {
|
||||
let inclusive = *i;
|
||||
*i = exclusive;
|
||||
return Ok((inclusive, lookahead));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -397,7 +397,7 @@ impl<'a> Cond<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct CondTest<'a> {
|
||||
pub target: Option<Target<'a>>,
|
||||
pub expr: WithSpan<'a, Expr<'a>>,
|
||||
@ -420,7 +420,19 @@ impl<'a> CondTest<'a> {
|
||||
ws(|i: &mut _| Target::parse(i, s)),
|
||||
ws('='),
|
||||
)),
|
||||
ws(|i: &mut _| Expr::parse(i, s.level.get(), false)),
|
||||
ws(|i: &mut _| {
|
||||
let start = *i;
|
||||
let mut expr = Expr::parse(i, s.level.get(), false)?;
|
||||
if let Expr::BinOp(_, _, ref mut right) = expr.inner {
|
||||
if matches!(right.inner, Expr::Var("set" | "let")) {
|
||||
*i = right.span.as_suffix_of(start).unwrap();
|
||||
let start_span = Span::from(*i);
|
||||
let new_right = Self::parse_cond(i, s)?;
|
||||
right.inner = Expr::LetCond(Box::new(WithSpan::new(new_right, start_span)));
|
||||
}
|
||||
}
|
||||
Ok(expr)
|
||||
}),
|
||||
)
|
||||
.parse_next(i)?;
|
||||
let contains_bool_lit_or_is_defined = expr.contains_bool_lit_or_is_defined();
|
||||
|
Loading…
x
Reference in New Issue
Block a user