trillium_tera/tera_conn_ext.rs
1use crate::TeraHandler;
2use serde::Serialize;
3use tera::{Context, Tera};
4use trillium::{Conn, KnownHeaderName};
5
6/**
7Extends trillium::Conn with tera template-rendering functionality.
8*/
9pub trait TeraConnExt {
10 /// Adds a key-value pair to the assigns [`Context`], where the key is
11 /// a &str and the value is any [`Serialize`] type.
12 fn assign(self, key: &str, value: impl Serialize) -> Self;
13
14 /// Uses the accumulated assigns context to render the template by
15 /// registered name to the conn body and return the conn. Halts
16 /// and sets a 200 status on successful render. Must be run
17 /// downsequence of the [`TeraHandler`], and will panic if the
18 /// TeraHandler has not already been called.
19 fn render(self, template: &str) -> Self;
20
21 /// Retrieves a reference to the [`Tera`] instance. Must be called
22 /// downsequence of the [`TeraHandler`], and will panic if the
23 /// TeraHandler has not already been called.
24 fn tera(&self) -> &Tera;
25
26 /// retrieves a reference to the tera assigns context. must be run
27 /// downsequence of the [`TeraHandler`], and will panic if the
28 /// TeraHandler has not already been called.
29 fn context_mut(&mut self) -> &mut Context;
30
31 /// Retrieves a reference to the tera assigns context. Must be run
32 /// downsequence of the [`TeraHandler`], and will panic if the
33 /// TeraHandler has not already been called.
34 fn context(&self) -> &Context;
35}
36
37impl TeraConnExt for Conn {
38 fn assign(mut self, key: &str, value: impl Serialize) -> Self {
39 self.context_mut().insert(key, &value);
40 self
41 }
42
43 fn tera(&self) -> &Tera {
44 self.state::<TeraHandler>()
45 .expect("tera must be run after the tera handler")
46 .tera()
47 }
48
49 fn context_mut(&mut self) -> &mut Context {
50 self.state_mut()
51 .expect("context_mut must be run after the tera handler")
52 }
53
54 fn context(&self) -> &Context {
55 self.state()
56 .expect("context must be run after the tera handler")
57 }
58
59 fn render(mut self, template_name: &str) -> Self {
60 let context = self.context();
61 match self.tera().render(template_name, context) {
62 Ok(string) => {
63 if let Some(mime) = mime_guess::from_path(template_name).first_raw() {
64 self.response_headers_mut()
65 .try_insert(KnownHeaderName::ContentType, mime);
66 }
67
68 self.ok(string)
69 }
70
71 Err(e) => {
72 log::error!("{:?}", &e);
73 self.with_status(500).with_body(e.to_string())
74 }
75 }
76 }
77}