diff options
Diffstat (limited to 'askama_derive/src')
-rw-r--r-- | askama_derive/src/generator.rs | 164 | ||||
-rw-r--r-- | askama_derive/src/lib.rs | 68 | ||||
-rw-r--r-- | askama_derive/src/path.rs | 42 |
3 files changed, 137 insertions, 137 deletions
diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index 6c53a89..de2e6f2 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -9,6 +9,88 @@ use std::collections::HashSet; use syn; +pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec<Node>) -> String { + let mut base: Option<Expr> = None; + let mut blocks = Vec::new(); + let mut block_names = Vec::new(); + let mut content = Vec::new(); + for n in nodes.drain(..) { + match n { + Node::Extends(path) => { + match base { + Some(_) => panic!("multiple extend blocks found"), + None => { base = Some(path); }, + } + }, + Node::BlockDef(ws1, name, _, ws2) => { + blocks.push(n); + block_names.push(name); + content.push(Node::Block(ws1, name, ws2)); + }, + _ => { content.push(n); }, + } + } + + let mut locals = HashSet::new(); + let mut gen = Generator::default(&mut locals); + if !blocks.is_empty() { + let trait_name = trait_name_for_path(&base, path); + if base.is_none() { + gen.define_trait(&trait_name, &block_names); + } else { + gen.deref_to_parent(ast, &get_parent_type(ast).unwrap()); + } + + let trait_nodes = if base.is_none() { Some(&content[..]) } else { None }; + gen.impl_trait(ast, &trait_name, &blocks, trait_nodes); + gen.impl_template_for_trait(ast, base.is_some()); + } else { + gen.impl_template(ast, &content); + } + gen.result() +} + +fn trait_name_for_path(base: &Option<Expr>, path: &str) -> String { + let rooted_path = match *base { + Some(Expr::StrLit(user_path)) => { + path::find_template_from_path(user_path, Some(path)) + }, + _ => { + let mut path_buf = PathBuf::new(); + path_buf.push(&path); + path_buf + }, + }; + + let mut res = String::new(); + res.push_str("TraitFrom"); + for c in rooted_path.to_string_lossy().chars() { + if c.is_alphanumeric() { + res.push(c); + } else { + res.push_str(&format!("{:x}", c as u32)); + } + } + res +} + +fn get_parent_type(ast: &syn::DeriveInput) -> Option<&syn::Ty> { + match ast.body { + syn::Body::Struct(ref data) => { + data.fields().iter().filter_map(|f| { + f.ident.as_ref().and_then(|name| { + if name.as_ref() == "_parent" { + Some(&f.ty) + } else { + None + } + }) + }) + }, + _ => panic!("derive(Template) only works for struct items"), + }.next() +} + struct Generator<'a> { buf: String, indent: u8, @@ -451,85 +533,3 @@ impl<'a> Generator<'a> { } } - -fn trait_name_for_path(base: &Option<Expr>, path: &str) -> String { - let rooted_path = match *base { - Some(Expr::StrLit(user_path)) => { - path::find_template_from_path(user_path, Some(path)) - }, - _ => { - let mut path_buf = PathBuf::new(); - path_buf.push(&path); - path_buf - }, - }; - - let mut res = String::new(); - res.push_str("TraitFrom"); - for c in rooted_path.to_string_lossy().chars() { - if c.is_alphanumeric() { - res.push(c); - } else { - res.push_str(&format!("{:x}", c as u32)); - } - } - res -} - -fn get_parent_type(ast: &syn::DeriveInput) -> Option<&syn::Ty> { - match ast.body { - syn::Body::Struct(ref data) => { - data.fields().iter().filter_map(|f| { - f.ident.as_ref().and_then(|name| { - if name.as_ref() == "_parent" { - Some(&f.ty) - } else { - None - } - }) - }) - }, - _ => panic!("derive(Template) only works for struct items"), - }.next() -} - -pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec<Node>) -> String { - let mut base: Option<Expr> = None; - let mut blocks = Vec::new(); - let mut block_names = Vec::new(); - let mut content = Vec::new(); - for n in nodes.drain(..) { - match n { - Node::Extends(path) => { - match base { - Some(_) => panic!("multiple extend blocks found"), - None => { base = Some(path); }, - } - }, - Node::BlockDef(ws1, name, _, ws2) => { - blocks.push(n); - block_names.push(name); - content.push(Node::Block(ws1, name, ws2)); - }, - _ => { content.push(n); }, - } - } - - let mut locals = HashSet::new(); - let mut gen = Generator::default(&mut locals); - if !blocks.is_empty() { - let trait_name = trait_name_for_path(&base, path); - if base.is_none() { - gen.define_trait(&trait_name, &block_names); - } else { - gen.deref_to_parent(ast, &get_parent_type(ast).unwrap()); - } - - let trait_nodes = if base.is_none() { Some(&content[..]) } else { None }; - gen.impl_trait(ast, &trait_name, &blocks, trait_nodes); - gen.impl_template_for_trait(ast, base.is_some()); - } else { - gen.impl_template(ast, &content); - } - gen.result() -} diff --git a/askama_derive/src/lib.rs b/askama_derive/src/lib.rs index 5ee5a4c..ad0e218 100644 --- a/askama_derive/src/lib.rs +++ b/askama_derive/src/lib.rs @@ -10,10 +10,36 @@ mod generator; mod parser; mod path; -// Holds metadata for the template, based on the `template()` attribute. -struct TemplateMeta { - path: String, - print: String, +#[proc_macro_derive(Template, attributes(template))] +pub fn derive_template(input: TokenStream) -> TokenStream { + let ast = syn::parse_derive_input(&input.to_string()).unwrap(); + match ast.body { + syn::Body::Struct(ref data) => data, + _ => panic!("#[derive(Template)] can only be used with structs"), + }; + build_template(&ast).parse().unwrap() +} + +/// Takes a `syn::DeriveInput` and generates source code for it +/// +/// Reads the metadata from the `template()` attribute to get the template +/// metadata, then fetches the source from the filesystem. The source is +/// parsed, and the parse tree is fed to the code generator. Will print +/// the parse tree and/or generated source according to the `print` key's +/// value as passed to the `template()` attribute. +fn build_template(ast: &syn::DeriveInput) -> String { + let meta = get_template_meta(ast); + let path = path::find_template_from_path(&meta.path, None); + let src = path::get_template_source(&path); + let nodes = parser::parse(&src); + if meta.print == "ast" || meta.print == "all" { + println!("{:?}", nodes); + } + let code = generator::generate(ast, &meta.path, nodes); + if meta.print == "code" || meta.print == "all" { + println!("{}", code); + } + code } // Returns a `TemplateMeta` based on the `template()` attribute data found @@ -57,34 +83,8 @@ fn get_template_meta(ast: &syn::DeriveInput) -> TemplateMeta { TemplateMeta { path: path.unwrap(), print: print } } -/// Takes a `syn::DeriveInput` and generates source code for it -/// -/// Reads the metadata from the `template()` attribute to get the template -/// metadata, then fetches the source from the filesystem. The source is -/// parsed, and the parse tree is fed to the code generator. Will print -/// the parse tree and/or generated source according to the `print` key's -/// value as passed to the `template()` attribute. -fn build_template(ast: &syn::DeriveInput) -> String { - let meta = get_template_meta(ast); - let path = path::find_template_from_path(&meta.path, None); - let src = path::get_template_source(&path); - let nodes = parser::parse(&src); - if meta.print == "ast" || meta.print == "all" { - println!("{:?}", nodes); - } - let code = generator::generate(ast, &meta.path, nodes); - if meta.print == "code" || meta.print == "all" { - println!("{}", code); - } - code -} - -#[proc_macro_derive(Template, attributes(template))] -pub fn derive_template(input: TokenStream) -> TokenStream { - let ast = syn::parse_derive_input(&input.to_string()).unwrap(); - match ast.body { - syn::Body::Struct(ref data) => data, - _ => panic!("#[derive(Template)] can only be used with structs"), - }; - build_template(&ast).parse().unwrap() +// Holds metadata for the template, based on the `template()` attribute. +struct TemplateMeta { + path: String, + print: String, } diff --git a/askama_derive/src/path.rs b/askama_derive/src/path.rs index b51f5f3..03ab20d 100644 --- a/askama_derive/src/path.rs +++ b/askama_derive/src/path.rs @@ -3,10 +3,23 @@ use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; -fn template_dir() -> PathBuf { - let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - path.push("templates"); - path +pub fn get_template_source(tpl_path: &Path) -> String { + let mut path = template_dir(); + path.push(tpl_path); + let mut f = match File::open(&path) { + Err(_) => { + let msg = format!("unable to open template file '{}'", + &path.to_str().unwrap()); + panic!(msg); + }, + Ok(f) => f, + }; + let mut s = String::new(); + f.read_to_string(&mut s).unwrap(); + if s.ends_with('\n') { + let _ = s.pop(); + } + s } pub fn find_template_from_path<'a>(path: &str, start_at: Option<&str>) -> PathBuf { @@ -33,23 +46,10 @@ pub fn find_template_from_path<'a>(path: &str, start_at: Option<&str>) -> PathBu } } -pub fn get_template_source(tpl_path: &Path) -> String { - let mut path = template_dir(); - path.push(tpl_path); - let mut f = match File::open(&path) { - Err(_) => { - let msg = format!("unable to open template file '{}'", - &path.to_str().unwrap()); - panic!(msg); - }, - Ok(f) => f, - }; - let mut s = String::new(); - f.read_to_string(&mut s).unwrap(); - if s.ends_with('\n') { - let _ = s.pop(); - } - s +fn template_dir() -> PathBuf { + let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + path.push("templates"); + path } #[cfg(test)] |