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