home ~ socials ~ projects ~ rss

Use StructObject For Dynamic Variables In Rust's MiniJinja

July 2023
use crate::page::Page;
use minijinja::value::{StructObject, Value};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(content = "content", rename_all = "lowercase", tag = "type")]
pub struct Universe {
    pub pages: Vec<Page>,
}

impl Universe {
    pub fn home_page_links(&self) -> Vec<(String, String)> {
        vec![("a".to_string(), "b".to_string())]
    }
}

impl StructObject for Universe {
    fn get_field(&self, field: &str) -> Option<Value> {
        match field {
            "home_page_links" => Some(Value::from_serializable(&self.clone().home_page_links())),
            _ => None,
        }
    }
}
use minijinja::value::{StructObject, Value};
use minijinja::{context, Environment};
use serde::{Deserialize, Serialize};

pub struct Widget;
pub struct Thing;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(content = "content", rename_all = "lowercase", tag = "type")]
pub enum Payload {
    Tango { value: String },
}

impl Widget {
    fn alfa_signal(&self) -> String {
        "This is alfa in widget".to_string()
    }
}

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

impl Thing {
    fn bravo_signal(&self) -> String {
        "This is bravo in thing".to_string()
    }
}

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

fn main() {
    let widget = Widget;
    let thing = Thing;
    let p = Payload::Tango {
        value: "charlie".to_string(),
    };
    let env = Environment::new();
    let tmpl = env
        .template_from_str("{{ p.type}};{{ p.value }} -  {{ w.alfa }} - {{ t.bravo }}")
        .unwrap();
    let w = Value::from_struct_object(widget);
    let t = Value::from_struct_object(thing);
    let output = tmpl.render(context!(w => w, t => t, p=> p)).unwrap();
    dbg!(output);
}

Notes

  • needs cargo add minijinja serde
  • needs derive added to serde in Cargo.toml like:
serde = {version = "1.0.177", features = ["derive"] }

Notes

  • If you try to just print the main object out in the template (e.g. with {{ whatever }} and don't call a function on it nothing will show up. Makes sense, but it threw me the first couple of times
  • The old notes had #[serde(content = "content", rename_all = "lowercase", tag = "type")] but I think that causes a problem with enums with a single value (i.e. not struct enums, but I forget what the other ones are called)

TODO

    Combine these two examples with .from and .from_seriliazble

    Combine with 2vauitbb

    Show regular object which allows method calls and is the way to go with neopoligin

end of line

References

This is the key to being able to use functions from object inside templates

Share link:
https://www.alanwsmith.com/en/2t/as/go/yx/?use-structobject-for-dynamic-variables-in-rust-s-minijinja