trillium_handlebars/handlebars_handler.rs
1use handlebars::{Handlebars, RenderError};
2use serde::Serialize;
3use std::{
4 path::PathBuf,
5 sync::{Arc, RwLock},
6};
7use trillium::{Conn, Handler};
8/// A trillium handler that provides registered templates to
9/// downsequence handlers
10
11#[derive(Default, Clone, Debug)]
12pub struct HandlebarsHandler(Arc<RwLock<Handlebars<'static>>>);
13
14impl HandlebarsHandler {
15 /// Builds a new trillium Handlebars handler from either a directory
16 /// glob string or [`PathBuf`] or a
17 /// [`handlebars::Handlebars<'static>`](handlebars::Handlebars)
18 /// instance
19 ///
20 /// ## From a glob
21 /// ```
22 /// # if cfg!(unix) {
23 /// # use std::path::PathBuf;
24 /// use trillium_handlebars::{HandlebarsConnExt, HandlebarsHandler};
25 /// use trillium_testing::TestServer;
26 ///
27 /// # trillium_testing::block_on(async {
28 /// let handler = (
29 /// HandlebarsHandler::new("**/*.hbs"),
30 /// |mut conn: trillium::Conn| async move {
31 /// conn.assign("name", "handlebars")
32 /// .render("examples/templates/hello.hbs")
33 /// },
34 /// );
35 ///
36 /// let app = TestServer::new(handler).await;
37 /// app.get("/")
38 /// .await
39 /// .assert_ok()
40 /// .assert_body("hello handlebars!");
41 /// # });
42 /// # }
43 /// ```
44 /// ## From a [`handlebars::Handlebars`]
45 ///
46 /// ```
47 /// use trillium_handlebars::{HandlebarsHandler, Handlebars, HandlebarsConnExt};
48 /// use trillium_testing::TestServer;
49 ///
50 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
51 /// // building a Handlebars directly
52 /// let mut handlebars = Handlebars::new();
53 /// handlebars.register_template_string("greet-user", "Hello {{name}}")?;
54 /// let handler = (
55 /// HandlebarsHandler::new(handlebars),
56 /// |mut conn: trillium::Conn| async move {
57 /// conn.assign("name", "handlebars")
58 /// .render("greet-user")
59 /// }
60 /// );
61 ///
62 /// # trillium_testing::block_on(async {
63 /// let app = TestServer::new(handler).await;
64 /// app.get("/")
65 /// .await
66 /// .assert_ok()
67 /// .assert_body("Hello handlebars");
68 /// # });
69 /// # Ok(()) }
70 /// ```
71 pub fn new(source: impl Into<Self>) -> Self {
72 source.into()
73 }
74
75 pub(crate) fn render(
76 &self,
77 template: &str,
78 data: &impl Serialize,
79 ) -> Result<String, RenderError> {
80 self.0.read().unwrap().render(template, data)
81 }
82
83 fn glob(self, pattern: &str) -> Self {
84 {
85 let mut handlebars = self.0.write().unwrap();
86 for file in glob::glob(pattern).unwrap().filter_map(Result::ok) {
87 log::debug!("registered template {:?}", &file);
88 handlebars
89 .register_template_file(file.clone().to_string_lossy().as_ref(), file)
90 .unwrap();
91 }
92 }
93
94 self
95 }
96}
97
98impl From<&str> for HandlebarsHandler {
99 fn from(pattern: &str) -> Self {
100 Self::default().glob(pattern)
101 }
102}
103
104impl From<Handlebars<'static>> for HandlebarsHandler {
105 fn from(ah: Handlebars<'static>) -> Self {
106 Self(Arc::new(RwLock::new(ah)))
107 }
108}
109
110impl From<PathBuf> for HandlebarsHandler {
111 fn from(path: PathBuf) -> Self {
112 path.to_str().unwrap().into()
113 }
114}
115
116impl Handler for HandlebarsHandler {
117 async fn run(&self, conn: Conn) -> Conn {
118 conn.with_state(self.clone())
119 }
120}