From ae02be87c69c17df1ba2b4c862c561e2e75a5bef Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Wed, 28 Jun 2023 09:28:56 +0200 Subject: Parse templates once --- askama_derive/src/generator.rs | 57 +++++++++++++++++++++++++++++++---------- askama_derive/src/lib.rs | 1 - askama_derive/src/parser/mod.rs | 5 +--- 3 files changed, 45 insertions(+), 18 deletions(-) (limited to 'askama_derive') diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index cdf5ebf..5e70849 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -38,17 +38,15 @@ fn build_template(ast: &syn::DeriveInput) -> Result { Source::Path(_) => get_template_source(&input.path)?, }; - let mut sources = HashMap::new(); - find_used_templates(&input, &mut sources, source)?; - - let mut parsed = HashMap::new(); - for (path, src) in &sources { - parsed.insert(path.as_path(), parse(src, input.syntax)?); - } + let mut templates = HashMap::new(); + find_used_templates(&input, &mut templates, source)?; let mut contexts = HashMap::new(); - for (path, nodes) in &parsed { - contexts.insert(*path, Context::new(input.config, path, nodes)?); + for (path, parsed) in &templates { + contexts.insert( + path.as_path(), + Context::new(input.config, path, parsed.nodes())?, + ); } let ctx = &contexts[input.path.as_path()]; @@ -59,7 +57,7 @@ fn build_template(ast: &syn::DeriveInput) -> Result { }; if input.print == Print::Ast || input.print == Print::All { - eprintln!("{:?}", parsed[input.path.as_path()]); + eprintln!("{:?}", templates[input.path.as_path()].nodes()); } let code = Generator::new( @@ -204,13 +202,14 @@ impl TemplateArgs { fn find_used_templates( input: &TemplateInput<'_>, - map: &mut HashMap, + map: &mut HashMap, source: String, ) -> Result<(), CompileError> { let mut dependency_graph = Vec::new(); let mut check = vec![(input.path.clone(), source)]; while let Some((path, source)) = check.pop() { - for n in parse(&source, input.syntax)? { + let parsed = Parsed::new(source, input.syntax)?; + for n in parsed.nodes() { match n { Node::Extends(extends) => { let extends = input.config.find_template(extends, Some(&path))?; @@ -237,11 +236,43 @@ fn find_used_templates( _ => {} } } - map.insert(path, source); + map.insert(path, parsed); } Ok(()) } +mod _parsed { + use std::mem; + + use crate::config::Syntax; + use crate::parser::{parse, Node}; + use crate::CompileError; + + pub(super) struct Parsed { + #[allow(dead_code)] + source: String, + nodes: Vec>, + } + + impl Parsed { + pub(super) fn new(source: String, syntax: &Syntax<'_>) -> Result { + // Self-referential borrowing: `self` will keep the source alive as `String`, + // internally we will transmute it to `&'static str` to satisfy the compiler. + // However, we only expose the nodes with a lifetime limited to `self`. + let src = unsafe { mem::transmute::<&str, &'static str>(source.as_str()) }; + let nodes = parse(src, syntax)?; + Ok(Self { source, nodes }) + } + + // The return value's lifetime must be limited to `self` to uphold the unsafe invariant. + pub(super) fn nodes(&self) -> &[Node<'_>] { + &self.nodes + } + } +} + +use _parsed::Parsed; + struct Generator<'a> { // The template input state: original struct AST and attributes input: &'a TemplateInput<'a>, diff --git a/askama_derive/src/lib.rs b/askama_derive/src/lib.rs index 2acf583..0683e71 100644 --- a/askama_derive/src/lib.rs +++ b/askama_derive/src/lib.rs @@ -1,4 +1,3 @@ -#![forbid(unsafe_code)] #![deny(elided_lifetimes_in_paths)] #![deny(unreachable_pub)] diff --git a/askama_derive/src/parser/mod.rs b/askama_derive/src/parser/mod.rs index 79b178e..ccc9ff9 100644 --- a/askama_derive/src/parser/mod.rs +++ b/askama_derive/src/parser/mod.rs @@ -58,10 +58,7 @@ impl From for Whitespace { } } -pub(crate) fn parse<'a>( - src: &'a str, - syntax: &'a Syntax<'_>, -) -> Result>, CompileError> { +pub(crate) fn parse<'a>(src: &'a str, syntax: &Syntax<'_>) -> Result>, CompileError> { match Node::parse(src, &State::new(syntax)) { Ok((left, res)) => { if !left.is_empty() { -- cgit