use std::path::Path; use std::process::exit; use serde::Serialize; use rocket::request::Request; use rocket::response::{self, Responder}; use rocket::http::{Status, ContentType}; use tera::{Tera, Context}; pub struct TemplateState { tera: Tera, } impl TemplateState { pub fn new(template_directory: &Path) -> Self { let template_glob = template_directory.join("**").join("*"); match Tera::new(template_glob.to_str().expect("valid glob path string")) { Ok(tera) => TemplateState { tera }, Err(e) => { println!("Loading templates failed: {}", e); exit(1) } } } } pub struct Template<'t, S: Serialize> { pub name: &'t str, pub context: S, } impl<'r, S: Serialize> Template<'r, S> { pub fn new(name: &'r str, context: S) -> Self { Template { name, context } } fn render(self, tera: &Tera) -> Result<(ContentType, String), Status> { let context = Context::from_serialize(self.context).map_err(|e| { error!("Failed to serialize context: {}", e); Status::InternalServerError })?; let content = tera.render(self.name, &context).map_err(|e| { error!("Failed to render template `{}`: {}", self.name, e); Status::InternalServerError })?; Ok((ContentType::HTML, content)) } } impl<'r, 't, S: Serialize> Responder<'r, 'static> for Template<'t, S> { fn respond_to(self, request: &'r Request<'_>) -> response::Result<'static> { let template_state = request.rocket().state::().ok_or_else(|| { Status::InternalServerError })?; self.render(&template_state.tera).respond_to(request) } }