rjango 0.1.1

A full-stack Rust backend framework inspired by Django
Documentation
use minijinja::Value;

use crate::template::engine::TemplateEngine;

#[derive(Debug, Clone)]
pub struct TemplateResponse {
    pub template_name: String,
    pub context: Value,
    rendered: Option<String>,
}

impl TemplateResponse {
    #[must_use]
    pub fn new(template_name: impl Into<String>, context: Value) -> Self {
        Self {
            template_name: template_name.into(),
            context,
            rendered: None,
        }
    }

    pub fn render(&mut self, engine: &TemplateEngine) -> Result<&str, String> {
        if self.rendered.is_none() {
            let rendered = engine
                .render(&self.template_name, self.context.clone())
                .map_err(|error| error.to_string())?;
            self.rendered = Some(rendered);
        }

        Ok(self
            .rendered
            .as_deref()
            .expect("rendered response should contain cached content"))
    }

    #[must_use]
    pub fn is_rendered(&self) -> bool {
        self.rendered.is_some()
    }
}

#[cfg(test)]
mod tests {
    use minijinja::context;

    use super::TemplateResponse;
    use crate::template::engine::TemplateEngine;

    #[test]
    fn new_response_starts_unrendered() {
        let response = TemplateResponse::new("hello.html", context!(name => "World"));

        assert_eq!(response.template_name, "hello.html");
        assert!(!response.is_rendered());
    }

    #[test]
    fn render_uses_engine_and_caches_content() {
        let mut engine = TemplateEngine::new();
        engine.add_template("hello.html", "Hello {{ name }}!");

        let mut response = TemplateResponse::new("hello.html", context!(name => "World"));
        let first = response
            .render(&engine)
            .expect("response renders")
            .to_string();
        let second = response
            .render(&engine)
            .expect("response reuses cache")
            .to_string();

        assert_eq!(first, "Hello World!");
        assert_eq!(second, "Hello World!");
        assert!(response.is_rendered());
    }

    #[test]
    fn render_returns_missing_template_errors() {
        let engine = TemplateEngine::new();
        let mut response = TemplateResponse::new("missing.html", context! {});

        let error = response
            .render(&engine)
            .expect_err("missing template should error");

        assert!(error.contains("missing.html"));
        assert!(!response.is_rendered());
    }

    #[test]
    fn cached_render_is_not_recomputed_after_context_changes() {
        let mut engine = TemplateEngine::new();
        engine.add_template("hello.html", "Hello {{ name }}!");

        let mut response = TemplateResponse::new("hello.html", context!(name => "World"));
        let first = response
            .render(&engine)
            .expect("response renders")
            .to_string();
        response.context = context!(name => "Changed");
        let second = response
            .render(&engine)
            .expect("cached response returns")
            .to_string();

        assert_eq!(first, "Hello World!");
        assert_eq!(second, "Hello World!");
    }
}