#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(elided_lifetimes_in_paths)]
#![deny(unreachable_pub)]
#![deny(missing_docs)]
#![no_std]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
mod ascii_str;
mod error;
pub mod filters;
#[doc(hidden)]
pub mod helpers;
mod html;
mod values;
#[cfg(feature = "alloc")]
use alloc::string::String;
use core::fmt;
use core::ops::Deref;
#[cfg(feature = "std")]
use std::io;
#[cfg(feature = "derive")]
pub use askama_macros::Template;
#[cfg(feature = "derive")]
pub use askama_macros::filter_fn;
pub use crate::error::{Error, Result};
pub use crate::helpers::PrimitiveType;
pub use crate::values::{NO_VALUES, Value, Values, get_value};
pub trait Template: fmt::Display + FastWritable {
#[inline]
#[cfg(feature = "alloc")]
fn render(&self) -> Result<String> {
self.render_with_values(NO_VALUES)
}
#[inline]
#[cfg(feature = "alloc")]
fn render_with_values(&self, values: &dyn Values) -> Result<String> {
let mut buf = String::new();
let _ = buf.try_reserve(Self::SIZE_HINT);
self.render_into_with_values(&mut buf, values)?;
Ok(buf)
}
#[inline]
fn render_into(&self, writer: &mut dyn fmt::Write) -> Result<()> {
self.render_into_with_values(writer, NO_VALUES)
}
fn render_into_with_values(
&self,
writer: &mut dyn fmt::Write,
values: &dyn Values,
) -> Result<()>;
#[inline]
#[cfg(feature = "std")]
fn write_into(&self, writer: &mut dyn io::Write) -> io::Result<()> {
self.write_into_with_values(writer, NO_VALUES)
}
#[cfg(feature = "std")]
fn write_into_with_values(
&self,
writer: &mut dyn io::Write,
values: &dyn Values,
) -> io::Result<()> {
struct Wrapped<W: io::Write> {
writer: W,
err: Option<io::Error>,
}
impl<W: io::Write> fmt::Write for Wrapped<W> {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
if let Err(err) = self.writer.write_all(s.as_bytes()) {
self.err = Some(err);
Err(fmt::Error)
} else {
Ok(())
}
}
}
let mut wrapped = Wrapped { writer, err: None };
if self.render_into_with_values(&mut wrapped, values).is_ok() {
Ok(())
} else {
let err = wrapped.err.take();
Err(err.unwrap_or_else(|| io::Error::other(fmt::Error)))
}
}
const SIZE_HINT: usize;
}
impl<T: Template + ?Sized> Template for &T {
#[inline]
#[cfg(feature = "alloc")]
fn render(&self) -> Result<String> {
<T as Template>::render(self)
}
#[inline]
#[cfg(feature = "alloc")]
fn render_with_values(&self, values: &dyn Values) -> Result<String> {
<T as Template>::render_with_values(self, values)
}
#[inline]
fn render_into(&self, writer: &mut dyn fmt::Write) -> Result<()> {
<T as Template>::render_into(self, writer)
}
#[inline]
fn render_into_with_values(
&self,
writer: &mut dyn fmt::Write,
values: &dyn Values,
) -> Result<()> {
<T as Template>::render_into_with_values(self, writer, values)
}
#[inline]
#[cfg(feature = "std")]
fn write_into(&self, writer: &mut dyn io::Write) -> io::Result<()> {
<T as Template>::write_into(self, writer)
}
#[inline]
#[cfg(feature = "std")]
fn write_into_with_values(
&self,
writer: &mut dyn io::Write,
values: &dyn Values,
) -> io::Result<()> {
<T as Template>::write_into_with_values(self, writer, values)
}
const SIZE_HINT: usize = T::SIZE_HINT;
}
pub trait DynTemplate {
#[cfg(feature = "alloc")]
fn dyn_render(&self) -> Result<String>;
#[cfg(feature = "alloc")]
fn dyn_render_with_values(&self, values: &dyn Values) -> Result<String>;
fn dyn_render_into(&self, writer: &mut dyn fmt::Write) -> Result<()>;
fn dyn_render_into_with_values(
&self,
writer: &mut dyn fmt::Write,
values: &dyn Values,
) -> Result<()>;
#[cfg(feature = "std")]
fn dyn_write_into(&self, writer: &mut dyn io::Write) -> io::Result<()>;
#[cfg(feature = "std")]
fn dyn_write_into_with_values(
&self,
writer: &mut dyn io::Write,
values: &dyn Values,
) -> io::Result<()>;
fn size_hint(&self) -> usize;
}
impl<T: Template> DynTemplate for T {
#[inline]
#[cfg(feature = "alloc")]
fn dyn_render(&self) -> Result<String> {
<Self as Template>::render(self)
}
#[inline]
#[cfg(feature = "alloc")]
fn dyn_render_with_values(&self, values: &dyn Values) -> Result<String> {
<Self as Template>::render_with_values(self, values)
}
#[inline]
fn dyn_render_into(&self, writer: &mut dyn fmt::Write) -> Result<()> {
<Self as Template>::render_into(self, writer)
}
#[inline]
fn dyn_render_into_with_values(
&self,
writer: &mut dyn fmt::Write,
values: &dyn Values,
) -> Result<()> {
<Self as Template>::render_into_with_values(self, writer, values)
}
#[inline]
#[cfg(feature = "std")]
fn dyn_write_into(&self, writer: &mut dyn io::Write) -> io::Result<()> {
<Self as Template>::write_into(self, writer)
}
#[inline]
#[cfg(feature = "std")]
fn dyn_write_into_with_values(
&self,
writer: &mut dyn io::Write,
values: &dyn Values,
) -> io::Result<()> {
<Self as Template>::write_into_with_values(self, writer, values)
}
#[inline]
fn size_hint(&self) -> usize {
<Self as Template>::SIZE_HINT
}
}
impl fmt::Display for dyn DynTemplate {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.dyn_render_into(f).map_err(|_| fmt::Error {})
}
}
macro_rules! impl_for_ref {
(impl $Trait:ident for $T:ident $body:tt) => {
const _: () = {
crate::impl_for_ref! {
impl<$T> $Trait for [
&T
&mut T
core::cell::Ref<'_, T>
core::cell::RefMut<'_, T>
] $body
}
};
#[cfg(feature = "alloc")]
const _: () = {
crate::impl_for_ref! {
impl<$T> $Trait for [
alloc::boxed::Box<T>
alloc::rc::Rc<T>
alloc::sync::Arc<T>
] $body
}
};
#[cfg(feature = "std")]
const _: () = {
crate::impl_for_ref! {
impl<$T> $Trait for [
std::sync::MutexGuard<'_, T>
std::sync::RwLockReadGuard<'_, T>
std::sync::RwLockWriteGuard<'_, T>
] $body
}
};
};
(impl<$T:ident> $Trait:ident for [$($ty:ty)*] $body:tt) => {
$(impl<$T: $Trait + ?Sized> $Trait for $ty $body)*
}
}
pub trait FastWritable {
fn write_into(&self, dest: &mut dyn fmt::Write, values: &dyn Values) -> crate::Result<()>;
}
const _: () = {
crate::impl_for_ref! {
impl FastWritable for T {
#[inline]
fn write_into(
&self,
dest: &mut dyn fmt::Write,
values: &dyn Values,
) -> crate::Result<()> {
<T>::write_into(self, dest, values)
}
}
}
impl<T> FastWritable for core::pin::Pin<T>
where
T: Deref,
<T as Deref>::Target: FastWritable,
{
#[inline]
fn write_into(&self, dest: &mut dyn fmt::Write, values: &dyn Values) -> crate::Result<()> {
self.as_ref().get_ref().write_into(dest, values)
}
}
#[cfg(feature = "alloc")]
impl<T: FastWritable + alloc::borrow::ToOwned + ?Sized> FastWritable for alloc::borrow::Cow<'_, T> {
#[inline]
fn write_into(&self, dest: &mut dyn fmt::Write, values: &dyn Values) -> crate::Result<()> {
T::write_into(self.as_ref(), dest, values)
}
}
macro_rules! impl_for_int {
($($ty:ty)*) => { $(
impl FastWritable for $ty {
#[inline]
fn write_into(
&self,
dest: &mut dyn fmt::Write,
values: &dyn Values,
) -> crate::Result<()> {
itoa::Buffer::new().format(*self).write_into(dest, values)
}
}
)* };
}
impl_for_int!(
u8 u16 u32 u64 u128 usize
i8 i16 i32 i64 i128 isize
);
macro_rules! impl_for_nz_int {
($($id:ident)*) => { $(
impl FastWritable for core::num::$id {
#[inline]
fn write_into(
&self,
dest: &mut dyn fmt::Write,
values: &dyn Values,
) -> crate::Result<()> {
self.get().write_into(dest, values)
}
}
)* };
}
impl_for_nz_int!(
NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize
NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize
);
impl FastWritable for str {
#[inline]
fn write_into(&self, dest: &mut dyn fmt::Write, _: &dyn Values) -> crate::Result<()> {
Ok(dest.write_str(self)?)
}
}
#[cfg(feature = "alloc")]
impl FastWritable for alloc::string::String {
#[inline]
fn write_into(&self, dest: &mut dyn fmt::Write, values: &dyn Values) -> crate::Result<()> {
self.as_str().write_into(dest, values)
}
}
impl FastWritable for bool {
#[inline]
fn write_into(&self, dest: &mut dyn fmt::Write, _: &dyn Values) -> crate::Result<()> {
Ok(dest.write_str(match self {
true => "true",
false => "false",
})?)
}
}
impl FastWritable for char {
#[inline]
fn write_into(&self, dest: &mut dyn fmt::Write, _: &dyn Values) -> crate::Result<()> {
Ok(dest.write_char(*self)?)
}
}
impl FastWritable for fmt::Arguments<'_> {
#[inline]
fn write_into(&self, dest: &mut dyn fmt::Write, _: &dyn Values) -> crate::Result<()> {
Ok(match self.as_str() {
Some(s) => dest.write_str(s),
None => dest.write_fmt(*self),
}?)
}
}
impl<S: crate::Template + ?Sized> filters::WriteWritable for &filters::Writable<'_, S> {
#[inline]
fn askama_write(
&self,
dest: &mut dyn fmt::Write,
values: &dyn Values,
) -> crate::Result<()> {
self.0.render_into_with_values(dest, values)
}
}
impl<S: FastWritable + ?Sized> filters::WriteWritable for &&filters::Writable<'_, S> {
#[inline]
fn askama_write(
&self,
dest: &mut dyn fmt::Write,
values: &dyn Values,
) -> crate::Result<()> {
self.0.write_into(dest, values)
}
}
impl<S: fmt::Display + ?Sized> filters::WriteWritable for &&&filters::Writable<'_, S> {
#[inline]
fn askama_write(&self, dest: &mut dyn fmt::Write, _: &dyn Values) -> crate::Result<()> {
Ok(write!(dest, "{}", self.0)?)
}
}
};
pub(crate) use impl_for_ref;
#[cfg(all(test, feature = "alloc"))]
mod tests {
use std::fmt;
use super::*;
use crate::{DynTemplate, Template};
#[test]
fn dyn_template() {
use alloc::string::ToString;
struct Test;
impl Template for Test {
fn render_into_with_values(
&self,
writer: &mut dyn fmt::Write,
_values: &dyn Values,
) -> Result<()> {
Ok(writer.write_str("test")?)
}
const SIZE_HINT: usize = 4;
}
impl fmt::Display for Test {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.render_into(f).map_err(|_| fmt::Error {})
}
}
impl FastWritable for Test {
#[inline]
fn write_into(&self, f: &mut dyn fmt::Write, values: &dyn Values) -> crate::Result<()> {
self.render_into_with_values(f, values)
}
}
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!(alloc::format!("{test}"), "test");
let mut vec = alloc::vec![];
test.dyn_write_into(&mut vec).unwrap();
assert_eq!(vec, alloc::vec![b't', b'e', b's', b't']);
}
}