Skip to content

Commit

Permalink
Add Expr::is_cachable()
Browse files Browse the repository at this point in the history
  • Loading branch information
Kijewski committed Sep 17, 2022
1 parent eded39c commit 06ab17f
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 4 deletions.
6 changes: 2 additions & 4 deletions askama_derive/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::CompileError;
use proc_macro::TokenStream;
use quote::{quote, ToTokens};

use std::collections::HashMap;
use std::collections::hash_map::{Entry, HashMap};
use std::path::{Path, PathBuf};
use std::{cmp, hash, mem, str};

Expand Down Expand Up @@ -1196,11 +1196,9 @@ impl<'a> Generator<'a> {
expr_buf.buf, self.input.escaper
),
};
let is_cacheable = !matches!(s, Expr::Call(..));

use std::collections::hash_map::Entry;
let id = match expr_cache.entry(expression.clone()) {
Entry::Occupied(e) if is_cacheable => *e.get(),
Entry::Occupied(e) if s.is_cachable() => *e.get(),
e => {
let id = self.named;
self.named += 1;
Expand Down
33 changes: 33 additions & 0 deletions askama_derive/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,39 @@ impl Expr<'_> {
_ => false,
}
}

/// Returns `true` if the outcome of this expression may be used multiple times in the same
/// `write!()` call, without evaluating the expression again, i.e. the expression should be
/// side-effect free.
pub(crate) fn is_cachable(&self) -> bool {
match self {
// Literals are the definition of pure:
Expr::BoolLit(_) => true,
Expr::NumLit(_) => true,
Expr::StrLit(_) => true,
Expr::CharLit(_) => true,
// fmt::Display should have no effects:
Expr::Var(_) => true,
Expr::Path(_) => true,
// Check recursively:
Expr::Array(args) => args.iter().all(|arg| arg.is_cachable()),
Expr::Attr(lhs, _) => lhs.is_cachable(),
Expr::Index(lhs, rhs) => lhs.is_cachable() && rhs.is_cachable(),
Expr::Filter(_, args) => args.iter().all(|arg| arg.is_cachable()),
Expr::Unary(_, arg) => arg.is_cachable(),
Expr::BinOp(_, lhs, rhs) => lhs.is_cachable() && rhs.is_cachable(),
Expr::Range(_, lhs, rhs) => {
lhs.as_ref().map_or(true, |v| v.is_cachable())
&& rhs.as_ref().map_or(true, |v| v.is_cachable())
}
Expr::Group(arg) => arg.is_cachable(),
Expr::Tuple(args) => args.iter().all(|arg| arg.is_cachable()),
// We have too little information to tell if the expression is pure:
Expr::Call(_, _) => false,
Expr::RustMacro(_, _) => false,
Expr::Try(_) => false,
}
}
}

pub(crate) type When<'a> = (Ws, Target<'a>, Vec<Node<'a>>);
Expand Down

0 comments on commit 06ab17f

Please sign in to comment.