Crate minijinja_stack_ref

source ·
Expand description

An extension package to MiniJinja that allows stack borrows.

This is an experimental package. There might be soundness issues and there might be problems with the API. Please give feedback.

§Intro

When implementing dynamic objects for MiniJinja lifetimes a common hurdle can be lifetimes. That’s because MiniJinja requires that all values passed to the template are owned by the runtime engine. Thus it becomes impossible to carry non static lifetimes into the template.

This crate provides a solution to this issue by moving lifetime checks to the runtime for MiniJinja objects. One first needs to create a Scope with the scope function. It invokes a callback to which a scope is passed which in turn then provides functionality to create Values to those borrowed values such as the object_ref method.

§Example

This example demonstrates how to pass borrowed information into a template:

use minijinja::value::{StructObject, Value};
use minijinja::{context, Environment};
use minijinja_stack_ref::scope;

struct State {
    version: &'static str,
}

impl StructObject for State {
    fn get_field(&self, field: &str) -> Option<Value> {
        match field {
            "version" => Some(Value::from(self.version)),
            _ => None,
        }
    }
}

let mut env = Environment::new();
env.add_template(
    "info",
    "app version: {{ state.version }}\nitems: {{ items }}"
)
.unwrap();

let state = State {
    version: env!("CARGO_PKG_VERSION"),
};
let items = [1u32, 2, 3, 4];

let rv = scope(|scope| {
    let tmpl = env.get_template("info").unwrap();
    tmpl.render(context! {
        state => scope.struct_object_ref(&state),
        items => scope.seq_object_ref(&items[..]),
    }).unwrap()
});
println!("{}", rv);

§Reborrowing

If an object holds other complex values it can be interesting to again return a reference to a member rather. In that case it becomes necessary again to get access to the Scope. This can be accomplished with the reborrow functionality which. It lets you return references to &self from within an referenced object:

use minijinja::value::{StructObject, Value};
use minijinja::{context, Environment};
use minijinja_stack_ref::{reborrow, scope};

struct Config {
    version: &'static str,
}

struct State {
    config: Config,
}

impl StructObject for Config {
    fn get_field(&self, field: &str) -> Option<Value> {
        match field {
            "version" => Some(Value::from(self.version)),
            _ => None,
        }
    }
}

impl StructObject for State {
    fn get_field(&self, field: &str) -> Option<Value> {
        match field {
            // return a reference to the inner config through reborrowing
            "config" => Some(reborrow(self, |slf, scope| {
                scope.struct_object_ref(&slf.config)
            })),
            _ => None,
        }
    }
}

let mut env = Environment::new();
env.add_template(
    "info",
    "app version: {{ state.config.version }}"
)
.unwrap();

let state = State {
    config: Config {
        version: env!("CARGO_PKG_VERSION"),
    }
};

let rv = scope(|scope| {
    let tmpl = env.get_template("info").unwrap();
    tmpl.render(context! {
        state => scope.struct_object_ref(&state),
    }).unwrap()
});
println!("{}", rv);

Structs§

Functions§

  • Returns true if reborrowing is possible.
  • Reborrows a reference to a dynamic object with the scope’s lifetime.
  • Invokes a function with a reference to the stack scope so values can be borrowed.