mirror of
https://github.com/askama-rs/askama.git
synced 2025-10-02 15:25:19 +00:00
Reorganize code order in askama_derive modules
This commit is contained in:
parent
946e8c7587
commit
d40f5a3ea6
@ -9,6 +9,88 @@ use std::collections::HashSet;
|
|||||||
|
|
||||||
use syn;
|
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> {
|
struct Generator<'a> {
|
||||||
buf: String,
|
buf: String,
|
||||||
indent: u8,
|
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()
|
|
||||||
}
|
|
||||||
|
@ -10,10 +10,36 @@ mod generator;
|
|||||||
mod parser;
|
mod parser;
|
||||||
mod path;
|
mod path;
|
||||||
|
|
||||||
// Holds metadata for the template, based on the `template()` attribute.
|
#[proc_macro_derive(Template, attributes(template))]
|
||||||
struct TemplateMeta {
|
pub fn derive_template(input: TokenStream) -> TokenStream {
|
||||||
path: String,
|
let ast = syn::parse_derive_input(&input.to_string()).unwrap();
|
||||||
print: String,
|
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
|
// 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 }
|
TemplateMeta { path: path.unwrap(), print: print }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a `syn::DeriveInput` and generates source code for it
|
// Holds metadata for the template, based on the `template()` attribute.
|
||||||
///
|
struct TemplateMeta {
|
||||||
/// Reads the metadata from the `template()` attribute to get the template
|
path: String,
|
||||||
/// metadata, then fetches the source from the filesystem. The source is
|
print: String,
|
||||||
/// 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()
|
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,23 @@ use std::fs::File;
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
fn template_dir() -> PathBuf {
|
pub fn get_template_source(tpl_path: &Path) -> String {
|
||||||
let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
let mut path = template_dir();
|
||||||
path.push("templates");
|
path.push(tpl_path);
|
||||||
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 {
|
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 {
|
fn template_dir() -> PathBuf {
|
||||||
let mut path = template_dir();
|
let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||||
path.push(tpl_path);
|
path.push("templates");
|
||||||
let mut f = match File::open(&path) {
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user