Skip to main content

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}