aboutsummaryrefslogtreecommitdiffstats
path: root/askama_derive/src/lib.rs
diff options
context:
space:
mode:
authorLibravatar David Tolnay <dtolnay@gmail.com>2018-04-12 11:42:36 -0700
committerLibravatar Dirkjan Ochtman <dirkjan@ochtman.nl>2018-04-12 21:14:49 +0200
commit1267e966f47893bdaa9ab0f3f15d32110eddfd6e (patch)
tree0f84c80d1e3bcdff4bdf898a4db4e0da934166e8 /askama_derive/src/lib.rs
parente56a0d034b4cbd146c374366964c6c7f4587539f (diff)
downloadaskama-1267e966f47893bdaa9ab0f3f15d32110eddfd6e.tar.gz
askama-1267e966f47893bdaa9ab0f3f15d32110eddfd6e.tar.bz2
askama-1267e966f47893bdaa9ab0f3f15d32110eddfd6e.zip
Isolate proc-macro dependency to the proc macro
The dependency of askama on askama_shared on syn on libproc_macro used to mean libproc_macro would be dynamically linked into any crate using askama. We want only askama_derive to have a runtime dependency on proc macro. This commit moves all proc macro code from askama_shared into askama_derive so that the askama crate no longer dynamically links libproc_macro.
Diffstat (limited to 'askama_derive/src/lib.rs')
-rw-r--r--askama_derive/src/lib.rs72
1 files changed, 71 insertions, 1 deletions
diff --git a/askama_derive/src/lib.rs b/askama_derive/src/lib.rs
index 7c92fdc..df76507 100644
--- a/askama_derive/src/lib.rs
+++ b/askama_derive/src/lib.rs
@@ -1,8 +1,24 @@
extern crate askama_shared as shared;
+#[macro_use]
+extern crate nom;
extern crate proc_macro;
+#[macro_use]
+extern crate quote;
+#[macro_use(parse_quote)]
extern crate syn;
+mod input;
+mod generator;
+mod parser;
+
+use input::Print;
+use parser::{Macro, Node};
use proc_macro::TokenStream;
+use shared::path;
+
+use std::borrow::Cow;
+use std::collections::HashMap;
+use std::path::Path;
#[proc_macro_derive(Template, attributes(template))]
pub fn derive_template(input: TokenStream) -> TokenStream {
@@ -11,5 +27,59 @@ pub fn derive_template(input: TokenStream) -> TokenStream {
syn::Data::Struct(ref data) => data,
_ => panic!("#[derive(Template)] can only be used with structs"),
};
- shared::build_template(&ast).parse().unwrap()
+ 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 data = input::TemplateInput::new(ast);
+ let nodes = parser::parse(data.source.as_ref());
+ let imports = Imports::new(&nodes, &data.path);
+ if data.meta.print == Print::Ast || data.meta.print == Print::All {
+ println!("{:?}", nodes);
+ }
+ let code = generator::generate(&data, &nodes, &imports.macro_map());
+ if data.meta.print == Print::Code || data.meta.print == Print::All {
+ println!("{}", code);
+ }
+ code
+}
+
+struct Imports<'a> {
+ sources: HashMap<&'a str, Cow<'a, str>>,
+}
+
+impl<'a> Imports<'a> {
+ fn new(parent_nodes: &'a [Node], parent_path: &'a Path) -> Imports<'a> {
+ let sources = parent_nodes.iter().filter_map(|n| {
+ match *n {
+ Node::Import(_, import_path, scope) => {
+ let path = path::find_template_from_path(import_path, Some(parent_path));
+ let src = path::get_template_source(&path);
+ Some((scope, Cow::Owned(src)))
+ },
+ _ => None,
+ }
+ }).collect();
+ Imports { sources }
+ }
+
+ fn macro_map(&'a self) -> HashMap<(&'a str, &'a str), Macro<'a>> {
+ let mut macro_map = HashMap::new();
+ for (scope, s) in &self.sources {
+ for n in parser::parse(s.as_ref()) {
+ match n {
+ Node::Macro(name, m) => macro_map.insert((*scope, name), m),
+ _ => None,
+ };
+ }
+ }
+ macro_map
+ }
}