From 723567ba0c8d28a292b87d6d65cb97e4b4406e4b Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Sun, 5 Feb 2017 15:18:47 +0100 Subject: Implement basic support for template inheritance --- askama/src/generator.rs | 100 ++++++++++++++++++++++++++++++++++++++++++- testing/templates/base.html | 2 + testing/templates/child.html | 2 + testing/tests/inheritance.rs | 19 ++++++++ 4 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 testing/templates/base.html create mode 100644 testing/templates/child.html create mode 100644 testing/tests/inheritance.rs diff --git a/askama/src/generator.rs b/askama/src/generator.rs index 61d4fab..4c2adef 100644 --- a/askama/src/generator.rs +++ b/askama/src/generator.rs @@ -193,6 +193,23 @@ impl Generator { } } + fn block_methods(&mut self, blocks: &Vec) { + for b in blocks { + if let &Node::BlockDef(name, ref nodes) = b { + self.writeln("#[allow(unused_variables)]"); + self.writeln(&format!( + "fn render_block_{}_into(&self, writer: &mut std::fmt::Write) {{", + name)); + self.indent(); + self.handle(&nodes); + self.dedent(); + self.writeln("}"); + } else { + panic!("only block definitions allowed"); + } + } + } + fn path_based_name(&mut self, ast: &syn::DeriveInput, path: &str) { let encoded = path_as_identifier(path); let original = ast.ident.as_ref(); @@ -219,15 +236,94 @@ impl Generator { self.writeln("}"); } + fn trait_impl(&mut self, ast: &syn::DeriveInput, base: &str, blocks: &Vec) { + let anno = annotations(&ast.generics); + self.writeln(&format!("impl{} TraitFrom{} for {}{} {{", + anno, path_as_identifier(base), + ast.ident.as_ref(), anno)); + self.indent(); + + self.block_methods(blocks); + + self.dedent(); + self.writeln("}"); + } + + fn trait_based_impl(&mut self, ast: &syn::DeriveInput) { + let anno = annotations(&ast.generics); + self.writeln(&format!("impl{} askama::Template for {}{} {{", + anno, ast.ident.as_ref(), anno)); + self.indent(); + + self.writeln("fn render_into(&self, writer: &mut std::fmt::Write) {"); + self.indent(); + self.writeln("self.render_trait_into(writer);"); + self.dedent(); + self.writeln("}"); + + self.dedent(); + self.writeln("}"); + } + + fn template_trait(&mut self, ast: &syn::DeriveInput, path: &str, + blocks: &Vec, nodes: &Vec) { + let anno = annotations(&ast.generics); + self.writeln(&format!("trait{} TraitFrom{}{} {{", anno, + path_as_identifier(path), anno)); + self.indent(); + + self.block_methods(blocks); + + self.writeln("fn render_trait_into(&self, writer: &mut std::fmt::Write) {"); + self.indent(); + self.handle(nodes); + self.dedent(); + self.writeln("}"); + + self.dedent(); + self.writeln("}"); + } + fn result(self) -> String { self.buf } } -pub fn generate(ast: &syn::DeriveInput, path: &str, nodes: Vec) -> String { +pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec) -> String { + let mut base: Option = None; + let mut blocks = Vec::new(); + let mut content = Vec::new(); + for n in nodes.drain(..) { + match n { + Node::Extends(path) => { + match base { + Some(_) => panic!("multiple extend blocks found"), + None => { base = Some(path); }, + } + }, + Node::BlockDef(name, _) => { + blocks.push(n); + content.push(Node::Block(name)); + }, + _ => { content.push(n); }, + } + } + let mut gen = Generator::new(); gen.path_based_name(ast, path); - gen.template_impl(ast, &nodes); + if blocks.len() > 0 { + if let Some(extends) = base { + if let Expr::StrLit(base_path) = extends { + gen.trait_impl(ast, &base_path, &blocks); + } + } else { + gen.template_trait(ast, path, &blocks, &content); + gen.trait_impl(ast, path, &Vec::new()); + } + gen.trait_based_impl(ast); + } else { + gen.template_impl(ast, &content); + } gen.result() } diff --git a/testing/templates/base.html b/testing/templates/base.html new file mode 100644 index 0000000..f40d867 --- /dev/null +++ b/testing/templates/base.html @@ -0,0 +1,2 @@ +{% block content %}{% endblock %} +Copyright 2017 diff --git a/testing/templates/child.html b/testing/templates/child.html new file mode 100644 index 0000000..23aed37 --- /dev/null +++ b/testing/templates/child.html @@ -0,0 +1,2 @@ +{% extends "base.html" %} +{% block content %}Content goes here{% endblock %} diff --git a/testing/tests/inheritance.rs b/testing/tests/inheritance.rs new file mode 100644 index 0000000..ebcca70 --- /dev/null +++ b/testing/tests/inheritance.rs @@ -0,0 +1,19 @@ +extern crate askama; +#[macro_use] +extern crate askama_derive; + +use askama::Template; + +#[derive(Template)] +#[template(path = "base.html")] +struct BaseTemplate { } + +#[derive(Template)] +#[template(path = "child.html")] +struct ChildTemplate { } + +#[test] +fn test_simple_extends() { + let t = ChildTemplate { }; + assert_eq!(t.render(), "Content goes here\nCopyright 2017\n"); +} -- cgit