A template engine for Rust based on Jinja2/Django

Overview

Tera

Actions Status Crates.io Docs Gitter

Tera is a template engine inspired by Jinja2 and the Django template language.

<title>{% block title %}{% endblock title %}</title>
<ul>
{% for user in users %}
  <li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>

It does not aim to be 100% compatible with them but has many of the Jinja2/Django filters and testers.

Documentation

API documentation is available on docs.rs.

Tera documentation is available on its site.

Community

There's a gitter community chat (not managed/used by @Keats): https://gitter.im/Tera-templates/community

SemVer

This project follows SemVer only for the public API, public API here meaning functions appearing in the docs. Some features, like accessing the AST, are also available but breaking changes in them can happen in any versions.

Comments
  • Streamable templates

    Streamable templates

    Currently, Tera::render returns a string, which comes from Renderer::render, which builds the string by rendering each component and then using String::push_str.

    Unfortunately for extremely large templates, this uses a lot of memory and might take a lot of time, and the biggest problem is that rendering is a blocking operation. It might be smarter to have a different render method that returns a struct that implements std::io::Read so it can be streamed and not store the entire response in the heap before sending it over to the server.

    (I'm obviously talking about using tera in conjunction with a web application framework as opposed to tera alone, but let's be honest, that's 99% of tera's use cases)

    opened by ghost 41
  • Various crashes from fuzzing

    Various crashes from fuzzing

    This is an issue I found while fuzzing this morning. Here is a simplified test case:

    let context = Context::new(); let _ = Tera::one_off("{{1/0}}", &context, true);

    bug 
    opened by stusmall 36
  • Can we keep type safety ?

    Can we keep type safety ?

    Ideas:

    • https://www.reddit.com/r/rust/comments/4ewsg6/introducing_tera_a_template_engine_in_rust/d250nro

    Inspiration:

    • https://playframework.com/documentation/2.4.x/ScalaTemplates declares types in the template but uses reflection afaik
    opened by Keats 35
  • Filters to implement

    Filters to implement

    Intro

    Follow the examples in filters/string.rs with tests. Need to add all common and expected filters from Jinja2 and Django. Feedback on the API wanted as well! Each filter should have tests for all the possible options. Tera filters can only be applied to identifers and their arguments have to be named.

    Strings

    • [x] lower -> lowercase a string
    • [x] urlencode -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#urlencode
    • [x] wordcount -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#wordcount
    • [ ] truncate -> need to add preserve words option and preserve tags options -> mix of all truncatechars* likehttps://docs.djangoproject.com/en/1.10/ref/templates/builtins/#truncatechars and http://jinja.pocoo.org/docs/dev/templates/#truncate
    • [ ] truncatewords: same as truncate but on word level rather than chars
    • [x] striptags -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#striptags
    • [x] slugify -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#slugify (https://github.com/django/django/blob/master/django/utils/text.py#L417 to not forget about unicode chars)
    • [x] escape -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#escape
    • [ ] escape_js
    • [ ] escape_sql
    • [x] capitalize -> first char uppercase, rest lowercase
    • [x] replace -> http://jinja.pocoo.org/docs/dev/templates/#replace
    • [x] title -> http://jinja.pocoo.org/docs/dev/templates/#title
    • [x] addslashes -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#addslashes

    Numbers

    • [x] filesizeformat -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#filesizeformat
    • [x] round -> http://jinja.pocoo.org/docs/dev/templates/#round
    • [x] pluralize -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#pluralize

    Arrays

    • [ ] sort -> need to think this one a bit on how to sort
    • [x] first -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#first
    • [x] last -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#last
    • [x] join -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#join

    Common

    Those should be in filters/mod.rs or in a filters/common.rs file.

    • [x] length -> https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#length applies to string and arrays
    • [x] reverse -> http://jinja.pocoo.org/docs/dev/templates/#reverse applies to string and arrays

    Dates

    Date filters will wait until Tera has its own serialization instead of using JSON


    Note: I left out some filters that I don't really the use for like ljust in Django, but happy to include more than the currently listed if they are needed. I also probably forgot some

    opened by Keats 27
  • Avoid allocations

    Avoid allocations

    Right now, the Context::insert method converts a value to JSON value and it seems to be cloned when passing the context to the render method.

    It would be ideal to store in Context the data before converting (ie as dyn Serialize) but this is not possible. There might be a way to do that but I don't know any right now.

    Another solution could be for Tera::render to take a Context by value instead of a T: Serialize so we can grab the data. It would disallow rendering with something other than a Context but we could add functions to still provide that if needed. If I'm not reading this wrong, it would be a win in terms of allocations too big to ignore.

    opened by Keats 26
  • Processing time is increased even with unused values

    Processing time is increased even with unused values

    I inserted &Vector (1000over elements) to a Context. I don't use the ref documents, but processing time is increased. Why? I expect that context has reference of documents (don't have copy), and if i don't use the parameter it does not significantly affect processing time.

    Template (JSON)

    site.docs is unused.

     "site": {
        "total_posts": {{ site.total_docs }}
     },
    

    With context.insert("docs", &docs);

    Context

    // docs: Vec<Document>
    // docs.len() is 1000over
    
    let mut context = Context::new();
    context.insert("docs", &docs);
    context.insert("total_docs", &docs.len());
    
    cargo run --release (9.028s)
    

    Without context.insert("docs", &docs);

    Context

    // docs: Vec<Document>
    // docs.len() is 1000over
    
    let mut context = Context::new();
    // context.insert("docs", &docs);
    context.insert("total_docs", &docs.len());
    
    cargo run --release (0.848s)
    
    opened by ghost 22
  • (Rant) 1.14.0 changes signature of `Context`, breaks grcov

    (Rant) 1.14.0 changes signature of `Context`, breaks grcov

    Hello,

    Tera 1.14.0 adds a generic parameter to Context, which breaks anyone who mentioned this type in their code. One such program is grcov, which is used by many to calculate test coverage. I see the warning in the README, but isn't Context public?

    I already submitted a PR to fix grcov, and I don't expect you to do anything about this. Just please, be more considering the next time you change a public type ;)

    Thanks!

    opened by Minoru 19
  • Add support for

    Add support for "in" builtin test

    It would be great if Tera could support testing whether a given variable is equals to one of a series of values, ie Jinja2's in test: http://jinja.pocoo.org/docs/2.10/templates/#in

    enhancement For next major version done 
    opened by keirlawson 19
  • What is the easiest way to concatenate lists in template?

    What is the easiest way to concatenate lists in template?

    Hi!

    What would be "the official" way to iterate over xs + ys in template?

    Currently I use {% for kw in concat(a=keywords, b=contextual_keywords) %} where concat is defined as

    fn concat(args: HashMap<String, tera::Value>) -> tera::Result<tera::Value> {
        let mut elements = Vec::new();
        for &key in ["a", "b", "c"].iter() {
            let val = match args.get(key) {
                Some(val) => val,
                None => continue,
            };
            let val = val.as_array().unwrap();
            elements.extend(val.iter().cloned());
        }
        Ok(tera::Value::Array(elements))
    }
    

    and that is rather painful to look at :)

    Is there a better way?

    opened by matklad 17
  • Undefined template vars cause rendering error

    Undefined template vars cause rendering error

    Unlike jinja2 and django templates, it seems that tera will fail to render a template if a referenced var is undefined in the given context.

    I fine it quite inconvenient to have to wrap all template vars which I do not always want to pass in {% if var_name %}{{ var_name }}{% endif %}. Could we at least have a flag we can optionally pass to render which makes the renderer ignore undefined vars?

    question 
    opened by vezult 17
  • Write our own serde format

    Write our own serde format

    serde-json was chosen as the simplest format I could think of but it would be interesting to see what writing our own format would do. We only use serde-json to have easy serialization of user data in the context in Value nodes, not really for anything else.

    Advantages:

    • we can add built-in support for dates
    • separation of int and float types, right now it's just Number
    • no parsing from string to size will be smaller than serde-json
    • probably quite a bit faster
    • no need for users to have serde-json, all you need would be in Tera
    • only need to impl serialization I think

    Cons:

    • more code
    • might not be that much faster but it's pretty unlikely
    • would need to impl something equivalent to the JSON pointers
    • a big breaking change

    If anyone is interesting in picking this up, please do! I won't have time to touch that for quite some time.

    help wanted For next major version 
    opened by Keats 16
  • missing a key on iterator

    missing a key on iterator

    Hi,

    I tried to use a generator like this :

    #[derive(Serialize, Deserialize, Debug)]
    pub struct Accounts {
        pub key: u32,
    }
    
    impl Accounts {
        fn new() -> Accounts {
            Accounts {
                key: 0
            }
        }
    }
    
    pub struct Account {
        pub name: String
    }
    
    impl Iterator for Accounts {
        type Item = Account;
    
        fn next(&mut self) -> Option<Self::Item> {
            self.key += 1;
    
            if self.key > 3 {
                return None;
            }
    
            Some(Account {
                name: format!("generate value : {}", self.key)
            })
        }
    }
    let accounts = Accounts::new();
    context.insert("accounts", &accounts);
    

    but Tera response with :

    Err(Error { kind: Msg("Failed to render '__tera_one_off'"), source: Some(Error { kind: Msg("Tried to iterate using key value on variable `accounts`, but it is missing a key"), source: None }) })
    

    Is it possible to use a generator and if so, how?

    opened by mothsART 3
  • Truncate cuts off image urls and html tags

    Truncate cuts off image urls and html tags

    I was trying out zola for a SSG project for a client, their stuff is written in markdown. But the main issue with Zola (most probably Tera) is that it cuts off long urls in images and closing html tags which makes the site look weird. I'm trying to render some portion of {{ page.content }} for the description of a blog item in a list, but truncate is breaking the list by not stopping at full elements in markdown or the rendered html(whichever it works on first). Below is my code.

    {% for page in section.pages %}
        <li>
            <a href="{{ page.permalink | safe }}">{{ page.title }}</a>
            <div> {{ page.content | truncate(length=300) | safe }}</div>
        </li>
    {% endfor %}
    
    opened by that-ambuj 2
  • StripPrefixError when glob pattern has wildcard in the middle of path element

    StripPrefixError when glob pattern has wildcard in the middle of path element

    Using a glob pattern to load templates such as templates/test*.html results in a StripPrefixError(()) from tera-1.17.1/src/tera.rs:153:22

    A quick look at the code suggests that there is a built-in assumption that globs will not have the wildcard in the middle of path element. That is, a glob such as templates/*.html is fine because the * comes immediately after the / -- on line 131 of that file, the directory is split at the wildcard to find the parent_dir, which is then used as a parameter to strip_prefix() on 152. But strip_prefix() requires the prefix being stripped to be in whole path elements.

    I think a reasonable fix would be to add something like around line 132 or 133 -- note that this is just a suggestion and has not been tested / validated in any way:

    if let Some(idx) = parent_dir.rfind('/') {
        parent_dir = parent_dir.split_at(idx + 1).0;
    }
    
    opened by mdharm 0
  • Add builtin `is none` or `is null` test

    Add builtin `is none` or `is null` test

    I'd consider checking if a value is null to be a pretty common task, and I think it'd be a pretty good candidate for a builtin function.

    Currently I have the following in my application:

    /// Check if a value is [`Value::Null`].
    pub fn none(value: Option<&Value>, _args: &[Value]) -> Result<bool> {
        Ok(value.unwrap().is_null())
    }
    

    And yeah it's just three lines of code, but having this available everywhere just feels like it would be a good idea, especially for people who'd be coming from Jinja where this would be built in.

    opened by hwittenborn 4
  • Support Math.pow for number formatting

    Support Math.pow for number formatting

    It would be nice for formatting purpose to support Math.pow in the form of 10**3 for instance, where the result is 1000.

    I am hitting a case where I would need to format numbers such as 1000000000 (how many zeroes ? :)) into 1'000'000'000 (easier now) or even 1_000_000_000.

    Or even 1 or 1.0 given 1000000000 and 9 decimals as input. This likely requires BigInt but having already pow and a float formatting option would solve quite some problems.

    opened by chevdor 3
  • Displaying a vector of structs in a html.tera page

    Displaying a vector of structs in a html.tera page

    Hello i can't figure out how to display a vector of struct.

    template rendering:

    let a: Vec<ResultsStruct> = serde_json::from_value(query_result).unwrap();   
    Template::render("index", json!({ "user": user, "c_api": c_api, "query_result": a }))
    

    template code:

    <div class="container">
        {% for i in query_result %}
            {{ i }}
        {% endfor %} 
    </div>
    

    i would like to print a struct from the vec like this but i can't figure out how. thank you for the help

    opened by EnricoMucelli 3
Owner
Vincent Prouillet
Vincent Prouillet
A flexible template engine for Rust

Rustache Rustache is a Rust implementation of the Mustache spec. Documentation The different Mustache tags are documented at the mustache(5) man page.

rustache 208 May 10, 2022
MiniJinja is a powerful but minimal dependency template engine for Rust

MiniJinja: a powerful template engine for Rust with minimal dependencies MiniJinja is a powerful but minimal dependency template engine for Rust which

Armin Ronacher 686 Jan 5, 2023
finch - a super fast and efficient template rendering engine for node.js

finch A super fast and efficient template rendering engine for node.js, inspired by Handlebars. Usage Finch is very simple to use: Register a template

null 1 Nov 2, 2021
Balsa is a delightfully simple HTML template engine

?? balsa Balsa is a delightfully simple HTML template engine. It is designed to be used in user interfaces such as a CMS where a user needs to be able

Tyler Lafayette 1 Jan 19, 2022
Compiler for Jade-like template language to cito.js-based virtual dom

Marafet A very experimental DSL for creating (mostly) single page applications in HTML. It's mostly a Jade-like (or Haml-like) templating language wit

Paul Colomiets 11 Jun 25, 2020
Template for Cargo based SysY compiler projects.

基于 Cargo 的 SysY 编译器项目模板 该仓库中存放了一个基于 Cargo 的 SysY 编译器项目的模板, 你可以在该模板的基础上进行进一步的开发. 该仓库中的 Rust 代码实现仅作为演示, 不代表你的编译器必须以此方式实现. 如你需要使用该模板, 建议你删掉所有 Rust 源文件, 仅

PKU Compiler Course 1 Nov 1, 2021
A minimalist Rust WebAssembly project template

MiniWASM - A minimalist Rust WebAssembly project template This is a minimal Rust-powered WebAssembly application template. It was designed to showcase

Emil Loer 160 Jul 26, 2022
A template for a Rust-powered static-page Try Online interface

rust-tio-template A template for a Rust-powered static-page Try Online interface What is included This is an example setup that enables all of the fol

null 2 Dec 13, 2021
A "Hello, world!" template of a Rust binary crate for the ESP-IDF framework.

Rust on ESP-IDF "Hello, World" template A "Hello, world!" template of a Rust binary crate for the ESP-IDF framework. This is the crate you get when ru

Ivan Markov 140 Jan 4, 2023
A template for creating services in Rust using Axum and Prisma.

A template for creating services in Rust using Axum and Prisma. This uses the super cool Prisma Rust Client.

Aaron Leopold 6 Oct 19, 2022
✨ A perfect template for a binary rust project.

Rust Template A project template for Rust, helping to structure your projects blazingly fast ⚡ . Features ?? Code-ready for binary projects. Add amazi

bwtecode 3 Aug 21, 2022
A nice template for NEAR repos

Template for a NEAR project If you're looking for a no-std version of this template, go here. Contains: a setup script Cargo.toml setup with simulatio

Thor 12 Dec 28, 2021
Yew Tauri Desktop App Template

Yew Tauri Desktop App Template This is Template App for Desktop App. Run Develop $ cargo tauri dev Build App $ cargo tauri build Setup yew setup Using

usa 16 Oct 16, 2022
Bevy GitHub CI Template

Bevy GitHub CI Template This repo show how to setup CI on a github project for Bevy. It creates two workflows: CI Release CI Definition: .github/workf

MiniaczQ 2 Feb 7, 2022
This is a template repo for eframe, a framework for writing apps using egui.

eframe template This is a template repo for eframe, a framework for writing apps using egui. The goal is for this to be the simplest way to get starte

Viet Dinh 0 Feb 5, 2022
A macro-based html builder for rust

Horrorshow A macro-based html templating library, compatible with stable rust (currently requires rust >= 1.37). Features This crate will degrade grac

Steven Allen 267 Dec 11, 2022
Rust templating with Handlebars

handlebars-rust Handlebars templating language implemented in Rust and for Rust. Handlebars-rust is the template engine that renders the official Rust

Ning Sun 922 Dec 27, 2022
:pencil: Compile-time HTML templates for Rust

maud Documentation (source) • API reference • Change log Maud is an HTML template engine for Rust. It's implemented as a macro, html!, which compiles

Chris Wong 1.4k Jan 1, 2023
Rust Compiled Templates with static-file handling

Rust Compiled Templates — ructe This is my attempt at writing a HTML template system for Rust. Some inspiration comes from the scala template system u

Rasmus Kaj 337 Jan 8, 2023