handlebars_iron/
middleware.rs1use std::sync::{RwLock, RwLockWriteGuard};
2
3use iron::headers::ContentType;
4use iron::modifier::Modifier;
5use iron::prelude::*;
6use iron::status;
7use iron::{typemap, AfterMiddleware};
8use plugin::Plugin as PluginFor;
9
10use handlebars::{to_json, Handlebars, TemplateRenderError};
11use serde::ser::Serialize as ToJson;
12use serde_json::value::Value as Json;
13
14use source::{Source, SourceError};
15
16#[derive(Clone)]
17pub struct Template {
18 name: Option<String>,
19 content: Option<String>,
20 value: Json,
21}
22
23impl Template {
24 pub fn new<T: ToJson>(name: &str, value: T) -> Template {
26 Template {
27 name: Some(name.to_string()),
28 value: to_json(&value),
29 content: None,
30 }
31 }
32
33 pub fn with<T: ToJson>(content: &str, value: T) -> Template {
35 Template {
36 name: None,
37 value: to_json(&value),
38 content: Some(content.to_string()),
39 }
40 }
41}
42
43pub struct HandlebarsEngine {
45 pub sources: Vec<Box<dyn Source + Send + Sync>>,
46 pub registry: RwLock<Box<Handlebars<'static>>>,
47}
48
49impl typemap::Key for HandlebarsEngine {
50 type Value = Template;
51}
52
53impl Modifier<Response> for Template {
54 fn modify(self, resp: &mut Response) {
55 resp.extensions.insert::<HandlebarsEngine>(self);
56 }
57}
58
59impl PluginFor<Response> for HandlebarsEngine {
60 type Error = ();
61
62 fn eval(resp: &mut Response) -> Result<Template, ()> {
63 match resp.extensions.get::<HandlebarsEngine>() {
64 Some(t) => Ok(t.clone()),
65 None => Err(()),
66 }
67 }
68}
69
70impl HandlebarsEngine {
71 pub fn new() -> HandlebarsEngine {
73 HandlebarsEngine {
74 sources: Vec::new(),
75 registry: RwLock::new(Box::new(Handlebars::new())),
76 }
77 }
78
79 pub fn from(reg: Handlebars<'static>) -> HandlebarsEngine {
81 HandlebarsEngine {
82 sources: Vec::new(),
83 registry: RwLock::new(Box::new(reg)),
84 }
85 }
86
87 pub fn add(&mut self, source: Box<dyn Source + Send + Sync>) {
89 self.sources.push(source);
90 }
91
92 pub fn reload(&self) -> Result<(), SourceError> {
94 let mut hbs = self.handlebars_mut();
95 hbs.clear_templates();
96 for s in self.sources.iter() {
97 s.load(&mut hbs)?
98 }
99 Ok(())
100 }
101
102 pub fn handlebars_mut(&self) -> RwLockWriteGuard<Box<Handlebars<'static>>> {
104 self.registry.write().unwrap()
105 }
106}
107
108impl AfterMiddleware for HandlebarsEngine {
109 fn after(&self, _: &mut Request, mut resp: Response) -> IronResult<Response> {
110 let page_wrapper = resp.extensions.remove::<HandlebarsEngine>().and_then(|h| {
111 let hbs = self.registry.read().unwrap();
112 if let Some(ref name) = h.name {
113 Some(
114 hbs.render(name, &h.value)
115 .map_err(TemplateRenderError::from),
116 )
117 } else if let Some(ref content) = h.content {
118 Some(hbs.render_template(content, &h.value))
119 } else {
120 None
121 }
122 });
123
124 match page_wrapper {
125 Some(page_result) => match page_result {
126 Ok(page) => {
127 if !resp.headers.has::<ContentType>() {
128 resp.headers.set(ContentType::html());
129 }
130 resp.set_mut(page);
131 Ok(resp)
132 }
133 Err(e) => {
134 info!("{}", e);
135 Err(IronError::new(e, status::InternalServerError))
136 }
137 },
138 None => Ok(resp),
139 }
140 }
141
142 fn catch(&self, req: &mut Request, mut err: IronError) -> IronResult<Response> {
143 err.response = self.after(req, err.response)?;
144 Err(err)
145 }
146}
147
148#[cfg(test)]
149mod test {
150 use handlebars::{Context, Handlebars, Helper, Output, RenderContext, RenderError};
151 use iron::prelude::*;
152 use middleware::*;
153 use std::collections::BTreeMap;
154
155 fn hello_world() -> IronResult<Response> {
156 let resp = Response::new();
157
158 let mut data = BTreeMap::new();
159 data.insert("title".to_owned(), "Handlebars on Iron".to_owned());
160
161 Ok(resp.set(Template::new("index", data)))
162 }
163
164 fn hello_world2() -> IronResult<Response> {
165 let resp = Response::new();
166
167 let mut data = BTreeMap::new();
168 data.insert("title".to_owned(), "Handlebars on Iron".to_owned());
169
170 Ok(resp.set(Template::with("{{title}}", data)))
171 }
172
173 #[test]
174 fn test_resp_set() {
175 let mut resp = hello_world().ok().expect("response expected");
176
177 match resp.get::<HandlebarsEngine>() {
179 Ok(h) => {
180 assert_eq!(h.name.unwrap(), "index".to_string());
181 assert_eq!(
182 h.value
183 .as_object()
184 .unwrap()
185 .get(&"title".to_string())
186 .unwrap()
187 .as_str()
188 .unwrap(),
189 "Handlebars on Iron"
190 );
191 }
192 _ => panic!("template expected"),
193 }
194 }
195
196 #[test]
197 fn test_resp_set2() {
198 let mut resp = hello_world2().ok().expect("response expected");
199
200 match resp.get::<HandlebarsEngine>() {
202 Ok(h) => {
203 assert_eq!(h.content.unwrap(), "{{title}}".to_string());
204 assert_eq!(
205 h.value
206 .as_object()
207 .unwrap()
208 .get(&"title".to_string())
209 .unwrap()
210 .as_str()
211 .unwrap(),
212 "Handlebars on Iron"
213 );
214 }
215 _ => panic!("template expected"),
216 }
217 }
218
219 #[test]
220 fn test_register_helper() {
221 let hbs = HandlebarsEngine::new();
222 let mut reg = hbs.handlebars_mut();
223 reg.register_helper(
224 "ignore",
225 Box::new(
226 |_: &Helper,
227 _: &Handlebars,
228 _: &Context,
229 _: &mut RenderContext,
230 _: &mut dyn Output|
231 -> Result<(), RenderError> { Ok(()) },
232 ),
233 );
234 }
235}