Hide askama_derive dependency inside askama (fixes #2)

This commit is contained in:
Dirkjan Ochtman 2017-03-06 22:40:04 +01:00
parent 0efd0c5cc5
commit 664398b225
15 changed files with 131 additions and 128 deletions

6
Cargo.lock generated
View File

@ -3,22 +3,20 @@ name = "askama_testing"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"askama 0.2.1", "askama 0.2.1",
"askama_derive 0.2.1",
] ]
[[package]] [[package]]
name = "askama" name = "askama"
version = "0.2.1" version = "0.2.1"
dependencies = [ dependencies = [
"nom 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "askama_derive 0.2.1",
"syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "askama_derive" name = "askama_derive"
version = "0.2.1" version = "0.2.1"
dependencies = [ dependencies = [
"askama 0.2.1", "nom 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]

View File

@ -79,8 +79,7 @@ In any Rust file inside your crate, add the following:
```rust ```rust
#[macro_use] #[macro_use]
extern crate askama_derive; // for the custom derive implementation extern crate askama; // for the Template trait and custom derive macro
extern crate askama; // for the Template trait
use askama::Template; // bring trait in scope use askama::Template; // bring trait in scope

View File

@ -16,5 +16,4 @@ readme = "../README.md"
travis-ci = { repository = "djc/askama" } travis-ci = { repository = "djc/askama" }
[dependencies] [dependencies]
nom = "2.1" askama_derive = { path = "../askama_derive", version = "0.2.1" }
syn = "0.11"

View File

@ -182,8 +182,12 @@
//! Expressions can be grouped using parentheses. //! Expressions can be grouped using parentheses.
#[macro_use] #[macro_use]
extern crate nom; extern crate askama_derive;
extern crate syn;
use std::env;
use std::fs::{self, DirEntry};
use std::io;
use std::path::{Path, PathBuf};
/// Main `Template` trait; implementations are generally derived /// Main `Template` trait; implementations are generally derived
pub trait Template { pub trait Template {
@ -197,73 +201,42 @@ pub trait Template {
} }
} }
mod generator;
mod parser;
mod path;
pub mod filters; pub mod filters;
pub use path::rerun_if_templates_changed; pub use askama_derive::*;
// Holds metadata for the template, based on the `template()` attribute. // Duplicates askama_derive::path::template_dir()
struct TemplateMeta { fn template_dir() -> PathBuf {
path: String, let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
print: String, path.push("templates");
path
} }
// Returns a `TemplateMeta` based on the `template()` attribute data found fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> {
// in the parsed struct or enum. Will panic if it does not find the required if dir.is_dir() {
// template path, or if the `print` key has an unexpected value. for entry in try!(fs::read_dir(dir)) {
fn get_template_meta(ast: &syn::DeriveInput) -> TemplateMeta { let entry = try!(entry);
let mut path = None; let path = entry.path();
let mut print = "none".to_string(); if path.is_dir() {
let attr = ast.attrs.iter().find(|a| a.name() == "template").unwrap(); try!(visit_dirs(&path, cb));
if let syn::MetaItem::List(_, ref inner) = attr.value { } else {
for nm_item in inner { cb(&entry);
if let syn::NestedMetaItem::MetaItem(ref item) = *nm_item {
if let syn::MetaItem::NameValue(ref key, ref val) = *item {
match key.as_ref() {
"path" => if let syn::Lit::Str(ref s, _) = *val {
path = Some(s.clone());
} else {
panic!("template path must be string literal");
},
"print" => if let syn::Lit::Str(ref s, _) = *val {
print = s.clone();
} else {
panic!("print value must be string literal");
},
_ => { panic!("unsupported annotation key found") }
}
}
} }
} }
} }
if path.is_none() { Ok(())
panic!("template path not found in struct attributes");
}
TemplateMeta { path: path.unwrap(), print: print }
} }
/// Takes a `syn::DeriveInput` and generates source code for it /// Build script helper to rebuild crates if contained templates have changed
/// ///
/// Reads the metadata from the `template()` attribute to get the template /// Iterates over all files in the template dir (`templates` in
/// metadata, then fetches the source from the filesystem. The source is /// `CARGO_MANIFEST_DIR`) and writes a `cargo:rerun-if-changed=` line for each
/// parsed, and the parse tree is fed to the code generator. Will print /// of them to stdout.
/// the parse tree and/or generated source according to the `print` key's ///
/// value as passed to the `template()` attribute. /// This helper method can be used in build scripts (`build.rs`) in crates
pub fn build_template(ast: &syn::DeriveInput) -> String { /// that have templates, to make sure the crate gets rebuilt when template
let meta = get_template_meta(ast); /// source code changes.
let mut src = path::get_template_source(&meta.path); pub fn rerun_if_templates_changed() {
if src.ends_with('\n') { visit_dirs(&template_dir(), &|e: &DirEntry| {
let _ = src.pop(); println!("cargo:rerun-if-changed={}", e.path().to_str().unwrap());
} }).unwrap();
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
} }

View File

