Move FastWritable into askama root

This commit is contained in:
Guillaume Gomez 2025-04-20 12:15:29 +02:00 committed by René Kijewski
parent 7c5deda4c1
commit 6f912abee6
14 changed files with 208 additions and 214 deletions

View File

@ -7,9 +7,9 @@ use core::fmt::{self, Write};
use core::ops::Deref; use core::ops::Deref;
use core::pin::Pin; use core::pin::Pin;
use super::MAX_LEN;
use super::escape::HtmlSafeOutput; use super::escape::HtmlSafeOutput;
use super::{FastWritable, MAX_LEN}; use crate::{FastWritable, Result};
use crate::Result;
/// Return an ephemeral `&str` for `$src: impl fmt::Display` /// Return an ephemeral `&str` for `$src: impl fmt::Display`
/// ///

View File

@ -5,8 +5,7 @@ use core::ops::Deref;
use core::pin::Pin; use core::pin::Pin;
use super::MAX_LEN; use super::MAX_LEN;
use super::escape::FastWritable; use crate::{Error, FastWritable, Result, Values};
use crate::{Error, Result, Values};
/// Limit string length, appends '...' if truncated /// Limit string length, appends '...' if truncated
/// ///

View File

@ -1,10 +1,9 @@
use core::convert::Infallible; use core::convert::Infallible;
use core::fmt::{self, Formatter, Write}; use core::fmt::{self, Formatter, Write};
use core::ops::Deref;
use core::pin::Pin; use core::pin::Pin;
use core::str; use core::str;
use crate::Values; use crate::{FastWritable, Values};
/// Marks a string (or other `Display` type) as safe /// Marks a string (or other `Display` type) as safe
/// ///
@ -518,196 +517,6 @@ pub trait WriteWritable {
) -> crate::Result<()>; ) -> crate::Result<()>;
} }
/// Used internally by askama to speed up writing some types.
///
/// Types implementing this trait can be written without needing to employ an [`fmt::Formatter`].
pub trait FastWritable {
/// Used internally by askama to speed up writing some types.
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn Values,
) -> crate::Result<()>;
}
const _: () = {
crate::impl_for_ref! {
impl FastWritable for T {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn Values,
) -> crate::Result<()> {
<T>::write_into(self, dest, values)
}
}
}
impl<T> FastWritable for Pin<T>
where
T: Deref,
<T as Deref>::Target: FastWritable,
{
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn Values,
) -> crate::Result<()> {
self.as_ref().get_ref().write_into(dest, values)
}
}
#[cfg(feature = "alloc")]
impl<T: FastWritable + alloc::borrow::ToOwned> FastWritable for alloc::borrow::Cow<'_, T> {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn Values,
) -> crate::Result<()> {
T::write_into(self.as_ref(), dest, values)
}
}
// implement FastWritable for a list of types
macro_rules! impl_for_int {
($($ty:ty)*) => { $(
impl FastWritable for $ty {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
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
);
// implement FastWritable for a list of non-zero integral types
macro_rules! impl_for_nz_int {
($($id:ident)*) => { $(
impl FastWritable for core::num::$id {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
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<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
_: &dyn Values,
) -> crate::Result<()> {
Ok(dest.write_str(self)?)
}
}
#[cfg(feature = "alloc")]
impl FastWritable for alloc::string::String {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn Values,
) -> crate::Result<()> {
self.as_str().write_into(dest, values)
}
}
impl FastWritable for bool {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
_: &dyn Values,
) -> crate::Result<()> {
Ok(dest.write_str(match self {
true => "true",
false => "false",
})?)
}
}
impl FastWritable for char {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
_: &dyn Values,
) -> crate::Result<()> {
Ok(dest.write_char(*self)?)
}
}
impl FastWritable for fmt::Arguments<'_> {
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
_: &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> WriteWritable for &Writable<'_, S> {
#[inline]
fn askama_write<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn Values,
) -> crate::Result<()> {
self.0.render_into_with_values(dest, values)
}
}
impl<S: FastWritable + ?Sized> WriteWritable for &&Writable<'_, S> {
#[inline]
fn askama_write<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn Values,
) -> crate::Result<()> {
self.0.write_into(dest, values)
}
}
impl<S: fmt::Display + ?Sized> WriteWritable for &&&Writable<'_, S> {
#[inline]
fn askama_write<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
_: &dyn Values,
) -> crate::Result<()> {
Ok(write!(dest, "{}", self.0)?)
}
}
};
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test_escape() { fn test_escape() {

View File

@ -2,9 +2,8 @@ use core::convert::Infallible;
use core::fmt; use core::fmt;
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use super::FastWritable;
use crate::ascii_str::{AsciiChar, AsciiStr}; use crate::ascii_str::{AsciiChar, AsciiStr};
use crate::{NO_VALUES, Values}; use crate::{FastWritable, NO_VALUES, Values};
/// Returns adequate string representation (in KB, ..) of number of bytes /// Returns adequate string representation (in KB, ..) of number of bytes
/// ///

View File

@ -4,9 +4,9 @@ use std::{fmt, io, str};
use serde::Serialize; use serde::Serialize;
use serde_json::ser::{CompactFormatter, PrettyFormatter, Serializer}; use serde_json::ser::{CompactFormatter, PrettyFormatter, Serializer};
use super::{AsIndent, FastWritable}; use super::AsIndent;
use crate::ascii_str::{AsciiChar, AsciiStr}; use crate::ascii_str::{AsciiChar, AsciiStr};
use crate::{NO_VALUES, Values}; use crate::{FastWritable, NO_VALUES, Values};
/// Serialize to JSON (requires `json` feature) /// Serialize to JSON (requires `json` feature)
/// ///

View File

@ -27,8 +27,8 @@ pub use self::alloc::{
}; };
pub use self::builtin::{PluralizeCount, center, join, pluralize, truncate}; pub use self::builtin::{PluralizeCount, center, join, pluralize, truncate};
pub use self::escape::{ pub use self::escape::{
AutoEscape, AutoEscaper, Escaper, FastWritable, Html, HtmlSafe, HtmlSafeOutput, MaybeSafe, AutoEscape, AutoEscaper, Escaper, Html, HtmlSafe, HtmlSafeOutput, MaybeSafe, Safe, Text,
Safe, Text, Unsafe, Writable, WriteWritable, e, escape, safe, Unsafe, Writable, WriteWritable, e, escape, safe,
}; };
pub use self::humansize::filesizeformat; pub use self::humansize::filesizeformat;
#[cfg(feature = "serde_json")] #[cfg(feature = "serde_json")]

View File

@ -4,8 +4,8 @@ use std::fmt::Write;
use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, utf8_percent_encode}; use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, utf8_percent_encode};
use crate::Values; use crate::filters::HtmlSafeOutput;
use crate::filters::{FastWritable, HtmlSafeOutput}; use crate::{FastWritable, Values};
// Urlencode char encoding set. Only the characters in the unreserved set don't // Urlencode char encoding set. Only the characters in the unreserved set don't
// have any special purpose in any part of a URI and can be safely left // have any special purpose in any part of a URI and can be safely left

