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}