diff options
Diffstat (limited to '')
-rw-r--r-- | askama_shared/src/generator.rs (renamed from askama_derive/src/generator.rs) | 39 | ||||
-rw-r--r-- | askama_shared/src/heritage.rs | 111 | ||||
-rw-r--r-- | askama_shared/src/lib.rs | 39 |
3 files changed, 175 insertions, 14 deletions
diff --git a/askama_derive/src/generator.rs b/askama_shared/src/generator.rs index a12b1f0..3171c38 100644 --- a/askama_derive/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -1,13 +1,14 @@ -use super::{get_template_source, Context, Heritage}; -use askama_shared::filters; -use askama_shared::input::{Source, TemplateInput}; -use askama_shared::parser::{ +use super::{get_template_source, Integrations}; +use crate::filters; +use crate::heritage::{Context, Heritage}; +use crate::input::{Source, TemplateInput}; +use crate::parser::{ parse, Cond, Expr, MatchParameter, MatchParameters, MatchVariant, Node, Target, When, WS, }; use proc_macro2::Span; -use quote::ToTokens; +use quote::{quote, ToTokens}; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; @@ -15,12 +16,14 @@ use std::{cmp, hash, mem, str}; use syn; -pub(crate) fn generate( +pub fn generate( input: &TemplateInput, contexts: &HashMap<&PathBuf, Context>, heritage: &Option<Heritage>, + integrations: Integrations, ) -> String { - Generator::new(input, contexts, heritage, SetChain::new()).build(&contexts[&input.path]) + Generator::new(input, contexts, heritage, integrations, SetChain::new()) + .build(&contexts[&input.path]) } struct Generator<'a> { @@ -30,6 +33,8 @@ struct Generator<'a> { contexts: &'a HashMap<&'a PathBuf, Context<'a>>, // The heritage contains references to blocks and their ancestry heritage: &'a Option<Heritage<'a>>, + // What integrations need to be generated + integrations: Integrations, // Variables accessible directly from the current scope (not redirected to context) locals: SetChain<'a, &'a str>, // Suffix whitespace from the previous literal. Will be flushed to the @@ -52,12 +57,14 @@ impl<'a> Generator<'a> { input: &'n TemplateInput, contexts: &'n HashMap<&'n PathBuf, Context<'n>>, heritage: &'n Option<Heritage>, + integrations: Integrations, locals: SetChain<'n, &'n str>, ) -> Generator<'n> { Generator { input, contexts, heritage, + integrations, locals, next_ws: None, skip_ws: false, @@ -69,7 +76,13 @@ impl<'a> Generator<'a> { fn child(&mut self) -> Generator { let locals = SetChain::with_parent(&self.locals); - Self::new(self.input, self.contexts, self.heritage, locals) + Self::new( + self.input, + self.contexts, + self.heritage, + self.integrations, + locals, + ) } // Takes a Context and generates the relevant implementations. @@ -83,19 +96,19 @@ impl<'a> Generator<'a> { self.impl_template(ctx, &mut buf); self.impl_display(&mut buf); - if cfg!(feature = "iron") { + if self.integrations.iron { self.impl_modifier_response(&mut buf); } - if cfg!(feature = "rocket") { + if self.integrations.rocket { self.impl_rocket_responder(&mut buf); } - if cfg!(feature = "actix-web") { + if self.integrations.actix { self.impl_actix_web_responder(&mut buf); } - if cfg!(feature = "gotham") { + if self.integrations.gotham { self.impl_gotham_into_response(&mut buf); } - if cfg!(feature = "warp") { + if self.integrations.warp { self.impl_warp_reply(&mut buf); } buf.buf diff --git a/askama_shared/src/heritage.rs b/askama_shared/src/heritage.rs new file mode 100644 index 0000000..97580d5 --- /dev/null +++ b/askama_shared/src/heritage.rs @@ -0,0 +1,111 @@ +use std::collections::HashMap; +use std::path::{Path, PathBuf}; + +use crate::parser::{Expr, Macro, Node}; +use crate::Config; + +pub struct Heritage<'a> { + pub root: &'a Context<'a>, + pub blocks: BlockAncestry<'a>, +} + +impl<'a> Heritage<'a> { + pub fn new<'n>( + mut ctx: &'n Context<'n>, + contexts: &'n HashMap<&'n PathBuf, Context<'n>>, + ) -> Heritage<'n> { + let mut blocks: BlockAncestry<'n> = ctx + .blocks + .iter() + .map(|(name, def)| (*name, vec![(ctx, *def)])) + .collect(); + + while let Some(ref path) = ctx.extends { + ctx = &contexts[&path]; + for (name, def) in &ctx.blocks { + blocks + .entry(name) + .or_insert_with(|| vec![]) + .push((ctx, def)); + } + } + + Heritage { root: ctx, blocks } + } +} + +type BlockAncestry<'a> = HashMap<&'a str, Vec<(&'a Context<'a>, &'a Node<'a>)>>; + +pub struct Context<'a> { + pub nodes: &'a [Node<'a>], + pub extends: Option<PathBuf>, + pub blocks: HashMap<&'a str, &'a Node<'a>>, + pub macros: HashMap<&'a str, &'a Macro<'a>>, + pub imports: HashMap<&'a str, PathBuf>, +} + +impl<'a> Context<'a> { + pub fn new<'n>(config: &Config, path: &Path, nodes: &'n [Node<'n>]) -> Context<'n> { + let mut extends = None; + let mut blocks = Vec::new(); + let mut macros = HashMap::new(); + let mut imports = HashMap::new(); + + for n in nodes { + match n { + Node::Extends(Expr::StrLit(extends_path)) => match extends { + Some(_) => panic!("multiple extend blocks found"), + None => { + extends = Some(config.find_template(extends_path, Some(path))); + } + }, + def @ Node::BlockDef(_, _, _, _) => { + blocks.push(def); + } + Node::Macro(name, m) => { + macros.insert(*name, m); + } + Node::Import(_, import_path, scope) => { + let path = config.find_template(import_path, Some(path)); + imports.insert(*scope, path); + } + _ => {} + } + } + + let mut check_nested = 0; + let mut nested_blocks = Vec::new(); + while check_nested < blocks.len() { + if let Node::BlockDef(_, _, ref nodes, _) = blocks[check_nested] { + for n in nodes { + if let def @ Node::BlockDef(_, _, _, _) = n { + nested_blocks.push(def); + } + } + } else { + panic!("non block found in list of blocks"); + } + blocks.append(&mut nested_blocks); + check_nested += 1; + } + + let blocks: HashMap<_, _> = blocks + .iter() + .map(|def| { + if let Node::BlockDef(_, name, _, _) = def { + (*name, *def) + } else { + unreachable!() + } + }) + .collect(); + + Context { + nodes, + extends, + blocks, + macros, + imports, + } + } +} diff --git a/askama_shared/src/lib.rs b/askama_shared/src/lib.rs index fa4a9a0..0e8664d 100644 --- a/askama_shared/src/lib.rs +++ b/askama_shared/src/lib.rs @@ -15,8 +15,14 @@ pub use askama_escape::MarkupDisplay; mod error; pub use crate::error::{Error, Result}; pub mod filters; +#[doc(hidden)] +pub mod generator; pub mod helpers; +#[doc(hidden)] +pub mod heritage; +#[doc(hidden)] pub mod input; +#[doc(hidden)] pub mod parser; #[derive(Debug)] @@ -240,6 +246,31 @@ where vals.iter().map(|s| s.to_string()).collect() } +#[allow(clippy::match_wild_err_arm)] +pub fn get_template_source(tpl_path: &Path) -> String { + match fs::read_to_string(tpl_path) { + Err(_) => panic!( + "unable to open template file '{}'", + tpl_path.to_str().unwrap() + ), + Ok(mut source) => { + if source.ends_with('\n') { + let _ = source.pop(); + } + source + } + } +} + +#[derive(Clone, Copy, Debug)] +pub struct Integrations { + pub actix: bool, + pub gotham: bool, + pub iron: bool, + pub rocket: bool, + pub warp: bool, +} + static CONFIG_FILE_NAME: &str = "askama.toml"; static DEFAULT_SYNTAX_NAME: &str = "default"; static DEFAULT_ESCAPERS: &[(&[&str], &str)] = &[ @@ -256,6 +287,12 @@ mod tests { use std::path::{Path, PathBuf}; #[test] + fn get_source() { + let path = Config::new("").find_template("b.html", None); + assert_eq!(get_template_source(&path), "bar"); + } + + #[test] fn test_default_config() { let mut root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); root.push("templates"); @@ -293,7 +330,7 @@ mod tests { fn find_relative_nonexistent() { let config = Config::new(""); let root = config.find_template("a.html", None); - config.find_template("b.html", Some(&root)); + config.find_template("c.html", Some(&root)); } #[test] |