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