teravars 0.1.0

Tera templating + smart [vars] handling for self-rendering TOML configs
Documentation
use tera::{Context, Function, Tera};

use crate::Result;
use crate::helpers;

pub struct Engine {
    tera: Tera,
}

impl Engine {
    pub fn new() -> Self {
        let mut tera = Tera::default();
        helpers::register_default(&mut tera);
        Self { tera }
    }

    pub fn new_minimal() -> Self {
        Self {
            tera: Tera::default(),
        }
    }

    pub fn register_function<F>(&mut self, name: &str, f: F)
    where
        F: Function + 'static,
    {
        self.tera.register_function(name, f);
    }

    pub fn render(&mut self, src: &str, ctx: &Context) -> Result<String> {
        Ok(self.tera.render_str(src, ctx)?)
    }

    pub fn tera_mut(&mut self) -> &mut Tera {
        &mut self.tera
    }
}

impl Default for Engine {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn render_substitutes_variable() {
        let mut engine = Engine::new();
        let mut ctx = Context::new();
        ctx.insert("name", "world");
        let out = engine.render("hello {{ name }}", &ctx).unwrap();
        assert_eq!(out, "hello world");
    }

    #[test]
    fn new_minimal_has_no_default_helpers() {
        let mut engine = Engine::new_minimal();
        let ctx = Context::new();
        let err = engine
            .render(r#"{{ env(name="PATH") }}"#, &ctx)
            .unwrap_err();
        let msg = err.to_string();
        assert!(
            msg.contains("env") || msg.contains("not found"),
            "unexpected error: {msg}"
        );
    }

    #[test]
    fn register_function_overrides_helper() {
        let mut engine = Engine::new();
        engine.register_function("custom", |_args: &_| Ok("ok".into()));
        let out = engine.render("{{ custom() }}", &Context::new()).unwrap();
        assert_eq!(out, "ok");
    }
}