Refactor template handler module for tighter interfaces
This commit is contained in:
parent
37a1259c0d
commit
b443116e56
2 changed files with 138 additions and 36 deletions
|
|
@ -47,15 +47,17 @@ fn make_body(
|
|||
context.insert("message", out_message);
|
||||
context.insert("status_code", &out_code.to_string());
|
||||
|
||||
handlers::template::render(
|
||||
match handlers::template::render(
|
||||
"error",
|
||||
&context,
|
||||
Some(&format!(
|
||||
"Failed to render template for Error {out_code}: {out_message}"
|
||||
))
|
||||
.cloned(),
|
||||
)
|
||||
.0
|
||||
) {
|
||||
Ok(rendered) => rendered.html,
|
||||
Err(error) => error.template.html,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn not_found(State(state): State<GlobalState>) -> Response<Body> {
|
||||
|
|
|
|||
|
|
@ -31,39 +31,100 @@ pub(in crate::router::handlers) fn with_context(
|
|||
error_message: Option<String>,
|
||||
is_error: bool,
|
||||
) -> Response<Body> {
|
||||
let (body, render_status) = render(name, context, error_message);
|
||||
|
||||
let status_code = if is_error { error_code } else { render_status };
|
||||
|
||||
make_response(&body, status_code, &[(header::CONTENT_TYPE, "text/html")])
|
||||
match render(name, context, error_message) {
|
||||
Ok(rendered) => {
|
||||
let status_code = if is_error { error_code } else { rendered.code };
|
||||
make_response(
|
||||
&rendered.html,
|
||||
status_code,
|
||||
&[(header::CONTENT_TYPE, "text/html")],
|
||||
)
|
||||
},
|
||||
Err(error) => make_response(
|
||||
&error.template.html,
|
||||
error.template.code,
|
||||
&[(header::CONTENT_TYPE, "text/html")],
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Renderes a template into a String and error code.
|
||||
#[derive(Debug)]
|
||||
pub struct Rendered {
|
||||
pub html: String,
|
||||
pub code: u16,
|
||||
}
|
||||
|
||||
impl Rendered {
|
||||
fn ok(html: &str) -> Rendered {
|
||||
Rendered {
|
||||
code: 200,
|
||||
html: String::from(html),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RenderingError {
|
||||
pub message: String,
|
||||
pub template: Rendered,
|
||||
}
|
||||
|
||||
impl RenderingError {
|
||||
fn new(message: &str, code: u16, error: &tera::Error) -> RenderingError {
|
||||
RenderingError {
|
||||
message: String::from(message),
|
||||
template: Rendered {
|
||||
html: emergency_wrap(error, message),
|
||||
code,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn with_template(
|
||||
message: &str,
|
||||
code: u16,
|
||||
template: &str,
|
||||
) -> RenderingError {
|
||||
RenderingError {
|
||||
message: String::from(message),
|
||||
template: Rendered {
|
||||
html: String::from(template),
|
||||
code,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RenderingError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "Rendering Error: {}", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders a template into a String and error code.
|
||||
///
|
||||
/// The template name **must not** contain the extension (e.g. `.html`).
|
||||
pub(in crate::router::handlers) fn render(
|
||||
template: &str,
|
||||
// TODO take Option, skip context if None,
|
||||
// then template_handler can replace static_template_handler
|
||||
context: &tera::Context,
|
||||
error_message: Option<String>,
|
||||
) -> (String, u16) {
|
||||
) -> Result<Rendered, RenderingError> {
|
||||
let instant = now();
|
||||
// TODO just return an Option/String> here
|
||||
let tera = match tera::Tera::new("./templates/**/*") {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return (
|
||||
emergency_wrap(&e, "Failed instantiating template engine"),
|
||||
Ok(engine) => engine,
|
||||
Err(error) => {
|
||||
return Err(RenderingError::new(
|
||||
"Failed instantiating template engine",
|
||||
500,
|
||||
);
|
||||
&error,
|
||||
))
|
||||
},
|
||||
};
|
||||
|
||||
match tera.render(format!("{template}.html").as_str(), context) {
|
||||
Ok(t) => {
|
||||
Ok(html) => {
|
||||
tlog!(&instant, "Rendered template {template}");
|
||||
(t, 200)
|
||||
Ok(Rendered::ok(&html))
|
||||
},
|
||||
Err(e) => {
|
||||
let mut error_context = tera::Context::default();
|
||||
|
|
@ -87,11 +148,21 @@ pub(in crate::router::handlers) fn render(
|
|||
&StatusCode::INTERNAL_SERVER_ERROR.to_string(),
|
||||
);
|
||||
|
||||
(
|
||||
tera.render("error.html", &error_context)
|
||||
.unwrap_or(out_error_message.clone()),
|
||||
500,
|
||||
)
|
||||
match tera.render("error.html", &error_context) {
|
||||
Ok(rendered_error) => Err(RenderingError::with_template(
|
||||
&out_error_message,
|
||||
500,
|
||||
&rendered_error,
|
||||
)),
|
||||
Err(error_rendering_error) => Err(RenderingError::new(
|
||||
&format!(
|
||||
"Failed to render an error message template for \
|
||||
\"{out_error_message}\""
|
||||
),
|
||||
500,
|
||||
&error_rendering_error,
|
||||
)),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -192,38 +263,63 @@ mod tests {
|
|||
context.insert("node", &node);
|
||||
context.insert("graph", &graph);
|
||||
context.insert("incoming", &graph.incoming.get(&node.id));
|
||||
let (body, status) = render("node", &context, None);
|
||||
assert_eq!(status, 200);
|
||||
assert!(body.matches(payload).count() == 1);
|
||||
match render("node", &context, None) {
|
||||
Ok(rendered) => {
|
||||
assert_eq!(rendered.code, 200);
|
||||
assert!(rendered.html.matches(payload).count() == 1);
|
||||
},
|
||||
Err(error) => {
|
||||
panic!("Errored on template generation with {error:?}")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_custom_error_message() {
|
||||
let payload = "dBgIw8DnNHxJojiXzu445qUC4UpxwZCy";
|
||||
let (body, status) = render(
|
||||
match render(
|
||||
"ObH9jYUl4wMhUNcXnuqwVVzHoqx4ufyN",
|
||||
&tera::Context::default(),
|
||||
Some(payload.to_string()),
|
||||
);
|
||||
assert_eq!(status, 500);
|
||||
assert!(body.matches(payload).count() == 1);
|
||||
) {
|
||||
Ok(_) => panic!("Got Ok, expected Error"),
|
||||
Err(error) => {
|
||||
assert_eq!(error.template.code, 500);
|
||||
assert!(error.template.html.matches(payload).count() == 1);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_empty() {
|
||||
let (body, status) = render(
|
||||
match render(
|
||||
"R8D1pxwHZDxcH5SMjR7rZEnIzmpkiHkH",
|
||||
&tera::Context::default(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(status, 500);
|
||||
assert!(body.matches("Template render failed").count() == 1);
|
||||
) {
|
||||
Ok(_) => panic!("Got Ok, expected Error"),
|
||||
Err(error) => {
|
||||
assert_eq!(error.template.code, 500);
|
||||
assert!(
|
||||
error
|
||||
.template
|
||||
.html
|
||||
.matches("Template render failed")
|
||||
.count()
|
||||
== 1
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_not_found() {
|
||||
let payload = "OL6kb9qHe7Iwr7wFIRKUTeFhF34BRsQo";
|
||||
let (body, status) = render(payload, &tera::Context::default(), None);
|
||||
let (body, status) =
|
||||
match render(payload, &tera::Context::default(), None) {
|
||||
Ok(_) => panic!("Got Ok, expected Error"),
|
||||
Err(error) => (error.template.html, error.template.code),
|
||||
};
|
||||
|
||||
assert!(body.matches("TemplateNotFound").count() > 0);
|
||||
assert!(body.matches(payload).count() > 0);
|
||||
|
|
@ -232,7 +328,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn render_bad_context() {
|
||||
let (body, status) = render("node", &tera::Context::default(), None);
|
||||
let (body, status) =
|
||||
match render("node", &tera::Context::default(), None) {
|
||||
Ok(rendered) => panic!("Got Ok, expected Error"),
|
||||
Err(error) => (error.template.html, error.template.code),
|
||||
};
|
||||
assert!(body.matches("Template render failed.").count() > 0);
|
||||
assert_eq!(status, 500);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue