mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-28 05:21:14 +00:00
Merge pull request #311 from GuillaumeGomez/runtime-values
Add support for "runtime" values
This commit is contained in:
commit
cae09c50f4
@ -2,6 +2,7 @@
|
||||
|
||||
- [Introduction](./introduction.md)
|
||||
- [Creating templates](./creating_templates.md)
|
||||
- [Runtime values](./runtime.md)
|
||||
- [Debugging](./debugging.md)
|
||||
- [Configuration](./configuration.md)
|
||||
- [Template syntax](./template_syntax.md)
|
||||
|
38
book/src/runtime.md
Normal file
38
book/src/runtime.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Runtime values
|
||||
|
||||
It is possible to define variables at runtime and to use them in the templates using the `values`
|
||||
filter or the `rinja::get_value` function and to call the `_with_values` variants of the `render`
|
||||
methods. It expects an extra argument implementing the `Values` trait. This trait is implemented on
|
||||
a few types provided by the `std`, like `HashMap`:
|
||||
|
||||
```rust
|
||||
use std::collections::HashMap;
|
||||
|
||||
let mut values: HashMap<&str, Box<dyn Any>> = HashMap::new();
|
||||
// We add a new value named "name" with the value "Bibop".
|
||||
values.insert("name", Box::new("Bibop"));
|
||||
values.insert("age", Box::new(12u32));
|
||||
```
|
||||
|
||||
The `Values` trait is expecting types storing data with the `Any` trait, allowing to store any type.
|
||||
|
||||
Then to render with these values:
|
||||
|
||||
```rust
|
||||
template_struct.render_with_values(&values).unwrap();
|
||||
```
|
||||
|
||||
There are two ways to get the values from the template, either by using the `value` filter
|
||||
or by calling directly the `rinja::get_value` function:
|
||||
|
||||
```jinja
|
||||
{% if let Ok(name) = "name"|value::<&str> %}
|
||||
name is {{ name }}
|
||||
{% endif %}
|
||||
{% if let Ok(age) = rinja::get_value::<u32>("age") %}
|
||||
age is {{ age }}
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
If you try to retrieve a value with the wrong type or that you didn't set, you will get an
|
||||
`Err(ValueError)`.
|
@ -33,6 +33,7 @@ serde_json = { version = "1.0", optional = true, default-features = false, featu
|
||||
itoa = "1.0.11"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
criterion = "0.5"
|
||||
|
||||
[badges]
|
||||
|
@ -19,6 +19,10 @@ pub type Result<I, E = Error> = core::result::Result<I, E>;
|
||||
pub enum Error {
|
||||
/// Generic, unspecified formatting error
|
||||
Fmt,
|
||||
/// Key not present in [`Values`][crate::Values]
|
||||
ValueMissing,
|
||||
/// Incompatible value type for key in [`Values`][crate::Values]
|
||||
ValueType,
|
||||
/// An error raised by using `?` in a template
|
||||
#[cfg(feature = "alloc")]
|
||||
Custom(Box<dyn StdError + Send + Sync>),
|
||||
@ -41,6 +45,8 @@ impl Error {
|
||||
pub fn into_box(self) -> Box<dyn StdError + Send + Sync> {
|
||||
match self {
|
||||
Error::Fmt => fmt::Error.into(),
|
||||
Error::ValueMissing => Box::new(Error::ValueMissing),
|
||||
Error::ValueType => Box::new(Error::ValueType),
|
||||
Error::Custom(err) => err,
|
||||
#[cfg(feature = "serde_json")]
|
||||
Error::Json(err) => err.into(),
|
||||
@ -66,6 +72,8 @@ impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
match self {
|
||||
Error::Fmt => Some(&fmt::Error),
|
||||
Error::ValueMissing => None,
|
||||
Error::ValueType => None,
|
||||
#[cfg(feature = "alloc")]
|
||||
Error::Custom(err) => Some(err.as_ref()),
|
||||
#[cfg(feature = "serde_json")]
|
||||
@ -78,6 +86,8 @@ impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::Fmt => fmt::Error.fmt(f),
|
||||
Error::ValueMissing => f.write_str("key missing in values"),
|
||||
Error::ValueType => f.write_str("value has wrong type"),
|
||||
#[cfg(feature = "alloc")]
|
||||
Error::Custom(err) => err.fmt(f),
|
||||
#[cfg(feature = "serde_json")]
|
||||
|
@ -4,6 +4,8 @@ use core::ops::Deref;
|
||||
use core::pin::Pin;
|
||||
use core::str;
|
||||
|
||||
use crate::Values;
|
||||
|
||||
/// Marks a string (or other `Display` type) as safe
|
||||
///
|
||||
/// Use this if you want to allow markup in an expression, or if you know
|
||||
@ -489,7 +491,11 @@ pub struct Writable<'a, S: ?Sized>(pub &'a S);
|
||||
/// Used internally by rinja to select the appropriate [`write!()`] mechanism
|
||||
pub trait WriteWritable {
|
||||
/// Used internally by rinja to select the appropriate [`write!()`] mechanism
|
||||
fn rinja_write<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()>;
|
||||
fn rinja_write<W: fmt::Write + ?Sized>(
|
||||
&self,
|
||||
dest: &mut W,
|
||||
values: &dyn Values,
|
||||
) -> crate::Result<()>;
|
||||
}
|
||||
|
||||
/// Used internally by rinja to speed up writing some types.
|
||||
@ -604,16 +610,35 @@ const _: () = {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: FastWritable + ?Sized> WriteWritable for &Writable<'_, S> {
|
||||
impl<S: crate::Template + ?Sized> WriteWritable for &Writable<'_, S> {
|
||||
#[inline]
|
||||
fn rinja_write<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||
fn rinja_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 rinja_write<W: fmt::Write + ?Sized>(
|
||||
&self,
|
||||
dest: &mut W,
|
||||
_: &dyn Values,
|
||||
) -> crate::Result<()> {
|
||||
self.0.write_into(dest)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: fmt::Display + ?Sized> WriteWritable for &&Writable<'_, S> {
|
||||
impl<S: fmt::Display + ?Sized> WriteWritable for &&&Writable<'_, S> {
|
||||
#[inline]
|
||||
fn rinja_write<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
|
||||
fn rinja_write<W: fmt::Write + ?Sized>(
|
||||
&self,
|
||||
dest: &mut W,
|
||||
_: &dyn Values,
|
||||
) -> crate::Result<()> {
|
||||
Ok(write!(dest, "{}", self.0)?)
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ use core::pin::Pin;
|
||||
|
||||
pub use crate::error::{ErrorMarker, ResultConverter};
|
||||
use crate::filters::FastWritable;
|
||||
pub use crate::values::get_value;
|
||||
|
||||
pub struct TemplateLoop<I>
|
||||
where
|
||||
|
135
rinja/src/lib.rs
135
rinja/src/lib.rs
@ -70,6 +70,7 @@ pub mod filters;
|
||||
#[doc(hidden)]
|
||||
pub mod helpers;
|
||||
mod html;
|
||||
mod values;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::String;
|
||||
@ -83,6 +84,7 @@ pub use rinja_derive::Template;
|
||||
pub use crate as shared;
|
||||
pub use crate::error::{Error, Result};
|
||||
pub use crate::helpers::PrimitiveType;
|
||||
pub use crate::values::{NO_VALUES, Value, Values, get_value};
|
||||
|
||||
/// Main `Template` trait; implementations are generally derived
|
||||
///
|
||||
@ -106,21 +108,50 @@ pub use crate::helpers::PrimitiveType;
|
||||
///
|
||||
/// [dynamic methods calls]: <https://doc.rust-lang.org/stable/std/keyword.dyn.html>
|
||||
pub trait Template: fmt::Display + filters::FastWritable {
|
||||
/// Helper method which allocates a new `String` and renders into it
|
||||
/// Helper method which allocates a new `String` and renders into it.
|
||||
#[inline]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn render(&self) -> Result<String> {
|
||||
self.render_with_values(NO_VALUES)
|
||||
}
|
||||
|
||||
/// Helper method which allocates a new `String` and renders into it with provided [`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(&mut buf)?;
|
||||
self.render_into_with_values(&mut buf, values)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Renders the template to the given `writer` fmt buffer
|
||||
fn render_into<W: fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()>;
|
||||
/// Renders the template to the given `writer` fmt buffer.
|
||||
#[inline]
|
||||
fn render_into<W: fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()> {
|
||||
self.render_into_with_values(writer, NO_VALUES)
|
||||
}
|
||||
|
||||
/// Renders the template to the given `writer` io buffer
|
||||
/// Renders the template to the given `writer` fmt buffer with provided [`Values`].
|
||||
fn render_into_with_values<W: fmt::Write + ?Sized>(
|
||||
&self,
|
||||
writer: &mut W,
|
||||
values: &dyn Values,
|
||||
) -> Result<()>;
|
||||
|
||||
/// Renders the template to the given `writer` io buffer.
|
||||
#[inline]
|
||||
#[cfg(feature = "std")]
|
||||
fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
|
||||
self.write_into_with_values(writer, NO_VALUES)
|
||||
}
|
||||
|
||||
/// Renders the template to the given `writer` io buffer with provided [`Values`].
|
||||
#[cfg(feature = "std")]
|
||||
fn write_into_with_values<W: io::Write + ?Sized>(
|
||||
&self,
|
||||
writer: &mut W,
|
||||
values: &dyn Values,
|
||||
) -> io::Result<()> {
|
||||
struct Wrapped<W: io::Write> {
|
||||
writer: W,
|
||||
err: Option<io::Error>,
|
||||
@ -138,7 +169,7 @@ pub trait Template: fmt::Display + filters::FastWritable {
|
||||
}
|
||||
|
||||
let mut wrapped = Wrapped { writer, err: None };
|
||||
if self.render_into(&mut wrapped).is_ok() {
|
||||
if self.render_into_with_values(&mut wrapped, values).is_ok() {
|
||||
Ok(())
|
||||
} else {
|
||||
let err = wrapped.err.take();
|
||||
@ -160,23 +191,48 @@ pub trait Template: fmt::Display + filters::FastWritable {
|
||||
}
|
||||
|
||||
impl<T: Template + ?Sized> Template for &T {
|
||||
#[inline]
|
||||
fn render_into<W: fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()> {
|
||||
<T as Template>::render_into(self, writer)
|
||||
}
|
||||
|
||||
#[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<W: fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()> {
|
||||
<T as Template>::render_into(self, writer)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn render_into_with_values<W: fmt::Write + ?Sized>(
|
||||
&self,
|
||||
writer: &mut W,
|
||||
values: &dyn Values,
|
||||
) -> Result<()> {
|
||||
<T as Template>::render_into_with_values(self, writer, values)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "std")]
|
||||
fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
|
||||
<T as Template>::write_into(self, writer)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "std")]
|
||||
fn write_into_with_values<W: io::Write + ?Sized>(
|
||||
&self,
|
||||
writer: &mut W,
|
||||
values: &dyn Values,
|
||||
) -> io::Result<()> {
|
||||
<T as Template>::write_into_with_values(self, writer, values)
|
||||
}
|
||||
|
||||
const SIZE_HINT: usize = T::SIZE_HINT;
|
||||
}
|
||||
|
||||
@ -186,18 +242,37 @@ impl<T: Template + ?Sized> Template for &T {
|
||||
///
|
||||
/// [`dyn`-compatible]: https://doc.rust-lang.org/stable/reference/items/traits.html#dyn-compatibility
|
||||
pub trait DynTemplate {
|
||||
/// Helper method which allocates a new `String` and renders into it
|
||||
/// Helper method which allocates a new `String` and renders into it.
|
||||
#[cfg(feature = "alloc")]
|
||||
fn dyn_render(&self) -> Result<String>;
|
||||
|
||||
/// Renders the template to the given `writer` fmt buffer
|
||||
/// Helper method which allocates a new `String` and renders into it with provided [`Values`].
|
||||
#[cfg(feature = "alloc")]
|
||||
fn dyn_render_with_values(&self, values: &dyn Values) -> Result<String>;
|
||||
|
||||
/// Renders the template to the given `writer` fmt buffer.
|
||||
fn dyn_render_into(&self, writer: &mut dyn fmt::Write) -> Result<()>;
|
||||
|
||||
/// Renders the template to the given `writer` io buffer
|
||||
/// Renders the template to the given `writer` fmt buffer with provided [`Values`].
|
||||
fn dyn_render_into_with_values(
|
||||
&self,
|
||||
writer: &mut dyn fmt::Write,
|
||||
values: &dyn Values,
|
||||
) -> Result<()>;
|
||||
|
||||
/// Renders the template to the given `writer` io buffer.
|
||||
#[cfg(feature = "std")]
|
||||
fn dyn_write_into(&self, writer: &mut dyn io::Write) -> io::Result<()>;
|
||||
|
||||
/// Provides a conservative estimate of the expanded length of the rendered template
|
||||
/// Renders the template to the given `writer` io buffer with provided [`Values`].
|
||||
#[cfg(feature = "std")]
|
||||
fn dyn_write_into_with_values(
|
||||
&self,
|
||||
writer: &mut dyn io::Write,
|
||||
values: &dyn Values,
|
||||
) -> io::Result<()>;
|
||||
|
||||
/// Provides a conservative estimate of the expanded length of the rendered template.
|
||||
fn size_hint(&self) -> usize;
|
||||
}
|
||||
|
||||
@ -208,17 +283,39 @@ impl<T: Template> DynTemplate for T {
|
||||
<Self as Template>::render(self)
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
#[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
|
||||
@ -287,7 +384,11 @@ mod tests {
|
||||
struct Test;
|
||||
|
||||
impl Template for Test {
|
||||
fn render_into<W: fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()> {
|
||||
fn render_into_with_values<W: fmt::Write + ?Sized>(
|
||||
&self,
|
||||
writer: &mut W,
|
||||
_values: &dyn Values,
|
||||
) -> Result<()> {
|
||||
Ok(writer.write_str("test")?)
|
||||
}
|
||||
|
||||
|
200
rinja/src/values.rs
Normal file
200
rinja/src/values.rs
Normal file
@ -0,0 +1,200 @@
|
||||
use core::any::Any;
|
||||
use core::borrow::Borrow;
|
||||
|
||||
use crate::Error;
|
||||
|
||||
/// No runtime values provided.
|
||||
pub const NO_VALUES: &dyn Values = &();
|
||||
|
||||
/// Try to find `key` in `values` and then to convert it to `T`.
|
||||
pub fn get_value<T: Any>(values: &dyn Values, key: impl AsRef<str>) -> Result<&T, Error> {
|
||||
let Some(src) = values.get_value(key.as_ref()) else {
|
||||
return Err(Error::ValueMissing);
|
||||
};
|
||||
|
||||
if let Some(value) = src.downcast_ref::<T>() {
|
||||
return Ok(value);
|
||||
} else if let Some(value) = src.downcast_ref::<&T>() {
|
||||
return Ok(value);
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
if let Some(value) = src.downcast_ref::<alloc::boxed::Box<T>>() {
|
||||
return Ok(value);
|
||||
} else if let Some(value) = src.downcast_ref::<alloc::rc::Rc<T>>() {
|
||||
return Ok(value);
|
||||
} else if let Some(value) = src.downcast_ref::<alloc::sync::Arc<T>>() {
|
||||
return Ok(value);
|
||||
}
|
||||
|
||||
Err(Error::ValueType)
|
||||
}
|
||||
|
||||
/// A runtime value store for [`Template::render_with_values()`][crate::Template::render_with_values].
|
||||
pub trait Values {
|
||||
/// Try to find `key` in this store.
|
||||
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any>;
|
||||
}
|
||||
|
||||
crate::impl_for_ref! {
|
||||
impl Values for T {
|
||||
#[inline]
|
||||
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||
T::get_value(self, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Values for () {
|
||||
#[inline]
|
||||
fn get_value<'a>(&'a self, _: &str) -> Option<&'a dyn Any> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Values> Values for Option<T> {
|
||||
#[inline]
|
||||
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||
self.as_ref()?.get_value(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, const N: usize> Values for [(K, V); N]
|
||||
where
|
||||
K: Borrow<str>,
|
||||
V: Value,
|
||||
{
|
||||
#[inline]
|
||||
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||
self.as_slice().get_value(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Values for [(K, V)]
|
||||
where
|
||||
K: Borrow<str>,
|
||||
V: Value,
|
||||
{
|
||||
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||
for (k, v) in self {
|
||||
if k.borrow() == key {
|
||||
return v.ref_any();
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<K, V> Values for alloc::collections::BTreeMap<K, V>
|
||||
where
|
||||
K: Borrow<str> + core::cmp::Ord,
|
||||
V: Value,
|
||||
{
|
||||
#[inline]
|
||||
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||
self.get(key)?.ref_any()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<K, V, S> Values for std::collections::HashMap<K, V, S>
|
||||
where
|
||||
K: Borrow<str> + Eq + core::hash::Hash,
|
||||
V: Value,
|
||||
S: core::hash::BuildHasher,
|
||||
{
|
||||
#[inline]
|
||||
fn get_value<'a>(&'a self, key: &str) -> Option<&'a dyn Any> {
|
||||
self.get(key)?.ref_any()
|
||||
}
|
||||
}
|
||||
|
||||
/// A value in a [`Values`] collection.
|
||||
///
|
||||
/// This is <code>[dyn](https://doc.rust-lang.org/stable/std/keyword.dyn.html) [Any]</code>,
|
||||
/// <code>[Option]<dyn Any></code>, or a reference to either.
|
||||
pub trait Value {
|
||||
/// Returns a reference to this value unless it is `None`.
|
||||
fn ref_any(&self) -> Option<&dyn Any>;
|
||||
}
|
||||
|
||||
crate::impl_for_ref! {
|
||||
impl Value for T {
|
||||
#[inline]
|
||||
fn ref_any(&self) -> Option<&dyn Any> {
|
||||
T::ref_any(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value for dyn Any {
|
||||
#[inline]
|
||||
fn ref_any(&self) -> Option<&dyn Any> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Value> Value for Option<T> {
|
||||
#[inline]
|
||||
fn ref_any(&self) -> Option<&dyn Any> {
|
||||
T::ref_any(self.as_ref()?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[test]
|
||||
fn values_on_hashmap() {
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use std::collections::HashMap;
|
||||
|
||||
let mut values: HashMap<String, Box<dyn Any>> = HashMap::new();
|
||||
values.insert("a".into(), Box::new(10u32));
|
||||
values.insert("c".into(), Box::new("blam"));
|
||||
assert_matches!(get_value::<u32>(&values, "a"), Ok(&10u32));
|
||||
assert_matches!(get_value::<&str>(&values, "c"), Ok(&"blam"));
|
||||
assert_matches!(get_value::<u8>(&values, "a"), Err(Error::ValueType));
|
||||
assert_matches!(get_value::<u8>(&values, "d"), Err(Error::ValueMissing));
|
||||
|
||||
let mut values: HashMap<&str, Box<dyn Any>> = HashMap::new();
|
||||
values.insert("a", Box::new(10u32));
|
||||
assert_matches!(get_value::<u32>(&values, "a"), Ok(&10u32));
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[test]
|
||||
fn values_on_btreemap() {
|
||||
use alloc::boxed::Box;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::String;
|
||||
|
||||
let mut values: BTreeMap<String, Box<dyn Any>> = BTreeMap::new();
|
||||
values.insert("a".into(), Box::new(10u32));
|
||||
values.insert("c".into(), Box::new("blam"));
|
||||
|
||||
assert_matches!(get_value::<u32>(&values, "a"), Ok(&10u32));
|
||||
assert_matches!(get_value::<&str>(&values, "c"), Ok(&"blam"));
|
||||
assert_matches!(get_value::<u8>(&values, "a"), Err(Error::ValueType));
|
||||
assert_matches!(get_value::<u8>(&values, "d"), Err(Error::ValueMissing));
|
||||
|
||||
let mut values: BTreeMap<&str, Box<dyn Any>> = BTreeMap::new();
|
||||
values.insert("a", Box::new(10u32));
|
||||
assert_matches!(get_value::<u32>(&values, "a"), Ok(&10u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn values_on_slice() {
|
||||
let values: &[(&str, &dyn Any)] = &[("a", &12u32), ("c", &"blam")];
|
||||
assert_matches!(get_value::<u32>(&values, "a"), Ok(12u32));
|
||||
assert_matches!(get_value::<&str>(&values, "c"), Ok(&"blam"));
|
||||
assert_matches!(get_value::<u8>(&values, "a"), Err(Error::ValueType));
|
||||
assert_matches!(get_value::<u8>(&values, "d"), Err(Error::ValueMissing));
|
||||
}
|
||||
}
|
@ -124,12 +124,16 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
) -> Result<usize, CompileError> {
|
||||
write_header(self.input.ast, buf, target);
|
||||
buf.write(
|
||||
"fn render_into<RinjaW>(&self, __rinja_writer: &mut RinjaW) -> rinja::Result<()>\
|
||||
"fn render_into_with_values<RinjaW>(\
|
||||
&self,\
|
||||
__rinja_writer: &mut RinjaW,\
|
||||
__rinja_values: &dyn rinja::Values\
|
||||
) -> rinja::Result<()>\
|
||||
where \
|
||||
RinjaW: rinja::helpers::core::fmt::Write + ?rinja::helpers::core::marker::Sized\
|
||||
{\
|
||||
use rinja::filters::{AutoEscape as _, WriteWritable as _};\
|
||||
use rinja::helpers::ResultConverter as _;
|
||||
use rinja::helpers::ResultConverter as _;\
|
||||
use rinja::helpers::core::fmt::Write as _;",
|
||||
);
|
||||
|
||||
|
@ -264,6 +264,7 @@ impl<'a> Generator<'a, '_> {
|
||||
"safe" => Self::_visit_safe_filter,
|
||||
"urlencode" => Self::_visit_urlencode_filter,
|
||||
"urlencode_strict" => Self::_visit_urlencode_strict_filter,
|
||||
"value" => return self._visit_value(ctx, buf, args, generics, node, "`value` filter"),
|
||||
name if BUILTIN_FILTERS.contains(&name) => {
|
||||
return self._visit_builtin_filter(ctx, buf, name, args, generics, node);
|
||||
}
|
||||
@ -676,6 +677,37 @@ impl<'a> Generator<'a, '_> {
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn _visit_value(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
args: &[WithSpan<'_, Expr<'a>>],
|
||||
generics: &[WithSpan<'_, TyGenerics<'_>>],
|
||||
node: Span<'_>,
|
||||
kind: &str,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
let [key] = args else {
|
||||
return Err(ctx.generate_error(
|
||||
format_args!("{kind} only takes one argument, found {}", args.len()),
|
||||
node,
|
||||
));
|
||||
};
|
||||
let [gen] = generics else {
|
||||
return Err(ctx.generate_error(
|
||||
format_args!("{kind} expects one generic, found {}", generics.len()),
|
||||
node,
|
||||
));
|
||||
};
|
||||
buf.write("rinja::helpers::get_value");
|
||||
buf.write("::<");
|
||||
self.visit_ty_generic(buf, gen);
|
||||
buf.write('>');
|
||||
buf.write("(&__rinja_values, &(");
|
||||
self._visit_arg(ctx, buf, key)?;
|
||||
buf.write("))");
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn _visit_args(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
@ -893,6 +925,17 @@ impl<'a> Generator<'a, '_> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// We special-case "rinja::get_value".
|
||||
Expr::Path(path) if path == &["rinja", "get_value"] => {
|
||||
self._visit_value(
|
||||
ctx,
|
||||
buf,
|
||||
args,
|
||||
generics,
|
||||
left.span(),
|
||||
"`get_value` function",
|
||||
)?;
|
||||
}
|
||||
sub_left => {
|
||||
match sub_left {
|
||||
Expr::Var(name) => match self.locals.resolve(name) {
|
||||
|
@ -1168,7 +1168,8 @@ impl<'a> Generator<'a, '_> {
|
||||
idx
|
||||
};
|
||||
lines.write(format_args!(
|
||||
"(&&rinja::filters::Writable(expr{idx})).rinja_write(__rinja_writer)?;",
|
||||
"(&&&rinja::filters::Writable(expr{idx})).\
|
||||
rinja_write(__rinja_writer, __rinja_values)?;",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,11 @@ fn compare(jinja: &str, expected: &str, fields: &[(&str, &str)], size_hint: usiz
|
||||
extern crate rinja as rinja;
|
||||
|
||||
impl rinja::Template for Foo {
|
||||
fn render_into<RinjaW>(&self, __rinja_writer: &mut RinjaW) -> rinja::Result<()>
|
||||
fn render_into_with_values<RinjaW>(
|
||||
&self,
|
||||
__rinja_writer: &mut RinjaW,
|
||||
__rinja_values: &dyn rinja::Values,
|
||||
) -> rinja::Result<()>
|
||||
where
|
||||
RinjaW: rinja::helpers::core::fmt::Write + ?rinja::helpers::core::marker::Sized,
|
||||
{
|
||||
@ -153,7 +157,7 @@ fn check_if_let() {
|
||||
&((&&rinja::filters::AutoEscaper::new(&(query), rinja::filters::Text)).rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
}",
|
||||
@ -170,7 +174,7 @@ fn check_if_let() {
|
||||
&((&&rinja::filters::AutoEscaper::new(&(s), rinja::filters::Text)).rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
}",
|
||||
@ -187,7 +191,7 @@ fn check_if_let() {
|
||||
&((&&rinja::filters::AutoEscaper::new(&(s), rinja::filters::Text)).rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
}",
|
||||
@ -211,9 +215,9 @@ fn check_if_let_chain() {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0, expr2) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
__rinja_writer.write_str(" ")?;
|
||||
(&&rinja::filters::Writable(expr2)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr2)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
}"#,
|
||||
@ -236,9 +240,9 @@ fn check_if_let_chain() {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0, expr2) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
__rinja_writer.write_str(" ")?;
|
||||
(&&rinja::filters::Writable(expr2)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr2)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
}"#,
|
||||
@ -265,9 +269,9 @@ fn check_if_let_chain() {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0, expr2) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
__rinja_writer.write_str(" ")?;
|
||||
(&&rinja::filters::Writable(expr2)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr2)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
}"#,
|
||||
@ -292,9 +296,9 @@ fn check_if_let_chain() {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0, expr2) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
__rinja_writer.write_str(" ")?;
|
||||
(&&rinja::filters::Writable(expr2)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr2)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
}"#,
|
||||
@ -429,7 +433,7 @@ __rinja_writer.write_str("12")?;
|
||||
&((&&rinja::filters::AutoEscaper::new(&(self.x), rinja::filters::Text)).rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -443,7 +447,7 @@ __rinja_writer.write_str("12")?;
|
||||
&((&&rinja::filters::AutoEscaper::new(&(self.x), rinja::filters::Text)).rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
",
|
||||
@ -474,7 +478,7 @@ if rinja::helpers::as_bool(&(self.y == 12)) {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -496,7 +500,7 @@ match (
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
",
|
||||
@ -616,7 +620,7 @@ fn check_bool_conditions() {
|
||||
&((&&rinja::filters::AutoEscaper::new(&(self.x), rinja::filters::Text)).rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
",
|
||||
@ -634,7 +638,7 @@ fn check_bool_conditions() {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -658,7 +662,7 @@ fn check_bool_conditions() {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -678,7 +682,7 @@ fn check_bool_conditions() {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
",
|
||||
@ -699,7 +703,7 @@ if rinja::helpers::as_bool(&(self.y == 3))
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -899,9 +903,9 @@ fn test_pluralize() {
|
||||
)?),
|
||||
) {
|
||||
(expr0, expr3) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
__rinja_writer.write_str(" dog")?;
|
||||
(&&rinja::filters::Writable(expr3)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr3)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}"#,
|
||||
&[("dogs", "i8")],
|
||||
@ -923,9 +927,9 @@ fn test_pluralize() {
|
||||
)?),
|
||||
) {
|
||||
(expr0, expr3) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
__rinja_writer.write_str(" dog")?;
|
||||
(&&rinja::filters::Writable(expr3)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr3)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}"#,
|
||||
&[("dogs", "i8")],
|
||||
@ -947,9 +951,9 @@ fn test_pluralize() {
|
||||
)?),
|
||||
) {
|
||||
(expr0, expr2) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
__rinja_writer.write_str(" ")?;
|
||||
(&&rinja::filters::Writable(expr2)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr2)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}"#,
|
||||
&[("dogs", "i8")],
|
||||
@ -975,7 +979,7 @@ fn test_pluralize() {
|
||||
)?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
",
|
||||
@ -991,7 +995,7 @@ fn test_pluralize() {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
",
|
||||
@ -1006,7 +1010,7 @@ fn test_pluralize() {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
",
|
||||
@ -1019,7 +1023,7 @@ fn test_pluralize() {
|
||||
r#"
|
||||
match (&(rinja::filters::Safe("pl")),) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
@ -1031,7 +1035,7 @@ fn test_pluralize() {
|
||||
r#"
|
||||
match (&(rinja::filters::Safe("sg")),) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
@ -1044,7 +1048,7 @@ fn test_pluralize() {
|
||||
r#"
|
||||
match (&(rinja::filters::Safe("s")),) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
@ -1056,7 +1060,7 @@ fn test_pluralize() {
|
||||
r"
|
||||
match (&(rinja::helpers::Empty),) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
",
|
||||
@ -1078,9 +1082,9 @@ fn test_concat() {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr1, expr3) => {
|
||||
(&&rinja::filters::Writable(expr1)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr1)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
__rinja_writer.write_str("|")?;
|
||||
(&&rinja::filters::Writable(expr3)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr3)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
__rinja_writer.write_str(">")?;
|
||||
@ -1105,7 +1109,7 @@ fn test_concat() {
|
||||
.rinja_auto_escape()?),
|
||||
) {
|
||||
(expr0,) => {
|
||||
(&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer)?;
|
||||
(&&&rinja::filters::Writable(expr0)).rinja_write(__rinja_writer, __rinja_values)?;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
45
testing/tests/ui/values.rs
Normal file
45
testing/tests/ui/values.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use rinja::Template;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
ext = "html",
|
||||
source = r#"{% if let Ok(x) = "a"|value %}{% endif %}"#,
|
||||
)]
|
||||
struct A;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
ext = "html",
|
||||
source = r#"{% if let Ok(x) = "a"|value::<u8, u8> %}{% endif %}"#,
|
||||
)]
|
||||
struct B;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
ext = "html",
|
||||
source = r#"{% if let Ok(x) = rinja::get_value("a") %}{% endif %}"#,
|
||||
)]
|
||||
struct C;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
ext = "html",
|
||||
source = r#"{% if let Ok(x) = rinja::get_value::<u8, u8>("a") %}{% endif %}"#,
|
||||
)]
|
||||
struct D;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
ext = "html",
|
||||
source = r#"{% if let Ok(x) = rinja::get_value::<u8>() %}{% endif %}"#,
|
||||
)]
|
||||
struct E;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
ext = "html",
|
||||
source = r#"{% if let Ok(x) = rinja::get_value::<u8>("a", "b") %}{% endif %}"#,
|
||||
)]
|
||||
struct F;
|
||||
|
||||
fn main() {}
|
47
testing/tests/ui/values.stderr
Normal file
47
testing/tests/ui/values.stderr
Normal file
@ -0,0 +1,47 @@
|
||||
error: `value` filter expects one generic, found 0
|
||||
--> A.html:1:18
|
||||
"\"a\"|value %}{% endif %}"
|
||||
--> tests/ui/values.rs:6:14
|
||||
|
|
||||
6 | source = r#"{% if let Ok(x) = "a"|value %}{% endif %}"#,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `value` filter expects one generic, found 2
|
||||
--> B.html:1:18
|
||||
"\"a\"|value::<u8, u8> %}{% endif %}"
|
||||
--> tests/ui/values.rs:13:14
|
||||
|
|
||||
13 | source = r#"{% if let Ok(x) = "a"|value::<u8, u8> %}{% endif %}"#,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `get_value` function expects one generic, found 0
|
||||
--> C.html:1:18
|
||||
"rinja::get_value(\"a\") %}{% endif %}"
|
||||
--> tests/ui/values.rs:20:14
|
||||
|
|
||||
20 | source = r#"{% if let Ok(x) = rinja::get_value("a") %}{% endif %}"#,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `get_value` function expects one generic, found 2
|
||||
--> D.html:1:18
|
||||
"rinja::get_value::<u8, u8>(\"a\") %}{% endif %}"
|
||||
--> tests/ui/values.rs:27:14
|
||||
|
|
||||
27 | source = r#"{% if let Ok(x) = rinja::get_value::<u8, u8>("a") %}{% endif %}"#,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `get_value` function only takes one argument, found 0
|
||||
--> E.html:1:18
|
||||
"rinja::get_value::<u8>() %}{% endif %}"
|
||||
--> tests/ui/values.rs:34:14
|
||||
|
|
||||
34 | source = r#"{% if let Ok(x) = rinja::get_value::<u8>() %}{% endif %}"#,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `get_value` function only takes one argument, found 2
|
||||
--> F.html:1:18
|
||||
"rinja::get_value::<u8>(\"a\", \"b\") %}{% endif %}"
|
||||
--> tests/ui/values.rs:41:14
|
||||
|
|
||||
41 | source = r#"{% if let Ok(x) = rinja::get_value::<u8>("a", "b") %}{% endif %}"#,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
90
testing/tests/values.rs
Normal file
90
testing/tests/values.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use rinja::Template;
|
||||
|
||||
#[test]
|
||||
fn test_values() {
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
source = r#"{% if let Ok(bla) = "a" | value::<u32> %}{{bla}}{% endif %}"#,
|
||||
ext = "txt"
|
||||
)]
|
||||
struct V;
|
||||
|
||||
let mut values: HashMap<String, Box<dyn Any>> = HashMap::default();
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), "");
|
||||
values.insert("a".to_string(), Box::new(12u32));
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), "12");
|
||||
values.insert("a".to_string(), Box::new(false));
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_values2() {
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
source = r#"
|
||||
{%- if let Ok(bla) = "a" | value::<&str> %}{{bla}}{% endif -%}
|
||||
{%- if let Ok(bla) = "b" | value::<bool> %} {{bla}}{% endif -%}
|
||||
"#,
|
||||
ext = "txt"
|
||||
)]
|
||||
struct V;
|
||||
|
||||
let mut values: HashMap<String, Box<dyn Any>> = HashMap::default();
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), "");
|
||||
values.insert("a".to_string(), Box::new("hey"));
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), "hey");
|
||||
values.insert("b".to_string(), Box::new(false));
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), "hey false");
|
||||
values.remove("a");
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), " false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_values3() {
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
source = r#"
|
||||
{%- match "data" | value::<&str> -%}
|
||||
{%- when Ok(data) -%} ok={{ data }}
|
||||
{%- when Err(err) -%} err={{ err }}
|
||||
{%- endmatch -%}
|
||||
"#,
|
||||
ext = "txt"
|
||||
)]
|
||||
struct V;
|
||||
|
||||
let mut values: HashMap<String, Box<dyn Any>> = HashMap::default();
|
||||
assert_eq!(
|
||||
V.render_with_values(&values).unwrap(),
|
||||
"err=key missing in values"
|
||||
);
|
||||
values.insert("data".to_string(), Box::new(false));
|
||||
assert_eq!(
|
||||
V.render_with_values(&values).unwrap(),
|
||||
"err=value has wrong type"
|
||||
);
|
||||
values.insert("data".to_string(), Box::new("hey"));
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), "ok=hey");
|
||||
values.insert("data".to_string(), Box::new(Box::new("hey")));
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), "ok=hey");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_function_getter() {
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
source = r#"{% if let Ok(bla) = rinja::get_value::<u32>("a") %}{{bla}}{% endif %}"#,
|
||||
ext = "txt"
|
||||
)]
|
||||
struct V;
|
||||
|
||||
let mut values: HashMap<String, Box<dyn Any>> = HashMap::default();
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), "");
|
||||
values.insert("a".to_string(), Box::new(12u32));
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), "12");
|
||||
values.insert("a".to_string(), Box::new(false));
|
||||
assert_eq!(V.render_with_values(&values).unwrap(), "");
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user