From a51433677d98ebdba2008315b6d79565530fdf7e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 11 Apr 2022 16:06:55 +0200 Subject: Parse template derive args outside of TemplateInput::new --- askama_shared/src/generator.rs | 111 +++++++++++++++++++++++++++++++++++++++- askama_shared/src/input.rs | 112 ++++++----------------------------------- 2 files changed, 125 insertions(+), 98 deletions(-) (limited to 'askama_shared') diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index 7eb5fbe..a09631c 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -28,9 +28,10 @@ pub fn derive_template(input: TokenStream) -> TokenStream { /// 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) -> Result { + let template_args = TemplateArgs::new(ast)?; let config_toml = read_config_file()?; let config = Config::new(&config_toml)?; - let input = TemplateInput::new(ast, &config)?; + let input = TemplateInput::new(ast, &config, template_args)?; let source: String = match input.source { Source::Source(ref s) => s.clone(), Source::Path(_) => get_template_source(&input.path)?, @@ -74,6 +75,114 @@ fn build_template(ast: &syn::DeriveInput) -> Result { Ok(code) } +#[derive(Default)] +pub(crate) struct TemplateArgs { + pub(crate) source: Option, + pub(crate) print: Print, + pub(crate) escaping: Option, + pub(crate) ext: Option, + pub(crate) syntax: Option, +} + +impl TemplateArgs { + fn new(ast: &'_ syn::DeriveInput) -> Result { + // Check that an attribute called `template()` exists once and that it is + // the proper type (list). + let mut template_args = None; + for attr in &ast.attrs { + let ident = match attr.path.get_ident() { + Some(ident) => ident, + None => continue, + }; + + if ident == "template" { + if template_args.is_some() { + return Err("duplicated 'template' attribute".into()); + } + + match attr.parse_meta() { + Ok(syn::Meta::List(syn::MetaList { nested, .. })) => { + template_args = Some(nested); + } + Ok(_) => return Err("'template' attribute must be a list".into()), + Err(e) => return Err(format!("unable to parse attribute: {}", e).into()), + } + } + } + let template_args = + template_args.ok_or_else(|| CompileError::from("no attribute 'template' found"))?; + + let mut args = Self::default(); + // Loop over the meta attributes and find everything that we + // understand. Return a CompileError if something is not right. + // `source` contains an enum that can represent `path` or `source`. + for item in template_args { + let pair = match item { + syn::NestedMeta::Meta(syn::Meta::NameValue(ref pair)) => pair, + _ => { + return Err(format!( + "unsupported attribute argument {:?}", + item.to_token_stream() + ) + .into()) + } + }; + let ident = match pair.path.get_ident() { + Some(ident) => ident, + None => unreachable!("not possible in syn::Meta::NameValue(…)"), + }; + + if ident == "path" { + if let syn::Lit::Str(ref s) = pair.lit { + if args.source.is_some() { + return Err("must specify 'source' or 'path', not both".into()); + } + args.source = Some(Source::Path(s.value())); + } else { + return Err("template path must be string literal".into()); + } + } else if ident == "source" { + if let syn::Lit::Str(ref s) = pair.lit { + if args.source.is_some() { + return Err("must specify 'source' or 'path', not both".into()); + } + args.source = Some(Source::Source(s.value())); + } else { + return Err("template source must be string literal".into()); + } + } else if ident == "print" { + if let syn::Lit::Str(ref s) = pair.lit { + args.print = s.value().parse()?; + } else { + return Err("print value must be string literal".into()); + } + } else if ident == "escape" { + if let syn::Lit::Str(ref s) = pair.lit { + args.escaping = Some(s.value()); + } else { + return Err("escape value must be string literal".into()); + } + } else if ident == "ext" { + if let syn::Lit::Str(ref s) = pair.lit { + args.ext = Some(s.value()); + } else { + return Err("ext value must be string literal".into()); + } + } else if ident == "syntax" { + if let syn::Lit::Str(ref s) = pair.lit { + args.syntax = Some(s.value()) + } else { + return Err("syntax value must be string literal".into()); + } + } else { + return Err(format!("unsupported attribute key {:?} found", ident).into()); + } + } + + Ok(args) + } +} + fn find_used_templates( input: &TemplateInput<'_>, map: &mut HashMap, diff --git a/askama_shared/src/input.rs b/askama_shared/src/input.rs index c70b250..6c49156 100644 --- a/askama_shared/src/input.rs +++ b/askama_shared/src/input.rs @@ -1,10 +1,10 @@ +use crate::generator::TemplateArgs; use crate::{CompileError, Config, Syntax}; use std::path::{Path, PathBuf}; use std::str::FromStr; use mime::Mime; -use quote::ToTokens; pub(crate) struct TemplateInput<'a> { pub(crate) ast: &'a syn::DeriveInput, @@ -27,103 +27,15 @@ impl TemplateInput<'_> { pub(crate) fn new<'n>( ast: &'n syn::DeriveInput, config: &'n Config<'_>, + args: TemplateArgs, ) -> Result, CompileError> { - // Check that an attribute called `template()` exists once and that it is - // the proper type (list). - let mut template_args = None; - for attr in &ast.attrs { - let ident = match attr.path.get_ident() { - Some(ident) => ident, - None => continue, - }; - - if ident == "template" { - if template_args.is_some() { - return Err("duplicated 'template' attribute".into()); - } - - match attr.parse_meta() { - Ok(syn::Meta::List(syn::MetaList { nested, .. })) => { - template_args = Some(nested); - } - Ok(_) => return Err("'template' attribute must be a list".into()), - Err(e) => return Err(format!("unable to parse attribute: {}", e).into()), - } - } - } - let template_args = - template_args.ok_or_else(|| CompileError::from("no attribute 'template' found"))?; - - // Loop over the meta attributes and find everything that we - // understand. Return a CompileError if something is not right. - // `source` contains an enum that can represent `path` or `source`. - let mut source = None; - let mut print = Print::None; - let mut escaping = None; - let mut ext = None; - let mut syntax = None; - for item in template_args { - let pair = match item { - syn::NestedMeta::Meta(syn::Meta::NameValue(ref pair)) => pair, - _ => { - return Err(format!( - "unsupported attribute argument {:?}", - item.to_token_stream() - ) - .into()) - } - }; - let ident = match pair.path.get_ident() { - Some(ident) => ident, - None => unreachable!("not possible in syn::Meta::NameValue(…)"), - }; - - if ident == "path" { - if let syn::Lit::Str(ref s) = pair.lit { - if source.is_some() { - return Err("must specify 'source' or 'path', not both".into()); - } - source = Some(Source::Path(s.value())); - } else { - return Err("template path must be string literal".into()); - } - } else if ident == "source" { - if let syn::Lit::Str(ref s) = pair.lit { - if source.is_some() { - return Err("must specify 'source' or 'path', not both".into()); - } - source = Some(Source::Source(s.value())); - } else { - return Err("template source must be string literal".into()); - } - } else if ident == "print" { - if let syn::Lit::Str(ref s) = pair.lit { - print = s.value().parse()?; - } else { - return Err("print value must be string literal".into()); - } - } else if ident == "escape" { - if let syn::Lit::Str(ref s) = pair.lit { - escaping = Some(s.value()); - } else { - return Err("escape value must be string literal".into()); - } - } else if ident == "ext" { - if let syn::Lit::Str(ref s) = pair.lit { - ext = Some(s.value()); - } else { - return Err("ext value must be string literal".into()); - } - } else if ident == "syntax" { - if let syn::Lit::Str(ref s) = pair.lit { - syntax = Some(s.value()) - } else { - return Err("syntax value must be string literal".into()); - } - } else { - return Err(format!("unsupported attribute key {:?} found", ident).into()); - } - } + let TemplateArgs { + source, + print, + escaping, + ext, + syntax, + } = args; // Validate the `source` and `ext` value together, since they are // related. In case `source` was used instead of `path`, the value @@ -261,6 +173,12 @@ impl FromStr for Print { } } +impl Default for Print { + fn default() -> Self { + Self::None + } +} + #[doc(hidden)] pub fn extension_to_mime_type(ext: &str) -> Mime { let basic_type = mime_guess::from_ext(ext).first_or_octet_stream(); -- cgit