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 | |
66 | mod error; |
67 | pub mod filters; |
68 | pub mod helpers; |
69 | |
70 | use std::fmt; |
71 | |
72 | pub use askama_derive::Template; |
73 | pub use askama_escape::{Html, MarkupDisplay, Text}; |
74 | |
75 | #[doc (hidden)] |
76 | pub use crate as shared; |
77 | pub 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`]. |
82 | pub 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. |
121 | pub 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 | |
141 | impl<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 | |
168 | impl 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 | )] |
181 | pub fn rerun_if_templates_changed() {} |
182 | |
183 | #[cfg (test)] |
184 | mod 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 | } |
229 | |