diff --git a/askama/src/filters/builtin.rs b/askama/src/filters/builtin.rs index fbe91a58..4f8d8cd8 100644 --- a/askama/src/filters/builtin.rs +++ b/askama/src/filters/builtin.rs @@ -6,7 +6,7 @@ use core::pin::Pin; use super::MAX_LEN; use super::escape::FastWritable; -use crate::{Error, Result}; +use crate::{Error, Result, Values}; /// Limit string length, appends '...' if truncated /// @@ -50,9 +50,13 @@ impl fmt::Display for TruncateFilter { impl FastWritable for TruncateFilter { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { self.source - .write_into(&mut TruncateWriter::new(dest, self.remaining)) + .write_into(&mut TruncateWriter::new(dest, self.remaining), values) } } @@ -440,10 +444,14 @@ impl fmt::Display for Pluralize { impl FastWritable for Pluralize { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { match self { - Pluralize::Singular(value) => value.write_into(dest), - Pluralize::Plural(value) => value.write_into(dest), + Pluralize::Singular(value) => value.write_into(dest, values), + Pluralize::Plural(value) => value.write_into(dest, values), } } } diff --git a/askama/src/filters/escape.rs b/askama/src/filters/escape.rs index 9bc08254..52d6136a 100644 --- a/askama/src/filters/escape.rs +++ b/askama/src/filters/escape.rs @@ -82,8 +82,12 @@ impl fmt::Display for EscapeDisplay { impl FastWritable for EscapeDisplay { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { - self.0.write_into(&mut EscapeWriter(dest, self.1)) + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + self.0.write_into(&mut EscapeWriter(dest, self.1), values) } } @@ -303,12 +307,16 @@ const _: () = { // This is the fallback. The filter is not the last element of the filter chain. impl FastWritable for MaybeSafe { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { let inner = match self { MaybeSafe::Safe(inner) => inner, MaybeSafe::NeedsEscaping(inner) => inner, }; - inner.write_into(dest) + inner.write_into(dest, values) } } @@ -338,10 +346,14 @@ const _: () = { } impl FastWritable for Wrapped<'_, T, E> { - fn write_into(&self, dest: &mut W) -> crate::Result<()> { + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { match *self { - Wrapped::Safe(t) => t.write_into(dest), - Wrapped::NeedsEscaping(t, e) => EscapeDisplay(t, e).write_into(dest), + Wrapped::Safe(t) => t.write_into(dest, values), + Wrapped::NeedsEscaping(t, e) => EscapeDisplay(t, e).write_into(dest, values), } } } @@ -409,8 +421,12 @@ const _: () = { // This is the fallback. The filter is not the last element of the filter chain. impl FastWritable for Safe { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { - self.0.write_into(dest) + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + self.0.write_into(dest, values) } } @@ -503,15 +519,23 @@ pub trait WriteWritable { /// 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) -> crate::Result<()>; + 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) -> crate::Result<()> { - ::write_into(self, dest) + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + ::write_into(self, dest, values) } } } @@ -522,16 +546,24 @@ const _: () = { ::Target: FastWritable, { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { - self.as_ref().get_ref().write_into(dest) + 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) -> crate::Result<()> { - T::write_into(self.as_ref(), dest) + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + T::write_into(self.as_ref(), dest, values) } } @@ -540,8 +572,12 @@ const _: () = { ($($ty:ty)*) => { $( impl FastWritable for $ty { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { - itoa::Buffer::new().format(*self).write_into(dest) + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + itoa::Buffer::new().format(*self).write_into(dest, values) } } )* }; @@ -557,8 +593,12 @@ const _: () = { ($($id:ident)*) => { $( impl FastWritable for core::num::$id { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { - self.get().write_into(dest) + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + self.get().write_into(dest, values) } } )* }; @@ -571,7 +611,11 @@ const _: () = { impl FastWritable for str { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { + fn write_into( + &self, + dest: &mut W, + _: &dyn Values, + ) -> crate::Result<()> { Ok(dest.write_str(self)?) } } @@ -579,14 +623,22 @@ const _: () = { #[cfg(feature = "alloc")] impl FastWritable for alloc::string::String { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { - self.as_str().write_into(dest) + 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) -> crate::Result<()> { + fn write_into( + &self, + dest: &mut W, + _: &dyn Values, + ) -> crate::Result<()> { Ok(dest.write_str(match self { true => "true", false => "false", @@ -596,13 +648,21 @@ const _: () = { impl FastWritable for char { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { + 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) -> crate::Result<()> { + 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), @@ -626,9 +686,9 @@ const _: () = { fn askama_write( &self, dest: &mut W, - _: &dyn Values, + values: &dyn Values, ) -> crate::Result<()> { - self.0.write_into(dest) + self.0.write_into(dest, values) } } diff --git a/askama/src/filters/humansize.rs b/askama/src/filters/humansize.rs index 716991b4..04359788 100644 --- a/askama/src/filters/humansize.rs +++ b/askama/src/filters/humansize.rs @@ -4,6 +4,7 @@ use core::mem::MaybeUninit; use super::FastWritable; use crate::ascii_str::{AsciiChar, AsciiStr}; +use crate::{NO_VALUES, Values}; /// Returns adequate string representation (in KB, ..) of number of bytes /// @@ -33,14 +34,18 @@ pub struct FilesizeFormatFilter(f32); impl fmt::Display for FilesizeFormatFilter { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(self.write_into(f)?) + Ok(self.write_into(f, NO_VALUES)?) } } impl FastWritable for FilesizeFormatFilter { - fn write_into(&self, dest: &mut W) -> crate::Result<()> { + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { if self.0 < 1e3 { - (self.0 as u32).write_into(dest)?; + (self.0 as u32).write_into(dest, values)?; Ok(dest.write_str(" B")?) } else if let Some((prefix, factor)) = SI_PREFIXES .iter() @@ -49,8 +54,8 @@ impl FastWritable for FilesizeFormatFilter { { // u32 is big enough to hold the number 999_999 let scaled = (self.0 * factor) as u32; - (scaled / 100).write_into(dest)?; - format_frac(&mut MaybeUninit::uninit(), prefix, scaled).write_into(dest) + (scaled / 100).write_into(dest, values)?; + format_frac(&mut MaybeUninit::uninit(), prefix, scaled).write_into(dest, values) } else { too_big(self.0, dest) } diff --git a/askama/src/filters/json.rs b/askama/src/filters/json.rs index 7dbae4c1..53185606 100644 --- a/askama/src/filters/json.rs +++ b/askama/src/filters/json.rs @@ -8,6 +8,7 @@ use serde_json::ser::{CompactFormatter, PrettyFormatter, Serializer}; use super::FastWritable; use crate::ascii_str::{AsciiChar, AsciiStr}; +use crate::{NO_VALUES, Values}; /// Serialize to JSON (requires `json` feature) /// @@ -188,7 +189,8 @@ where } impl FastWritable for ToJson { - fn write_into(&self, f: &mut W) -> crate::Result<()> { + #[inline] + fn write_into(&self, f: &mut W, _: &dyn Values) -> crate::Result<()> { serialize(f, &self.value, CompactFormatter) } } @@ -196,12 +198,13 @@ impl FastWritable for ToJson { impl fmt::Display for ToJson { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(self.write_into(f)?) + Ok(self.write_into(f, NO_VALUES)?) } } impl FastWritable for ToJsonPretty { - fn write_into(&self, f: &mut W) -> crate::Result<()> { + #[inline] + fn write_into(&self, f: &mut W, _: &dyn Values) -> crate::Result<()> { serialize( f, &self.value, @@ -213,11 +216,10 @@ impl FastWritable for ToJsonPretty { impl fmt::Display for ToJsonPretty { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(self.write_into(f)?) + Ok(self.write_into(f, NO_VALUES)?) } } -#[inline] fn serialize(dest: &mut W, value: &S, formatter: F) -> Result<(), crate::Error> where S: Serialize + ?Sized, diff --git a/askama/src/filters/urlencode.rs b/askama/src/filters/urlencode.rs index 8a837792..2fa5c4c8 100644 --- a/askama/src/filters/urlencode.rs +++ b/askama/src/filters/urlencode.rs @@ -4,6 +4,7 @@ use std::fmt::Write; use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, utf8_percent_encode}; +use crate::Values; use crate::filters::{FastWritable, HtmlSafeOutput}; // Urlencode char encoding set. Only the characters in the unreserved set don't @@ -110,8 +111,12 @@ impl fmt::Display for UrlencodeFilter { impl FastWritable for UrlencodeFilter { #[inline] - fn write_into(&self, f: &mut W) -> crate::Result<()> { - self.0.write_into(&mut UrlencodeWriter(f, self.1)) + fn write_into( + &self, + f: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + self.0.write_into(&mut UrlencodeWriter(f, self.1), values) } } diff --git a/askama/src/helpers.rs b/askama/src/helpers.rs index 7efdbd30..2b89b2cf 100644 --- a/askama/src/helpers.rs +++ b/askama/src/helpers.rs @@ -12,6 +12,7 @@ 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; @@ -245,7 +246,7 @@ impl fmt::Display for Empty { impl FastWritable for Empty { #[inline] - fn write_into(&self, _: &mut W) -> crate::Result<()> { + fn write_into(&self, _: &mut W, _: &dyn Values) -> crate::Result<()> { Ok(()) } } @@ -267,9 +268,13 @@ impl fmt::Display for Concat { impl FastWritable for Concat { #[inline] - fn write_into(&self, dest: &mut W) -> crate::Result<()> { - self.0.write_into(dest)?; - self.1.write_into(dest) + fn write_into( + &self, + dest: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + self.0.write_into(dest, values)?; + self.1.write_into(dest, values) } } diff --git a/askama/src/lib.rs b/askama/src/lib.rs index d53950c8..3a039156 100644 --- a/askama/src/lib.rs +++ b/askama/src/lib.rs @@ -406,8 +406,12 @@ mod tests { impl filters::FastWritable for Test { #[inline] - fn write_into(&self, f: &mut W) -> crate::Result<()> { - self.render_into(f) + fn write_into( + &self, + f: &mut W, + values: &dyn Values, + ) -> crate::Result<()> { + self.render_into_with_values(f, values) } } diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index edca2eaa..aa0d83c3 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -310,11 +310,15 @@ impl<'a, 'h> Generator<'a, 'h> { impl #wrapper_impl_generics askama::filters::FastWritable for #wrapper_id #wrapper_ty_generics #wrapper_where_clause { #[inline] - fn write_into(&self, dest: &mut AskamaW) -> askama::Result<()> + fn write_into( + &self, + dest: &mut AskamaW, + values: &dyn askama::Values, + ) -> askama::Result<()> where AskamaW: askama::helpers::core::fmt::Write + ?askama::helpers::core::marker::Sized { - <_ as askama::Template>::render_into(self, dest) + <_ as askama::Template>::render_into_with_values(self, dest, values) } } diff --git a/askama_derive/src/integration.rs b/askama_derive/src/integration.rs index f8653339..330f9204 100644 --- a/askama_derive/src/integration.rs +++ b/askama_derive/src/integration.rs @@ -64,11 +64,15 @@ fn impl_fast_writable(ast: &DeriveInput, buf: &mut Buffer) { buf.write( "\ #[inline]\ - fn write_into(&self, dest: &mut AskamaW) -> askama::Result<()> \ + fn write_into(\ + &self,\ + dest: &mut AskamaW,\ + values: &dyn askama::Values\ + ) -> askama::Result<()> \ where \ AskamaW: askama::helpers::core::fmt::Write + ?askama::helpers::core::marker::Sized,\ {\ - askama::Template::render_into(self, dest)\ + askama::Template::render_into_with_values(self, dest, values)\ }\ }", ); diff --git a/askama_derive/src/tests.rs b/askama_derive/src/tests.rs index fa901ec1..2a2d2926 100644 --- a/askama_derive/src/tests.rs +++ b/askama_derive/src/tests.rs @@ -69,11 +69,15 @@ fn compare_ex( impl askama::filters::FastWritable for Foo { #[inline] - fn write_into(&self, dest: &mut AskamaW) -> askama::Result<()> + fn write_into( + &self, + dest: &mut AskamaW, + values: &dyn askama::Values, + ) -> askama::Result<()> where AskamaW: askama::helpers::core::fmt::Write + ?askama::helpers::core::marker::Sized, { - askama::Template::render_into(self, dest) + askama::Template::render_into_with_values(self, dest, values) } } }; diff --git a/testing/tests/values.rs b/testing/tests/values.rs index fa6fea81..97591ae1 100644 --- a/testing/tests/values.rs +++ b/testing/tests/values.rs @@ -88,3 +88,26 @@ fn test_value_function_getter() { values.insert("a".to_string(), Box::new(false)); assert_eq!(V.render_with_values(&values).unwrap(), ""); } + +#[test] +fn test_value_in_subtemplates() { + // In this test we make sure that values are passed down to transcluded sub-templates, + // even if there is a filter in the mix, e.g. the implicit `|escape` filter. + + #[derive(Template)] + #[template(source = r#"{{ Child }}"#, ext = "html")] + struct Parent; + + #[derive(Template)] + #[template( + source = r#"Hello, {{ askama::get_value::("who")? }}!"#, + ext = "html" + )] + struct Child; + + let values: (&str, &dyn Any) = ("who", &"".to_owned()); + assert_eq!( + Parent.render_with_values(&values).unwrap(), + "Hello, &#60;world&#62;!", // sic: escaped twice + ); +}