View File

@ -12,10 +12,9 @@ use core::iter::{Enumerate, Peekable};
use core::ops::Deref; use core::ops::Deref;
use core::pin::Pin; use core::pin::Pin;
use crate::Values;
pub use crate::error::{ErrorMarker, ResultConverter}; pub use crate::error::{ErrorMarker, ResultConverter};
use crate::filters::FastWritable;
pub use crate::values::get_value; pub use crate::values::get_value;
use crate::{FastWritable, Values};
pub struct TemplateLoop<I> pub struct TemplateLoop<I>
where where

View File

@ -76,6 +76,7 @@ mod values;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use alloc::string::String; use alloc::string::String;
use core::fmt; use core::fmt;
use core::ops::Deref;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::io; use std::io;
@ -109,7 +110,7 @@ pub use crate::values::{NO_VALUES, Value, Values, get_value};
/// `.render()`. /// `.render()`.
/// ///
/// [dynamic methods calls]: <https://doc.rust-lang.org/stable/std/keyword.dyn.html> /// [dynamic methods calls]: <https://doc.rust-lang.org/stable/std/keyword.dyn.html>
pub trait Template: fmt::Display + filters::FastWritable { pub trait Template: fmt::Display + FastWritable {
/// Helper method which allocates a new `String` and renders into it. /// Helper method which allocates a new `String` and renders into it.
#[inline] #[inline]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
@ -370,6 +371,194 @@ macro_rules! impl_for_ref {
} }
} }
/// Types implementing this trait can be written without needing to employ an [`fmt::Formatter`].
pub trait FastWritable {
/// Used internally by askama to speed up writing some types.
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn Values,
) -> crate::Result<()>;
}
const _: () = {
crate::impl_for_ref! {
impl FastWritable for T {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
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<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn Values,
) -> crate::Result<()> {
self.as_ref().get_ref().write_into(dest, values)
}
}
#[cfg(feature = "alloc")]
impl<T: FastWritable + alloc::borrow::ToOwned> FastWritable for alloc::borrow::Cow<'_, T> {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn Values,
) -> crate::Result<()> {
T::write_into(self.as_ref(), dest, values)
}
}
// implement FastWritable for a list of types
macro_rules! impl_for_int {
($($ty:ty)*) => { $(
impl FastWritable for $ty {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
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
);
// implement FastWritable for a list of non-zero integral types
macro_rules! impl_for_nz_int {
($($id:ident)*) => { $(
impl FastWritable for core::num::$id {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
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<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
_: &dyn Values,
) -> crate::Result<()> {
Ok(dest.write_str(self)?)
}
}
#[cfg(feature = "alloc")]
impl FastWritable for alloc::string::String {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
values: &dyn Values,
) -> crate::Result<()> {
self.as_str().write_into(dest, values)
}
}
impl FastWritable for bool {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
_: &dyn Values,
) -> crate::Result<()> {
Ok(dest.write_str(match self {
true => "true",
false => "false",
})?)
}
}
impl FastWritable for char {
#[inline]
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
_: &dyn Values,
) -> crate::Result<()> {
Ok(dest.write_char(*self)?)
}
}
impl FastWritable for fmt::Arguments<'_> {
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
_: &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<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
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<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
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<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
_: &dyn Values,
) -> crate::Result<()> {
Ok(write!(dest, "{}", self.0)?)
}
}
};
pub(crate) use impl_for_ref; pub(crate) use impl_for_ref;
#[cfg(all(test, feature = "alloc"))] #[cfg(all(test, feature = "alloc"))]
@ -404,7 +593,7 @@ mod tests {
} }
} }
impl filters::FastWritable for Test { impl FastWritable for Test {
#[inline] #[inline]
fn write_into<W: fmt::Write + ?Sized>( fn write_into<W: fmt::Write + ?Sized>(
&self, &self,

View File

@ -308,7 +308,7 @@ impl<'a, 'h> Generator<'a, 'h> {
} }
// cannot use `crate::integrations::impl_fast_writable()` w/o cloning the struct // cannot use `crate::integrations::impl_fast_writable()` w/o cloning the struct
impl #wrapper_impl_generics askama::filters::FastWritable impl #wrapper_impl_generics askama::FastWritable
for #wrapper_id #wrapper_ty_generics #wrapper_where_clause { for #wrapper_id #wrapper_ty_generics #wrapper_where_clause {
#[inline] #[inline]
fn write_into<AskamaW>( fn write_into<AskamaW>(

View File

@ -60,7 +60,7 @@ fn impl_display(ast: &DeriveInput, buf: &mut Buffer) {
/// Implement `FastWritable` for the given item. /// Implement `FastWritable` for the given item.
fn impl_fast_writable(ast: &DeriveInput, buf: &mut Buffer) { fn impl_fast_writable(ast: &DeriveInput, buf: &mut Buffer) {
write_header(ast, buf, "askama::filters::FastWritable"); write_header(ast, buf, "askama::FastWritable");
buf.write( buf.write(
"\ "\
#[inline]\ #[inline]\

View File

@ -67,7 +67,7 @@ fn compare_ex(
} }
} }
impl askama::filters::FastWritable for Foo { impl askama::FastWritable for Foo {
#[inline] #[inline]
fn write_into<AskamaW>( fn write_into<AskamaW>(
&self, &self,

View File

@ -42,7 +42,7 @@ your `FastWritable` implementation:
[`fmt::Display`]: <https://doc.rust-lang.org/stable/std/fmt/trait.Display.html> [`fmt::Display`]: <https://doc.rust-lang.org/stable/std/fmt/trait.Display.html>
[`fmt::Formatter`]: <https://doc.rust-lang.org/stable/std/fmt/struct.Formatter.html> [`fmt::Formatter`]: <https://doc.rust-lang.org/stable/std/fmt/struct.Formatter.html>
[`FastWritable`]: <./doc/askama/filters/trait.FastWritable.html> [`FastWritable`]: <./doc/askama/trait.FastWritable.html>
[autoref-based specialization]: <https://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html> [autoref-based specialization]: <https://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html>
## Slow Debug Recompilations ## Slow Debug Recompilations

View File

@ -1,7 +1,6 @@
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use askama::NO_VALUES; use askama::{FastWritable, NO_VALUES};
use askama::filters::FastWritable;
// In a real application, please have a look at // In a real application, please have a look at
// https://github.com/kdeldycke/awesome-falsehood/blob/690a070/readme.md#human-identity // https://github.com/kdeldycke/awesome-falsehood/blob/690a070/readme.md#human-identity