trillium_handlebars/handlebars_conn_ext.rs
1use crate::{Assigns, HandlebarsHandler};
2use serde::Serialize;
3use serde_json::json;
4use std::borrow::Cow;
5use trillium::Conn;
6
7/// Extension trait that provides handlebar rendering capabilities to
8/// [`trillium::Conn`]s.
9pub trait HandlebarsConnExt {
10 /// Registers an "assigns" value on this Conn for use in a template.
11 /// See example usage at [`Handlebars::new`](crate::Handlebars::new)
12 fn assign(self, key: impl Into<Cow<'static, str>>, data: impl Serialize) -> Self;
13
14 /// renders a registered template by name with the provided data as
15 /// assigns. note that this does not use any data accumulated by
16 /// [`HandlebarsConnExt::assign`]
17 ///
18 /// ```
19 /// use trillium_handlebars::{Handlebars, HandlebarsConnExt, HandlebarsHandler};
20 /// use trillium_testing::TestServer;
21 ///
22 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
23 ///
24 /// #[derive(serde::Serialize)]
25 /// struct User {
26 /// name: &'static str,
27 /// };
28 ///
29 /// let mut handlebars = Handlebars::new();
30 /// handlebars.register_template_string("greet-user", "Hello {{name}}")?;
31 ///
32 /// let handler = (
33 /// HandlebarsHandler::new(handlebars),
34 /// |mut conn: trillium::Conn| async move {
35 /// conn.render_with("greet-user", &User { name: "handlebars" })
36 /// },
37 /// );
38 ///
39 /// # trillium_testing::block_on(async {
40 /// let app = TestServer::new(handler).await;
41 /// app.get("/")
42 /// .await
43 /// .assert_ok()
44 /// .assert_body("Hello handlebars");
45 /// # });
46 /// # Ok(()) }
47 /// ```
48 fn render_with(self, template: &str, data: &impl Serialize) -> Self;
49
50 /// renders a registered template, passing any accumulated assigns to
51 /// the template. See example at [`Handlebars::new`](crate::Handlebars::new)
52 fn render(self, template: &str) -> Self;
53
54 /// retrieves a reference to any accumulated assigns on this conn
55 fn assigns(&self) -> Option<&Assigns>;
56
57 /// retrieves a mutable reference to any accumulated assigns on this
58 /// conn
59 fn assigns_mut(&mut self) -> &mut Assigns;
60}
61
62impl HandlebarsConnExt for Conn {
63 fn render_with(self, template: &str, data: &impl Serialize) -> Self {
64 let handlebars: &HandlebarsHandler = self
65 .state()
66 .expect("HandlebarsConnExt::render called without running the handler first");
67
68 match handlebars.render(template, data) {
69 Ok(string) => self.ok(string),
70 Err(b) => self.with_status(500).with_body(b.to_string()),
71 }
72 }
73
74 fn assign(mut self, key: impl Into<Cow<'static, str>>, data: impl Serialize) -> Self {
75 self.assigns_mut().insert(
76 key.into(),
77 serde_json::to_value(data).expect("could not serialize assigns"),
78 );
79 self
80 }
81
82 fn render(self, template: &str) -> Self {
83 let handlebars: &HandlebarsHandler = self
84 .state()
85 .expect("HandlebarsConnExt::render called without running the handler first");
86
87 let string = if let Some(assigns) = self.assigns() {
88 handlebars.render(template, assigns)
89 } else {
90 handlebars.render(template, &json!({}))
91 };
92
93 match string {
94 Ok(string) => self.ok(string),
95 Err(b) => self.with_status(500).with_body(b.to_string()),
96 }
97 }
98
99 fn assigns(&self) -> Option<&Assigns> {
100 self.state()
101 }
102
103 fn assigns_mut(&mut self) -> &mut Assigns {
104 self.state_entry().or_default()
105 }
106}