From 6f912abee629a35166bff894060b3c3ace7c9852 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 20 Apr 2025 12:15:29 +0200 Subject: [PATCH] Move `FastWritable` into `askama` root --- askama/src/filters/alloc.rs | 4 +- askama/src/filters/builtin.rs | 3 +- askama/src/filters/escape.rs | 193 +----------------- askama/src/filters/humansize.rs | 3 +- askama/src/filters/json.rs | 4 +- askama/src/filters/mod.rs | 4 +- askama/src/filters/urlencode.rs | 4 +- askama/src/helpers.rs | 3 +- askama/src/lib.rs | 193 +++++++++++++++++- askama_derive/src/generator.rs | 2 +- askama_derive/src/integration.rs | 2 +- askama_derive/src/tests.rs | 2 +- book/src/performance.md | 2 +- ...mple_performance_fmt_call_fast_writable.rs | 3 +- 14 files changed, 208 insertions(+), 214 deletions(-) diff --git a/askama/src/filters/alloc.rs b/askama/src/filters/alloc.rs index 05ade6d9..6164b3ea 100644 --- a/askama/src/filters/alloc.rs +++ b/askama/src/filters/alloc.rs @@ -7,9 +7,9 @@ use core::fmt::{self, Write}; use core::ops::Deref; use core::pin::Pin; +use super::MAX_LEN; use super::escape::HtmlSafeOutput; -use super::{FastWritable, MAX_LEN}; -use crate::Result; +use crate::{FastWritable, Result}; /// Return an ephemeral `&str` for `$src: impl fmt::Display` /// diff --git a/askama/src/filters/builtin.rs b/askama/src/filters/builtin.rs index 4f8d8cd8..7e3e1f73 100644 --- a/askama/src/filters/builtin.rs +++ b/askama/src/filters/builtin.rs @@ -5,8 +5,7 @@ use core::ops::Deref; use core::pin::Pin; use super::MAX_LEN; -use super::escape::FastWritable; -use crate::{Error, Result, Values}; +use crate::{Error, FastWritable, Result, Values}; /// Limit string length, appends '...' if truncated /// diff --git a/askama/src/filters/escape.rs b/askama/src/filters/escape.rs index d1a05b6b..846a5b60 100644 --- a/askama/src/filters/escape.rs +++ b/askama/src/filters/escape.rs @@ -1,10 +1,9 @@ use core::convert::Infallible; use core::fmt::{self, Formatter, Write}; -use core::ops::Deref; use core::pin::Pin; use core::str; -use crate::Values; +use crate::{FastWritable, Values}; /// Marks a string (or other `Display` type) as safe /// @@ -518,196 +517,6 @@ pub trait WriteWritable { ) -> 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( - &self, - dest: &mut W, - values: &dyn Values, - ) -> crate::Result<()>; -} - -const _: () = { - crate::impl_for_ref! { - impl FastWritable for T { - #[inline] - fn write_into( - &self, - dest: &mut W, - values: &dyn Values, - ) -> crate::Result<()> { - ::write_into(self, dest, values) - } - } - } - - impl FastWritable for Pin - where - T: Deref, - ::Target: FastWritable, - { - #[inline] - fn write_into( - &self, - dest: &mut W, - values: &dyn Values, - ) -> crate::Result<()> { - self.as_ref().get_ref().write_into(dest, values) - } - } - - #[cfg(feature = "alloc")] - impl FastWritable for alloc::borrow::Cow<'_, T> { - #[inline] - fn write_into( - &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( - &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( - &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( - &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( - &self, - dest: &mut W, - values: &dyn Values, - ) -> crate::Result<()> { - self.as_str().write_into(dest, values) - } - } - - impl FastWritable for bool { - #[inline] - fn write_into( - &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( - &self, - dest: &mut W, - _: &dyn Values, - ) -> crate::Result<()> { - Ok(dest.write_char(*self)?) - } - } - - impl FastWritable for fmt::Arguments<'_> { - fn write_into( - &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 WriteWritable for &Writable<'_, S> { - #[inline] - fn askama_write( - &self, - dest: &mut W, - values: &dyn Values, - ) -> crate::Result<()> { - self.0.render_into_with_values(dest, values) - } - } - - impl WriteWritable for &&Writable<'_, S> { - #[inline] - fn askama_write( - &self, - dest: &mut W, - values: &dyn Values, - ) -> crate::Result<()> { - self.0.write_into(dest, values) - } - } - - impl WriteWritable for &&&Writable<'_, S> { - #[inline] - fn askama_write( - &self, - dest: &mut W, - _: &dyn Values, - ) -> crate::Result<()> { - Ok(write!(dest, "{}", self.0)?) - } - } -}; - #[test] #[cfg(feature = "alloc")] fn test_escape() { diff --git a/askama/src/filters/humansize.rs b/askama/src/filters/humansize.rs index 04359788..d8f489a7 100644 --- a/askama/src/filters/humansize.rs +++ b/askama/src/filters/humansize.rs @@ -2,9 +2,8 @@ use core::convert::Infallible; use core::fmt; use core::mem::MaybeUninit; -use super::FastWritable; 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 /// diff --git a/askama/src/filters/json.rs b/askama/src/filters/json.rs index c1d718dd..b040767f 100644 --- a/askama/src/filters/json.rs +++ b/askama/src/filters/json.rs @@ -4,9 +4,9 @@ use std::{fmt, io, str}; use serde::Serialize; use serde_json::ser::{CompactFormatter, PrettyFormatter, Serializer}; -use super::{AsIndent, FastWritable}; +use super::AsIndent; use crate::ascii_str::{AsciiChar, AsciiStr}; -use crate::{NO_VALUES, Values}; +use crate::{FastWritable, NO_VALUES, Values}; /// Serialize to JSON (requires `json` feature) /// diff --git a/askama/src/filters/mod.rs b/askama/src/filters/mod.rs index ee819c04..7fd72ef4 100644 --- a/askama/src/filters/mod.rs +++ b/askama/src/filters/mod.rs @@ -27,8 +27,8 @@ pub use self::alloc::{ }; pub use self::builtin::{PluralizeCount, center, join, pluralize, truncate}; pub use self::escape::{ - AutoEscape, AutoEscaper, Escaper, FastWritable, Html, HtmlSafe, HtmlSafeOutput, MaybeSafe, - Safe, Text, Unsafe, Writable, WriteWritable, e, escape, safe, + AutoEscape, AutoEscaper, Escaper, Html, HtmlSafe, HtmlSafeOutput, MaybeSafe, Safe, Text, + Unsafe, Writable, WriteWritable, e, escape, safe, }; pub use self::humansize::filesizeformat; #[cfg(feature = "serde_json")] diff --git a/askama/src/filters/urlencode.rs b/askama/src/filters/urlencode.rs index 2fa5c4c8..a5205e6d 100644 --- a/askama/src/filters/urlencode.rs +++ b/askama/src/filters/urlencode.rs @@ -4,8 +4,8 @@ use std::fmt::Write; use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, utf8_percent_encode}; -use crate::Values; -use crate::filters::{FastWritable, HtmlSafeOutput}; +use crate::filters::HtmlSafeOutput; +use crate::{FastWritable, Values}; // 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 diff --git a/askama/src/helpers.rs b/askama/src/helpers.rs index 059910ba..4188d334 100644 --- a/askama/src/helpers.rs +++ b/askama/src/helpers.rs @@ -12,10 +12,9 @@ use core::iter::{Enumerate, Peekable}; use core::ops::Deref; use core::pin::Pin; -use crate::Values; pub use crate::error::{ErrorMarker, ResultConverter}; -use crate::filters::FastWritable; pub use crate::values::get_value; +use crate::{FastWritable, Values}; pub struct TemplateLoop where diff --git a/askama/src/lib.rs b/askama/src/lib.rs index 3a039156..59931307 100644 --- a/askama/src/lib.rs +++ b/askama/src/lib.rs @@ -76,6 +76,7 @@ mod values; #[cfg(feature = "alloc")] use alloc::string::String; use core::fmt; +use core::ops::Deref; #[cfg(feature = "std")] use std::io; @@ -109,7 +110,7 @@ pub use crate::values::{NO_VALUES, Value, Values, get_value}; /// `.render()`. /// /// [dynamic methods calls]: -pub trait Template: fmt::Display + filters::FastWritable { +pub trait Template: fmt::Display + FastWritable { /// Helper method which allocates a new `String` and renders into it. #[inline] #[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( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()>; +} + +const _: () = { + crate::impl_for_ref! { + impl FastWritable for T { + #[inline] + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + ::write_into(self, dest, values) + } + } + } + + impl FastWritable for core::pin::Pin + where + T: Deref, + ::Target: FastWritable, + { + #[inline] + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + self.as_ref().get_ref().write_into(dest, values) + } + } + + #[cfg(feature = "alloc")] + impl FastWritable for alloc::borrow::Cow<'_, T> { + #[inline] + fn write_into( + &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( + &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( + &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( + &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( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + self.as_str().write_into(dest, values) + } + } + + impl FastWritable for bool { + #[inline] + fn write_into( + &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( + &self, + dest: &mut W, + _: &dyn Values, + ) -> crate::Result<()> { + Ok(dest.write_char(*self)?) + } + } + + impl FastWritable for fmt::Arguments<'_> { + fn write_into( + &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 filters::WriteWritable for &filters::Writable<'_, S> { + #[inline] + fn askama_write( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + self.0.render_into_with_values(dest, values) + } + } + + impl filters::WriteWritable for &&filters::Writable<'_, S> { + #[inline] + fn askama_write( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + self.0.write_into(dest, values) + } + } + + impl filters::WriteWritable for &&&filters::Writable<'_, S> { + #[inline] + fn askama_write( + &self, + dest: &mut W, + _: &dyn Values, + ) -> crate::Result<()> { + Ok(write!(dest, "{}", self.0)?) + } + } +}; + pub(crate) use impl_for_ref; #[cfg(all(test, feature = "alloc"))] @@ -404,7 +593,7 @@ mod tests { } } - impl filters::FastWritable for Test { + impl FastWritable for Test { #[inline] fn write_into( &self, diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index ea1bbfce..1d713613 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -308,7 +308,7 @@ impl<'a, 'h> Generator<'a, 'h> { } // 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 { #[inline] fn write_into( diff --git a/askama_derive/src/integration.rs b/askama_derive/src/integration.rs index 330f9204..930ac915 100644 --- a/askama_derive/src/integration.rs +++ b/askama_derive/src/integration.rs @@ -60,7 +60,7 @@ fn impl_display(ast: &DeriveInput, buf: &mut Buffer) { /// Implement `FastWritable` for the given item. fn impl_fast_writable(ast: &DeriveInput, buf: &mut Buffer) { - write_header(ast, buf, "askama::filters::FastWritable"); + write_header(ast, buf, "askama::FastWritable"); buf.write( "\ #[inline]\ diff --git a/askama_derive/src/tests.rs b/askama_derive/src/tests.rs index 2a2d2926..605fdbcc 100644 --- a/askama_derive/src/tests.rs +++ b/askama_derive/src/tests.rs @@ -67,7 +67,7 @@ fn compare_ex( } } - impl askama::filters::FastWritable for Foo { + impl askama::FastWritable for Foo { #[inline] fn write_into( &self, diff --git a/book/src/performance.md b/book/src/performance.md index 5c61b6cb..02063ad8 100644 --- a/book/src/performance.md +++ b/book/src/performance.md @@ -42,7 +42,7 @@ your `FastWritable` implementation: [`fmt::Display`]: [`fmt::Formatter`]: -[`FastWritable`]: <./doc/askama/filters/trait.FastWritable.html> +[`FastWritable`]: <./doc/askama/trait.FastWritable.html> [autoref-based specialization]: ## Slow Debug Recompilations diff --git a/testing/tests/book_example_performance_fmt_call_fast_writable.rs b/testing/tests/book_example_performance_fmt_call_fast_writable.rs index 33fb80c5..79265e1e 100644 --- a/testing/tests/book_example_performance_fmt_call_fast_writable.rs +++ b/testing/tests/book_example_performance_fmt_call_fast_writable.rs @@ -1,7 +1,6 @@ use std::fmt::{self, Write}; -use askama::NO_VALUES; -use askama::filters::FastWritable; +use askama::{FastWritable, NO_VALUES}; // In a real application, please have a look at // https://github.com/kdeldycke/awesome-falsehood/blob/690a070/readme.md#human-identity