Trait minijinja::value::Object

source ·
pub trait Object: Debug + Send + Sync {
    // Provided methods
    fn repr(self: &Arc<Self>) -> ObjectRepr { ... }
    fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> { ... }
    fn enumerate(self: &Arc<Self>) -> Enumerator { ... }
    fn enumerator_len(self: &Arc<Self>) -> Option<usize> { ... }
    fn is_true(self: &Arc<Self>) -> bool { ... }
    fn call(
        self: &Arc<Self>,
        state: &State<'_, '_>,
        args: &[Value]
    ) -> Result<Value, Error> { ... }
    fn call_method(
        self: &Arc<Self>,
        state: &State<'_, '_>,
        method: &str,
        args: &[Value]
    ) -> Result<Value, Error> { ... }
    fn render(self: &Arc<Self>, f: &mut Formatter<'_>) -> Result
       where Self: Sized + 'static { ... }
}
Expand description

A trait that represents a dynamic object.

There is a type erased wrapper of this trait available called DynObject which is what the engine actually holds internally.

§Basic Struct

The following example shows how to implement a dynamic object which represents a struct. All that’s needed is to implement get_value to look up a field by name as well as enumerate to return an enumerator over the known keys. The repr defaults to Map so nothing needs to be done here.

use std::sync::Arc;
use minijinja::value::{Value, Object, Enumerator};

#[derive(Debug)]
struct Point(f32, f32, f32);

impl Object for Point {
    fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
        match key.as_str()? {
            "x" => Some(Value::from(self.0)),
            "y" => Some(Value::from(self.1)),
            "z" => Some(Value::from(self.2)),
            _ => None,
        }
    }

    fn enumerate(self: &Arc<Self>) -> Enumerator {
        Enumerator::Str(&["x", "y", "z"])
    }
}

let value = Value::from_object(Point(1.0, 2.5, 3.0));

§Basic Sequence

The following example shows how to implement a dynamic object which represents a sequence. All that’s needed is to implement repr to indicate that this is a sequence, get_value to look up a field by index, and enumerate to return a sequential enumerator. This enumerator will automatically call get_value from 0..length.

use std::sync::Arc;
use minijinja::value::{Value, Object, ObjectRepr, Enumerator};

#[derive(Debug)]
struct Point(f32, f32, f32);

impl Object for Point {
    fn repr(self: &Arc<Self>) -> ObjectRepr {
        ObjectRepr::Seq
    }

    fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
        match key.as_usize()? {
            0 => Some(Value::from(self.0)),
            1 => Some(Value::from(self.1)),
            2 => Some(Value::from(self.2)),
            _ => None,
        }
    }

    fn enumerate(self: &Arc<Self>) -> Enumerator {
        Enumerator::Seq(3)
    }
}

let value = Value::from_object(Point(1.0, 2.5, 3.0));

§Iterables

If you have something that is not quite a sequence but is capable of yielding values over time, you can directly implement an iterable. This is somewhat uncommon as you can normally directly use Value::make_iterable. Here is how this can be done though:

use std::sync::Arc;
use minijinja::value::{Value, Object, ObjectRepr, Enumerator};

#[derive(Debug)]
struct Range10;

impl Object for Range10 {
    fn repr(self: &Arc<Self>) -> ObjectRepr {
        ObjectRepr::Iterable
    }

    fn enumerate(self: &Arc<Self>) -> Enumerator {
        Enumerator::Iter(Box::new((1..10).map(Value::from)))
    }
}

let value = Value::from_object(Range10);

Iteration is encouraged to fail immediately (object is not iterable) or not at all. However this is not always possible, but the iteration interface itself does not support fallible iteration. It is however possible to accomplish the same thing by creating an invalid value.

§Map As Context

Map can also be used as template rendering context. This has a lot of benefits as it means that the serialization overhead can be largely to completely avoided. This means that even if templates take hundreds of values, MiniJinja does not spend time eagerly converting them into values.

Here is a very basic example of how a template can be rendered with a dynamic context. Note that the implementation of enumerate is optional for this to work. It’s in fact not used by the engine during rendering but it is necessary for the debug() function to be able to show which values exist in the context.

use std::sync::Arc;
use minijinja::value::{Value, Object};

#[derive(Debug)]
pub struct DynamicContext {
    magic: i32,
}

impl Object for DynamicContext {
    fn get_value(self: &Arc<Self>, field: &Value) -> Option<Value> {
        match field.as_str()? {
            "pid" => Some(Value::from(std::process::id())),
            "env" => Some(Value::from_iter(std::env::vars())),
            "magic" => Some(Value::from(self.magic)),
            _ => None,
        }
    }
}

let tmpl = env.template_from_str("HOME={{ env.HOME }}; PID={{ pid }}; MAGIC={{ magic }}")?;
let ctx = Value::from_object(DynamicContext { magic: 42 });
let rv = tmpl.render(ctx)?;

