diff options
author | David Tolnay <dtolnay@gmail.com> | 2018-04-12 11:42:36 -0700 |
---|---|---|
committer | Dirkjan Ochtman <dirkjan@ochtman.nl> | 2018-04-12 21:14:49 +0200 |
commit | 1267e966f47893bdaa9ab0f3f15d32110eddfd6e (patch) | |
tree | 0f84c80d1e3bcdff4bdf898a4db4e0da934166e8 /askama_derive/src/input.rs | |
parent | e56a0d034b4cbd146c374366964c6c7f4587539f (diff) | |
download | askama-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/input.rs')
-rw-r--r-- | askama_derive/src/input.rs | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/askama_derive/src/input.rs b/askama_derive/src/input.rs new file mode 100644 index 0000000..b2b815b --- /dev/null +++ b/askama_derive/src/input.rs @@ -0,0 +1,175 @@ +use shared::path; + +use std::path::{Path, PathBuf}; + +use syn; + + +pub struct TemplateInput<'a> { + pub ast: &'a syn::DeriveInput, + pub meta: TemplateMeta, + pub path: PathBuf, + pub source: String, +} + +impl<'a> TemplateInput<'a> { + pub fn new(ast: &'a syn::DeriveInput) -> TemplateInput<'a> { + let meta = TemplateMeta::new(ast); + let (path, source) = match meta.source { + Source::Source(ref s) => { + let path = match meta.ext { + Some(ref v) => PathBuf::from(format!("_.{}", v)), + None => PathBuf::new(), + }; + (path, s.clone()) + }, + Source::Path(ref s) => { + let path = path::find_template_from_path(s, None); + let src = path::get_template_source(&path); + (path, src) + }, + }; + TemplateInput { ast, meta, path, source } + } +} + +// Holds metadata for the template, based on the `template()` attribute. +pub struct TemplateMeta { + source: Source, + pub print: Print, + pub escaping: EscapeMode, + pub ext: Option<String>, +} + +impl TemplateMeta { + fn new(ast: &syn::DeriveInput) -> TemplateMeta { + let attr = ast.attrs + .iter() + .find(|a| a.interpret_meta().unwrap().name() == "template"); + if attr.is_none() { + let msg = format!("'template' attribute not found on struct '{}'", + ast.ident.as_ref()); + panic!(msg); + } + + let attr = attr.unwrap(); + let mut source = None; + let mut print = Print::None; + let mut escaping = None; + let mut ext = None; + if let syn::Meta::List(ref inner) = attr.interpret_meta().unwrap() { + for nm_item in inner.nested.iter() { + if let syn::NestedMeta::Meta(ref item) = *nm_item { + if let syn::Meta::NameValue(ref pair) = *item { + match pair.ident.as_ref() { + "path" => if let syn::Lit::Str(ref s) = pair.lit { + if source.is_some() { + panic!("must specify 'source' or 'path', not both"); + } + source = Some(Source::Path(s.value())); + } else { + panic!("template path must be string literal"); + }, + "source" => if let syn::Lit::Str(ref s) = pair.lit { + if source.is_some() { + panic!("must specify 'source' or 'path', not both"); + } + source = Some(Source::Source(s.value())); + } else { + panic!("template source must be string literal"); + }, + "print" => if let syn::Lit::Str(ref s) = pair.lit { + print = s.value().into(); + } else { + panic!("print value must be string literal"); + }, + "escape" => if let syn::Lit::Str(ref s) = pair.lit { + escaping = Some(s.value().into()); + } else { + panic!("escape value must be string literal"); + }, + "ext" => if let syn::Lit::Str(ref s) = pair.lit { + ext = Some(s.value()); + } else { + panic!("ext value must be string literal"); + }, + attr => panic!("unsupported annotation key '{}' found", attr), + } + } + } + } + } + + let source = source.expect("template path or source not found in attributes"); + match (&source, ext.is_some()) { + (&Source::Path(_), true) => { + panic!("'ext' attribute cannot be used with 'path' attribute") + }, + (&Source::Source(_), false) => { + panic!("must include 'ext' attribute when using 'source' attribute") + }, + _ => {}, + } + let escaping = match escaping { + Some(m) => m, + None => { + let ext = match source { + Source::Path(ref p) => + Path::new(p).extension().map(|s| s.to_str().unwrap()).unwrap_or(""), + Source::Source(_) => ext.as_ref().unwrap(), // Already panicked if None + }; + if HTML_EXTENSIONS.contains(&ext) { + EscapeMode::Html + } else { + EscapeMode::None + } + }, + }; + TemplateMeta { source, print, escaping, ext } + } +} + +enum Source { + Path(String), + Source(String), +} + +#[derive(PartialEq)] +pub enum EscapeMode { + Html, + None, +} + +impl From<String> for EscapeMode { + fn from(s: String) -> EscapeMode { + use self::EscapeMode::*; + match s.as_ref() { + "html" => Html, + "none" => None, + v => panic!("invalid value for escape option: {}", v), + } + } +} + +#[derive(PartialEq)] +pub enum Print { + All, + Ast, + Code, + None, +} + +impl From<String> for Print { + fn from(s: String) -> Print { + use self::Print::*; + match s.as_ref() { + "all" => All, + "ast" => Ast, + "code" => Code, + "none" => None, + v => panic!("invalid value for print option: {}", v), + } + } +} + +const HTML_EXTENSIONS: [&str; 3] = ["html", "htm", "xml"]; |