From bf0386ade78f9403da2d3a9f71352f60182dc8bf Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 16 Oct 2022 21:36:23 +0100 Subject: [PATCH] reduce minijinja boilerplate --- Cargo.lock | 1 + data-factory/src/main.rs | 2 +- templating/minijinja/Cargo.toml | 1 + templating/minijinja/src/main.rs | 89 ++++++++++++++++++++------------ templating/tera/src/main.rs | 4 +- 5 files changed, 62 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e8d2f3..3300f43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6242,6 +6242,7 @@ dependencies = [ name = "templating-minijinja" version = "1.0.0" dependencies = [ + "actix-utils", "actix-web", "actix-web-lab 0.18.5", "env_logger 0.9.1", diff --git a/data-factory/src/main.rs b/data-factory/src/main.rs index 2369d75..aaf41fd 100644 --- a/data-factory/src/main.rs +++ b/data-factory/src/main.rs @@ -32,7 +32,7 @@ async fn main() -> std::io::Result<()> { "/", web::to(|data: web::Data| async move { assert_eq!(**data, 123); - HttpResponse::NoContent() + HttpResponse::NoContent().finish() }), ) }) diff --git a/templating/minijinja/Cargo.toml b/templating/minijinja/Cargo.toml index 984f561..d9071ac 100644 --- a/templating/minijinja/Cargo.toml +++ b/templating/minijinja/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] actix-web = "4" actix-web-lab = "0.18" +actix-utils = "3" env_logger = "0.9" log = "0.4" diff --git a/templating/minijinja/src/main.rs b/templating/minijinja/src/main.rs index 0dd812c..37fee7f 100644 --- a/templating/minijinja/src/main.rs +++ b/templating/minijinja/src/main.rs @@ -1,39 +1,66 @@ +use actix_utils::future::{ready, Ready}; use std::collections::HashMap; use actix_web::{ - dev::ServiceResponse, + dev::{self, ServiceResponse}, error, http::{header::ContentType, StatusCode}, middleware::{ErrorHandlerResponse, ErrorHandlers, Logger}, - web, App, Error, HttpResponse, HttpServer, Responder, Result, + web, App, FromRequest, HttpRequest, HttpResponse, HttpServer, Responder, Result, }; use actix_web_lab::respond::Html; -async fn index( +#[derive(Debug)] +struct MiniJinjaRenderer { tmpl_env: web::Data>, +} + +impl MiniJinjaRenderer { + fn render( + &self, + tmpl: &str, + ctx: impl Into, + ) -> actix_web::Result { + self.tmpl_env + .get_template(tmpl) + .map_err(|_| error::ErrorInternalServerError("could not find template"))? + .render(ctx.into()) + .map(Html) + .map_err(|err| { + log::error!("{err}"); + error::ErrorInternalServerError("template error") + }) + } +} + +impl FromRequest for MiniJinjaRenderer { + type Error = actix_web::Error; + type Future = Ready>; + + fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { + let tmpl_env = >>::from_request(req, payload) + .into_inner() + .unwrap(); + + ready(Ok(Self { tmpl_env })) + } +} + +async fn index( + tmpl_env: MiniJinjaRenderer, query: web::Query>, -) -> Result { - let html = if let Some(name) = query.get("name") { - let tmpl = tmpl_env - .get_template("user.html") - .map_err(|_| error::ErrorInternalServerError("Template error"))?; - - let ctx = minijinja::context! { - name, - text => "Welcome!", - }; - - tmpl.render(ctx) - .map_err(|_| error::ErrorInternalServerError("Template error"))? +) -> actix_web::Result { + if let Some(name) = query.get("name") { + tmpl_env.render( + "user.html", + minijinja::context! { + name, + text => "Welcome!", + }, + ) } else { - tmpl_env - .get_template("index.html") - .map_err(|_| error::ErrorInternalServerError("Template error"))? - .render(()) - .map_err(|_| error::ErrorInternalServerError("Template error"))? - }; - - Ok(Html(html)) + tmpl_env.render("index.html", ()) + } } #[actix_web::main] @@ -75,6 +102,8 @@ fn not_found(svc_res: ServiceResponse) -> Result> fn get_error_response(res: &ServiceResponse, error: &str) -> HttpResponse { let req = res.request(); + let tmpl_env = MiniJinjaRenderer::extract(req).into_inner().unwrap(); + // Provide a fallback to a simple plain text response in case an error occurs during the // rendering of the error page. let fallback = |err: &str| { @@ -88,17 +117,13 @@ fn get_error_response(res: &ServiceResponse, error: &str) -> HttpResponse status_code => res.status().as_str(), }; - match req - .app_data::>() - .and_then(|tmpl_env| tmpl_env.get_template("error.html").ok()) - .and_then(|tmpl| tmpl.render(ctx).ok()) - { - Some(body) => Html(body) + match tmpl_env.render("error.html", ctx) { + Ok(body) => body .customize() .with_status(res.status()) - .respond_to(&req) + .respond_to(req) .map_into_boxed_body(), - None => fallback(error), + Err(_) => fallback(error), } } diff --git a/templating/tera/src/main.rs b/templating/tera/src/main.rs index 3f19ffe..22c9f4e 100644 --- a/templating/tera/src/main.rs +++ b/templating/tera/src/main.rs @@ -6,7 +6,7 @@ use actix_web::{ error, http::{header::ContentType, StatusCode}, middleware::{self, ErrorHandlerResponse, ErrorHandlers}, - web, App, Error, HttpResponse, HttpServer, Result, + web, App, Error, HttpResponse, HttpServer, Responder, Result, }; use actix_web_lab::respond::Html; use tera::Tera; @@ -15,7 +15,7 @@ use tera::Tera; async fn index( tmpl: web::Data, query: web::Query>, -) -> Result { +) -> Result { let s = if let Some(name) = query.get("name") { // submitted form let mut ctx = tera::Context::new();