One thing of note here is that in the above example env would be re-created every time the template needs it. A better implementation would cache the value after it was created first.

Provided Methods§

source

fn repr(self: &Arc<Self>) -> ObjectRepr

Indicates the natural representation of an object.

The default implementation returns ObjectRepr::Map.

source

fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value>

Given a key, looks up the associated value.

source

fn enumerate(self: &Arc<Self>) -> Enumerator

Enumerates the object.

The engine uses the returned enumerator to implement iteration and the size information of an object. For more information see Enumerator. The default implementation returns Empty for all object representations other than ObjectRepr::Plain which default to NonEnumerable.

When wrapping other objects you might want to consider using ObjectExt::mapped_enumerator and ObjectExt::mapped_rev_enumerator.

source

fn enumerator_len(self: &Arc<Self>) -> Option<usize>

Returns the length of the enumerator.

By default the length is taken by calling enumerate and inspecting the Enumerator. This means that in order to determine the length, an iteration is started. If you this is a problem for your uses, you can manually implement this. This might for instance be needed if your type can only be iterated over once.

source

fn is_true(self: &Arc<Self>) -> bool

Returns true if this object is considered true for if conditions.

The default implementation checks if the enumerator_len is not Some(0) which is the recommended behavior for objects.

source

fn call( self: &Arc<Self>, state: &State<'_, '_>, args: &[Value] ) -> Result<Value, Error>

The engine calls this to invoke the object itself.

The default implementation returns an InvalidOperation error.

source

fn call_method( self: &Arc<Self>, state: &State<'_, '_>, method: &str, args: &[Value] ) -> Result<Value, Error>

The engine calls this to invoke a method on the object.

The default implementation returns an UnknownMethod error. When this error is returned the engine will invoke the unknown_method_callback of the environment.

source

fn render(self: &Arc<Self>, f: &mut Formatter<'_>) -> Result
where Self: Sized + 'static,

Formats the object for stringification.

The default implementation is specific to the behavior of repr and usually does not need modification.

Object Safety§

This trait is not object safe.

Implementations on Foreign Types§

source§

impl<T> Object for BTreeSet<T>
where T: Into<Value> + Clone + Send + Sync + Debug + 'static,

source§

fn repr(self: &Arc<Self>) -> ObjectRepr

source§

fn enumerate(self: &Arc<Self>) -> Enumerator

source§

impl<T> Object for LinkedList<T>
where T: Into<Value> + Clone + Send + Sync + Debug + 'static,

source§

fn repr(self: &Arc<Self>) -> ObjectRepr

source§

fn enumerate(self: &Arc<Self>) -> Enumerator

source§

impl<T> Object for VecDeque<T>
where T: Into<Value> + Clone + Send + Sync + Debug + 'static,

source§

fn repr(self: &Arc<Self>) -> ObjectRepr

source§

fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value>

source§

fn enumerate(self: &Arc<Self>) -> Enumerator

source§

impl<T> Object for Vec<T>
where T: Into<Value> + Clone + Send + Sync + Debug + 'static,

source§

fn repr(self: &Arc<Self>) -> ObjectRepr

source§

fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value>

source§

fn enumerate(self: &Arc<Self>) -> Enumerator

source§

impl<T> Object for HashSet<T>
where T: Into<Value> + Clone + Send + Sync + Debug + 'static,

source§

fn repr(self: &Arc<Self>) -> ObjectRepr

source§

fn enumerate(self: &Arc<Self>) -> Enumerator

source§

impl<V> Object for BTreeMap<String, V>
where V: Into<Value> + Clone + Send + Sync + Debug + 'static,

source§

fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value>

source§

fn enumerate(self: &Arc<Self>) -> Enumerator

source§

impl<V> Object for BTreeMap<Arc<str>, V>
where V: Into<Value> + Clone + Send + Sync + Debug + 'static,

source§

fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value>

source§

fn enumerate(self: &Arc<Self>) -> Enumerator

source§

impl<V> Object for BTreeMap<Value, V>
where V: Into<Value> + Clone + Send + Sync + Debug + 'static,

source§

fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value>

source§

fn enumerate(self: &Arc<Self>) -> Enumerator

source§

impl<V> Object for HashMap<String, V>
where V: Into<Value> + Clone + Send + Sync + Debug + 'static,

source§

fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value>

source§

fn enumerate(self: &Arc<Self>) -> Enumerator

source§

impl<V> Object for HashMap<Arc<str>, V>
where V: Into<Value> + Clone + Send + Sync + Debug + 'static,

source§

fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value>

source§

fn enumerate(self: &Arc<Self>) -> Enumerator

source§

impl<V> Object for HashMap<Value, V>
where V: Into<Value> + Clone + Send + Sync + Debug + 'static,

source§

fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value>

source§

fn enumerate(self: &Arc<Self>) -> Enumerator

Implementors§