@ -1,49 +0,0 @@
use std::env;
use std::fs::{self, DirEntry, File};
use std::io::{self, 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_file: &str) -> String {
let mut path = template_dir();
path.push(Path::new(tpl_file));
let mut f = File::open(path).unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
s
}
fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> {
if dir.is_dir() {
for entry in try!(fs::read_dir(dir)) {
let entry = try!(entry);
let path = entry.path();
if path.is_dir() {
try!(visit_dirs(&path, cb));
} else {
cb(&entry);
}
}
}
Ok(())
}
/// Build script helper to rebuild crates if contained templates have changed
///
/// Iterates over all files in the template dir (`templates` in
/// `CARGO_MANIFEST_DIR`) and writes a `cargo:rerun-if-changed=` line for each
/// of them to stdout.
///
/// This helper method can be used in build scripts (`build.rs`) in crates
/// that have templates, to make sure the crate gets rebuilt when template
/// source code changes.
pub fn rerun_if_templates_changed() {
visit_dirs(&template_dir(), &|e: &DirEntry| {
println!("cargo:rerun-if-changed={}", e.path().to_str().unwrap());
}).unwrap();
}

View File

@ -12,5 +12,5 @@ workspace = ".."
proc-macro = true proc-macro = true
[dependencies] [dependencies]
askama = { path = "../askama", version = "0.2.1" } nom = "2.1"
syn = "0.11" syn = "0.11"

View File

@ -1,9 +1,78 @@
extern crate askama; #[macro_use]
extern crate nom;
extern crate proc_macro; extern crate proc_macro;
extern crate syn; extern crate syn;
use proc_macro::TokenStream; use proc_macro::TokenStream;
mod generator;
mod parser;
mod path;
// Holds metadata for the template, based on the `template()` attribute.
struct TemplateMeta {
path: String,
print: String,
}
// Returns a `TemplateMeta` based on the `template()` attribute data found
// in the parsed struct or enum. Will panic if it does not find the required
// template path, or if the `print` key has an unexpected value.
fn get_template_meta(ast: &syn::DeriveInput) -> TemplateMeta {
let mut path = None;
let mut print = "none".to_string();
let attr = ast.attrs.iter().find(|a| a.name() == "template").unwrap();
if let syn::MetaItem::List(_, ref inner) = attr.value {
for nm_item in inner {
if let syn::NestedMetaItem::MetaItem(ref item) = *nm_item {
if let syn::MetaItem::NameValue(ref key, ref val) = *item {
match key.as_ref() {
"path" => if let syn::Lit::Str(ref s, _) = *val {
path = Some(s.clone());
} else {
panic!("template path must be string literal");
},
"print" => if let syn::Lit::Str(ref s, _) = *val {
print = s.clone();
} else {
panic!("print value must be string literal");
},
_ => { panic!("unsupported annotation key found") }
}
}
}
}
}
if path.is_none() {
panic!("template path not found in struct attributes");
}
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 mut src = path::get_template_source(&meta.path);
if src.ends_with('\n') {
let _ = src.pop();
}
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))] #[proc_macro_derive(Template, attributes(template))]
pub fn derive_template(input: TokenStream) -> TokenStream { pub fn derive_template(input: TokenStream) -> TokenStream {
let ast = syn::parse_derive_input(&input.to_string()).unwrap(); let ast = syn::parse_derive_input(&input.to_string()).unwrap();
@ -11,5 +80,5 @@ pub fn derive_template(input: TokenStream) -> TokenStream {
syn::Body::Struct(ref data) => data, syn::Body::Struct(ref data) => data,
_ => panic!("#[derive(Template)] can only be used with structs"), _ => panic!("#[derive(Template)] can only be used with structs"),
}; };
askama::build_template(&ast).parse().unwrap() build_template(&ast).parse().unwrap()
} }

19
askama_derive/src/path.rs Normal file
View File

@ -0,0 +1,19 @@
use std::env;
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_file: &str) -> String {
let mut path = template_dir();
path.push(Path::new(tpl_file));
let mut f = File::open(path).unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
s
}

View File

@ -7,7 +7,6 @@ build = "build.rs"
[dependencies] [dependencies]
askama = { path = "../askama", version = "*" } askama = { path = "../askama", version = "*" }
askama_derive = { path = "../askama_derive", version = "*" }
[build-dependencies] [build-dependencies]
askama = { path = "../askama", version = "*" } askama = { path = "../askama", version = "*" }

View File

@ -1,5 +1,4 @@
#[macro_use] #[macro_use]
extern crate askama_derive;
extern crate askama; extern crate askama;
use askama::Template; use askama::Template;

View File

@ -1,6 +1,5 @@
extern crate askama;
#[macro_use] #[macro_use]
extern crate askama_derive; extern crate askama;
use askama::Template; use askama::Template;

View File

@ -1,6 +1,5 @@
extern crate askama;
#[macro_use] #[macro_use]
extern crate askama_derive; extern crate askama;
use askama::Template; use askama::Template;

View File

@ -1,5 +1,4 @@
#[macro_use] #[macro_use]
extern crate askama_derive;
extern crate askama; extern crate askama;
use askama::Template; use askama::Template;