66 lines
1.6 KiB
Rust
66 lines
1.6 KiB
Rust
|
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::<TemplateState>().ok_or_else(|| {
|
||
|
Status::InternalServerError
|
||
|
})?;
|
||
|
|
||
|
self.render(&template_state.tera).respond_to(request)
|
||
|
}
|
||
|
}
|