askama/
lib.rs

1//! Askama implements a type-safe compiler for Jinja-like templates.
2//! It lets you write templates in a Jinja-like syntax,
3//! which are linked to a `struct` defining the template context.
4//! This is done using a custom derive implementation (implemented
5//! in [`askama_derive`](https://crates.io/crates/askama_derive)).
6//!
7//! For feature highlights and a quick start, please review the
8//! [README](https://github.com/djc/askama/blob/main/README.md).
9//!
10//! The primary documentation for this crate now lives in
11//! [the book](https://djc.github.io/askama/).
12//!
13//! # Creating Askama templates
14//!
15//! An Askama template is a `struct` definition which provides the template
16//! context combined with a UTF-8 encoded text file (or inline source, see
17//! below). Askama can be used to generate any kind of text-based format.
18//! The template file's extension may be used to provide content type hints.
19//!
20//! A template consists of **text contents**, which are passed through as-is,
21//! **expressions**, which get replaced with content while being rendered, and
22//! **tags**, which control the template's logic.
23//! The template syntax is very similar to [Jinja](http://jinja.pocoo.org/),
24//! as well as Jinja-derivatives like [Twig](http://twig.sensiolabs.org/) or
25//! [Tera](https://github.com/Keats/tera).
26//!
27//! ## The `template()` attribute
28//!
29//! Askama works by generating one or more trait implementations for any
30//! `struct` type decorated with the `#[derive(Template)]` attribute. The
31//! code generation process takes some options that can be specified through
32//! the `template()` attribute. The following sub-attributes are currently
33//! recognized:
34//!
35//! * `path` (as `path = "foo.html"`): sets the path to the template file. The
36//!   path is interpreted as relative to the configured template directories
37//!   (by default, this is a `templates` directory next to your `Cargo.toml`).
38//!   The file name extension is used to infer an escape mode (see below). In
39//!   web framework integrations, the path's extension may also be used to
40//!   infer the content type of the resulting response.
41//!   Cannot be used together with `source`.
42//! * `source` (as `source = "{{ foo }}"`): directly sets the template source.
43//!   This can be useful for test cases or short templates. The generated path
44//!   is undefined, which generally makes it impossible to refer to this
45//!   template from other templates. If `source` is specified, `ext` must also
46//!   be specified (see below). Cannot be used together with `path`.
47//! * `ext` (as `ext = "txt"`): lets you specify the content type as a file
48//!   extension. This is used to infer an escape mode (see below), and some
49//!   web framework integrations use it to determine the content type.
50//!   Cannot be used together with `path`.
51//! * `print` (as `print = "code"`): enable debugging by printing nothing
52//!   (`none`), the parsed syntax tree (`ast`), the generated code (`code`)
53//!   or `all` for both. The requested data will be printed to stdout at
54//!   compile time.
55//! * `escape` (as `escape = "none"`): override the template's extension used for
56//!   the purpose of determining the escaper for this template. See the section
57//!   on configuring custom escapers for more information.
58//! * `syntax` (as `syntax = "foo"`): set the syntax name for a parser defined
59//!   in the configuration file. The default syntax , "default",  is the one
60//!   provided by Askama.
61
62#![forbid(unsafe_code)]
63#![deny(elided_lifetimes_in_paths)]
64#![deny(unreachable_pub)]
65
66mod error;
67pub mod filters;
68pub mod helpers;
69
70use std::fmt;
71
72pub use askama_derive::Template;
73pub use askama_escape::{Html, MarkupDisplay, Text};
74
75#[doc(hidden)]
76pub use crate as shared;
77pub use crate::error::{Error, Result};
78
79/// Main `Template` trait; implementations are generally derived
80///
81/// If you need an object-safe template, use [`DynTemplate`].
82pub trait Template: fmt::Display {
83    /// Helper method which allocates a new `String` and renders into it
84    fn render(&self) -> Result<String> {
85        let mut buf = String::new();
86        let _ = buf.try_reserve(Self::SIZE_HINT);
87        self.render_into(&mut buf)?;
88        Ok(buf)
89    }
90
91    /// Renders the template to the given `writer` fmt buffer
92    fn render_into(&self, writer: &mut (impl std::fmt::Write + ?Sized)) -> Result<()>;
93
94    /// Renders the template to the given `writer` io buffer
95    #[inline]
96    fn write_into(&self, writer: &mut (impl std::io::Write + ?Sized)) -> std::io::Result<()> {
97        writer.write_fmt(format_args!("{self}"))
98    }
99
100    /// The template's extension, if provided
101    const EXTENSION: Option<&'static str>;
102
103    /// Provides a rough estimate of the expanded length of the rendered template. Larger
104    /// values result in higher memory usage but fewer reallocations. Smaller values result in the
105    /// opposite. This value only affects [`render`]. It does not take effect when calling
106    /// [`render_into`], [`write_into`], the [`fmt::Display`] implementation, or the blanket
107    /// [`ToString::to_string`] implementation.
108    ///
109    /// [`render`]: Template::render
110    /// [`render_into`]: Template::render_into
111    /// [`write_into`]: Template::write_into
112    const SIZE_HINT: usize;
113
114    /// The MIME type (Content-Type) of the data that gets rendered by this Template
115    const MIME_TYPE: &'static str;
116}
117
118/// Object-safe wrapper trait around [`Template`] implementers
119///
120/// This trades reduced performance (mostly due to writing into `dyn Write`) for object safety.
121pub trait DynTemplate {
122    /// Helper method which allocates a new `String` and renders into it
123    fn dyn_render(&self) -> Result<String>;
124
125    /// Renders the template to the given `writer` fmt buffer
126    fn dyn_render_into(&self, writer: &mut dyn std::fmt::Write) -> Result<()>;
127
128    /// Renders the template to the given `writer` io buffer
129    fn dyn_write_into(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()>;
130
131    /// Helper function to inspect the template's extension
132    fn extension(&self) -> Option<&'static str>;
133
134    /// Provides a conservative estimate of the expanded length of the rendered template
135    fn size_hint(&self) -> usize;
136
137    /// The MIME type (Content-Type) of the data that gets rendered by this Template
138    fn mime_type(&self) -> &'static str;
139}
140
141impl<T: Template> DynTemplate for T {
142    fn dyn_render(&self) -> Result<String> {
143        <Self as Template>::render(self)
144    }
145
146    fn dyn_render_into(&self, writer: &mut dyn std::fmt::Write) -> Result<()> {
147        <Self as Template>::render_into(self, writer)
148    }
149
150    #[inline]
151    fn dyn_write_into(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
152        writer.write_fmt(format_args!("{self}"))
153    }
154
155    fn extension(&self) -> Option<&'static str> {
156        Self::EXTENSION
157    }
158
159    fn size_hint(&self) -> usize {
160        Self::SIZE_HINT
161    }
162
163    fn mime_type(&self) -> &'static str {
164        Self::MIME_TYPE
165    }
166}
167
168impl fmt::Display for dyn DynTemplate {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        self.dyn_render_into(f).map_err(|_| ::std::fmt::Error {})
171    }
172}
173
174/// Old build script helper to rebuild crates if contained templates have changed
175///
176/// This function is now deprecated and does nothing.
177#[deprecated(
178    since = "0.8.1",
179    note = "file-level dependency tracking is handled automatically without build script"
180)]
181pub fn rerun_if_templates_changed() {}
182
183#[cfg(test)]
184mod tests {
185    use std::fmt;
186
187    use super::*;
188    use crate::{DynTemplate, Template};
189
190    #[test]
191    fn dyn_template() {
192        struct Test;
193        impl Template for Test {
194            fn render_into(&self, writer: &mut (impl std::fmt::Write + ?Sized)) -> Result<()> {
195                Ok(writer.write_str("test")?)
196            }
197
198            const EXTENSION: Option<&'static str> = Some("txt");
199
200            const SIZE_HINT: usize = 4;
201
202            const MIME_TYPE: &'static str = "text/plain; charset=utf-8";
203        }
204
205        impl fmt::Display for Test {
206            #[inline]
207            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208                self.render_into(f).map_err(|_| fmt::Error {})
209            }
210        }
211
212        fn render(t: &dyn DynTemplate) -> String {
213            t.dyn_render().unwrap()
214        }
215
216        let test = &Test as &dyn DynTemplate;
217
218        assert_eq!(render(test), "test");
219
220        assert_eq!(test.to_string(), "test");
221
222        assert_eq!(format!("{test}"), "test");
223
224        let mut vec = Vec::new();
225        test.dyn_write_into(&mut vec).unwrap();
226        assert_eq!(vec, vec![b't', b'e', b's', b't']);
227    }
228}