diff options
Diffstat (limited to 'askama_shared/src/generator.rs')
-rw-r--r-- | askama_shared/src/generator.rs | 111 |
1 files changed, 110 insertions, 1 deletions
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<String, CompileError> { + 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<String, CompileError> { Ok(code) } +#[derive(Default)] +pub(crate) struct TemplateArgs { + pub(crate) source: Option<Source>, + pub(crate) print: Print, + pub(crate) escaping: Option<String>, + pub(crate) ext: Option<String>, + pub(crate) syntax: Option<String>, +} + +impl TemplateArgs { + fn new(ast: &'_ syn::DeriveInput) -> Result<Self, 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"))?; + + 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<PathBuf, String>, |