Expand description
Provides a dynamic value type abstraction.
This module gives access to a dynamically typed value which is used by the template engine during execution.
For the most part the existence of the value type can be ignored as
MiniJinja will perform the necessary conversions for you. For instance
if you write a filter that converts a string you can directly declare the
filter to take a String
. However for some more advanced use cases it’s
useful to know that this type exists.
§Basic Value Conversions
Values are typically created via the From
trait:
use std::collections::BTreeMap;
let int_value = Value::from(42);
let none_value = Value::from(());
let true_value = Value::from(true);
let map = Value::from({
let mut m = BTreeMap::new();
m.insert("foo", 1);
m.insert("bar", 2);
m
});
Or via the FromIterator
trait which can create sequences or maps. When
given a tuple it creates maps, otherwise it makes a sequence.
// collection into a sequence
let value: Value = (1..10).into_iter().collect();
// collection into a map
let value: Value = [("key", "value")].into_iter().collect();
For certain types of iterators (Send
+ Sync
+ 'static
) it’s also
possible to make the value lazily iterate over the value by using the
Value::make_iterable
function instead. Whenever the value requires
iteration, the function is called to create that iterator.
let value: Value = Value::make_iterable(|| 1..10);
To to into the inverse directly the various TryFrom
implementations can be used:
use std::convert::TryFrom;
let v = u64::try_from(Value::from(42)).unwrap();
The special Undefined
value also exists but does not
have a rust equivalent. It can be created via the UNDEFINED
constant.
§Collections
The standard library’s collection types such as
HashMap
, Vec
and various others from the
collections module are implemented are objects. There is a cavet here which is
that maps can only have string or Value
as key. The values in the collections
are lazily converted into value when accessed or iterated over. These types can
be constructed either from Value::from
or Value::from_object
. Because the
types are boxed unchanged, you can also downcast them.
let vec = Value::from(vec![1i32, 2, 3, 4]);
let vec_ref = vec.downcast_object_ref::<Vec<i32>>().unwrap();
assert_eq!(vec_ref, &vec![1, 2, 3, 4]);
Caveat: for convenience reasons maps with &str
keys can be stored. The keys
however are converted into Arc<str>
.
§Serde Conversions
MiniJinja will usually however create values via an indirection via serde
when
a template is rendered or an expression is evaluated. This can also be
triggered manually by using the Value::from_serialize
method:
let value = Value::from_serialize(&[1, 2, 3]);
The inverse of that operation is to pass a value directly as serializer to
a type that supports deserialization. This requires the deserialization
feature.
use serde::Deserialize;
let value = Value::from(vec![1, 2, 3]);
let vec = Vec::<i32>::deserialize(value).unwrap();
§Value Function Arguments
Filters and tests can take values as arguments
but optionally also rust types directly. This conversion for function arguments
is performed by the FunctionArgs
and related traits (ArgType
, FunctionResult
).
§Memory Management
Values are immutable objects which are internally reference counted which means they can be copied relatively cheaply. Special care must be taken so that cycles are not created to avoid causing memory leaks.
§HTML Escaping
MiniJinja inherits the general desire to be clever about escaping. For this
purpose a value will (when auto escaping is enabled) always be escaped. To
prevent this behavior the safe
filter can be used
in the template. Outside of templates the Value::from_safe_string
method
can be used to achieve the same result.
§Dynamic Objects
Values can also hold “dynamic” objects. These are objects which implement the
Object
trait. These can be used to implement dynamic functionality such
as stateful values and more. Dynamic objects are internally also used to
implement the special loop
variable, macros and similar things.
To create a Value
from a dynamic object use Value::from_object
,
Value::from_dyn_object
:
#[derive(Debug)]
struct Foo;
impl Object for Foo {
/* implementation */
}
let value = Value::from_object(Foo);
let value = Value::from_dyn_object(Arc::new(Foo));
§Invalid Values
MiniJinja knows the concept of an “invalid value”. These are rare in practice
and should not be used, but they are needed in some situations. An invalid value
looks like a value but working with that value in the context of the engine will
fail in most situations. In principle an invalid value is a value that holds an
error internally. It’s created with From
:
use minijinja::{Value, Error, ErrorKind};
let error = Error::new(ErrorKind::InvalidOperation, "failed to generate an item");
let invalid_value = Value::from(error);
Invalid values are typically encountered in the following situations:
- serialization fails with an error: this is the case when a value is crated
via
Value::from_serialize
and the underlyingSerialize
implementation fails with an error. - fallible iteration: there might be situations where an iterator cannot indicate failure ahead of iteration and must abort. In that case the only option an iterator in MiniJinja has is to create an invalid value.
It’s generally recommende to ignore the existence of invalid objects and let them fail naturally as they are encountered.
§Notes on Bytes and Strings
Usually one would pass strings to templates as Jinja is entirely based on string rendering. However there are situations where it can be useful to pass bytes instead. As such MiniJinja allows a value type to carry bytes even though there is no syntax within the template language to create a byte literal.
When rendering bytes as strings, MiniJinja will attempt to interpret them as lossy utf-8. This is a bit different to Jinja2 which in Python 3 stopped rendering byte strings as strings. This is an intentional change that was deemed acceptable given how infrequently bytes are used but how relatively commonly bytes are often holding “almost utf-8” in templates. Most conversions to strings also will do almost the same. The debug rendering of bytes however is different and bytes are not iterable. Like strings however they can be sliced and indexed, but they will be sliced by bytes and not by characters.
Structs§
- Type-erased version of
Object
- Utility to accept keyword arguments.
- Utility type to capture remaining arguments.
- Represents a dynamically typed value in the template engine.
- Utility to iterate over values.
- Utility type to deserialize an argument.
Enums§
- Enumerators help define iteration behavior for
Object
s. - Defines the natural representation of this object.
- Describes the kind of value.
Traits§
- A trait implemented by all filter/test argument types.
- Helper trait representing valid filter, test and function arguments.
- A utility trait that represents the return value of functions and filters.
- A trait that represents a dynamic object.
- Provides utility methods for working with objects.
Functions§
- Utility function to convert a slice of values into arguments.
- Intern a string.
- Function that returns true when serialization for
Value
is taking place.