]>
Commit | Line | Data |
---|---|---|
416331ca XL |
1 | use crate::context::Context; |
2 | use crate::error::RenderError; | |
3 | use crate::registry::Registry; | |
4 | use crate::render::{Directive, RenderContext}; | |
8bb4bdeb XL |
5 | |
6 | pub use self::inline::INLINE_DIRECTIVE; | |
7 | ||
9fa01778 XL |
8 | pub type DirectiveResult = Result<(), RenderError>; |
9 | ||
8bb4bdeb XL |
10 | /// Decorator Definition |
11 | /// | |
12 | /// Implement this trait to define your own decorators or directives. Currently | |
13 | /// decorator shares same definition with helper. | |
14 | /// | |
9fa01778 | 15 | /// In handlebars, it is recommended to use decorator to change context data and update helper |
7cac9316 | 16 | /// definition. |
8bb4bdeb XL |
17 | /// ## Updating context data |
18 | /// | |
19 | /// In decorator, you can change some context data your are about to render. | |
20 | /// | |
041b39d2 | 21 | /// ``` |
8bb4bdeb | 22 | /// use handlebars::*; |
8bb4bdeb | 23 | /// |
9fa01778 | 24 | /// fn update_data<'reg: 'rc, 'rc>(_: &Decorator, _: &Handlebars, ctx: &Context, rc: &mut RenderContext) |
7cac9316 | 25 | /// -> Result<(), RenderError> { |
8bb4bdeb | 26 | /// // modify json object |
9fa01778 XL |
27 | /// let mut new_ctx = ctx.clone(); |
28 | /// { | |
29 | /// let mut data = new_ctx.data_mut(); | |
30 | /// if let Some(ref mut m) = data.as_object_mut() { | |
31 | /// m.insert("hello".to_string(), to_json("world")); | |
32 | /// } | |
8bb4bdeb | 33 | /// } |
9fa01778 | 34 | /// rc.set_context(new_ctx); |
8bb4bdeb XL |
35 | /// Ok(()) |
36 | /// } | |
37 | /// | |
38 | /// ``` | |
39 | /// | |
40 | /// ## Define local helper | |
41 | /// | |
42 | /// You can override behavior of a helper from position of decorator to the end of template. | |
43 | /// | |
44 | /// ``` | |
45 | /// use handlebars::*; | |
46 | /// | |
9fa01778 | 47 | /// fn override_helper(_: &Decorator, _: &Handlebars, _: &Context, rc: &mut RenderContext) |
7cac9316 | 48 | /// -> Result<(), RenderError> { |
416331ca | 49 | /// let new_helper = |h: &Helper, _: &Handlebars, _: &Context, rc: &mut RenderContext, out: &mut dyn Output| |
7cac9316 | 50 | /// -> Result<(), RenderError> { |
8bb4bdeb XL |
51 | /// // your helper logic |
52 | /// Ok(()) | |
53 | /// }; | |
54 | /// rc.register_local_helper("distance", Box::new(new_helper)); | |
55 | /// Ok(()) | |
56 | /// } | |
57 | /// ``` | |
58 | /// | |
59 | pub trait DirectiveDef: Send + Sync { | |
9fa01778 XL |
60 | fn call<'reg: 'rc, 'rc>( |
61 | &'reg self, | |
62 | d: &Directive<'reg, 'rc>, | |
63 | r: &'reg Registry, | |
64 | ctx: &'rc Context, | |
65 | rc: &mut RenderContext<'reg>, | |
66 | ) -> DirectiveResult; | |
8bb4bdeb XL |
67 | } |
68 | ||
69 | /// implement DirectiveDef for bare function so we can use function as directive | |
ea8adc8c | 70 | impl< |
9fa01778 XL |
71 | F: Send |
72 | + Sync | |
416331ca XL |
73 | + for<'reg, 'rc> Fn( |
74 | &Directive<'reg, 'rc>, | |
75 | &'reg Registry, | |
76 | &'rc Context, | |
77 | &mut RenderContext, | |
78 | ) -> DirectiveResult, | |
9fa01778 | 79 | > DirectiveDef for F |
83c7162d | 80 | { |
9fa01778 XL |
81 | fn call<'reg: 'rc, 'rc>( |
82 | &'reg self, | |
83 | d: &Directive<'reg, 'rc>, | |
84 | reg: &'reg Registry, | |
85 | ctx: &'rc Context, | |
86 | rc: &mut RenderContext, | |
87 | ) -> DirectiveResult { | |
88 | (*self)(d, reg, ctx, rc) | |
8bb4bdeb XL |
89 | } |
90 | } | |
91 | ||
92 | mod inline; | |
93 | ||
94 | #[cfg(test)] | |
95 | mod test { | |
416331ca XL |
96 | use crate::context::Context; |
97 | use crate::error::RenderError; | |
98 | use crate::output::Output; | |
99 | use crate::registry::Registry; | |
100 | use crate::render::{Directive, Helper, RenderContext}; | |
101 | use crate::value::{as_string, to_json}; | |
8bb4bdeb XL |
102 | |
103 | #[test] | |
104 | fn test_register_decorator() { | |
105 | let mut handlebars = Registry::new(); | |
ea8adc8c XL |
106 | handlebars |
107 | .register_template_string("t0", "{{*foo}}".to_string()) | |
108 | .unwrap(); | |
8bb4bdeb | 109 | |
83c7162d | 110 | let data = btreemap! { |
8bb4bdeb XL |
111 | "hello".to_string() => "world".to_string() |
112 | }; | |
113 | ||
114 | assert!(handlebars.render("t0", &data).is_err()); | |
115 | ||
ea8adc8c XL |
116 | handlebars.register_decorator( |
117 | "foo", | |
83c7162d | 118 | Box::new( |
9fa01778 XL |
119 | |_: &Directive, |
120 | _: &Registry, | |
121 | _: &Context, | |
122 | _: &mut RenderContext| | |
123 | -> Result<(), RenderError> { Ok(()) }, | |
83c7162d | 124 | ), |
ea8adc8c | 125 | ); |
8bb4bdeb XL |
126 | assert_eq!(handlebars.render("t0", &data).ok().unwrap(), "".to_string()); |
127 | } | |
128 | ||
9fa01778 | 129 | // updating context data disabled for now |
8bb4bdeb XL |
130 | #[test] |
131 | fn test_update_data_with_decorator() { | |
132 | let mut handlebars = Registry::new(); | |
ea8adc8c XL |
133 | handlebars |
134 | .register_template_string("t0", "{{hello}}{{*foo}}{{hello}}".to_string()) | |
7cac9316 | 135 | .unwrap(); |
8bb4bdeb | 136 | |
83c7162d | 137 | let data = btreemap! { |
8bb4bdeb XL |
138 | "hello".to_string() => "world".to_string() |
139 | }; | |
140 | ||
ea8adc8c XL |
141 | handlebars.register_decorator( |
142 | "foo", | |
83c7162d | 143 | Box::new( |
9fa01778 XL |
144 | |_: &Directive, |
145 | _: &Registry, | |
146 | ctx: &Context, | |
147 | rc: &mut RenderContext| | |
148 | -> Result<(), RenderError> { | |
83c7162d | 149 | // modify json object |
9fa01778 XL |
150 | let mut new_ctx = ctx.clone(); |
151 | { | |
152 | let data = new_ctx.data_mut(); | |
153 | if let Some(ref mut m) = data.as_object_mut().as_mut() { | |
154 | m.insert("hello".to_string(), to_json("war")); | |
155 | } | |
83c7162d | 156 | } |
9fa01778 | 157 | rc.set_context(new_ctx); |
83c7162d XL |
158 | Ok(()) |
159 | }, | |
160 | ), | |
ea8adc8c XL |
161 | ); |
162 | ||
163 | assert_eq!( | |
164 | handlebars.render("t0", &data).ok().unwrap(), | |
165 | "worldwar".to_string() | |
166 | ); | |
8bb4bdeb XL |
167 | |
168 | let data2 = 0; | |
ea8adc8c XL |
169 | handlebars.register_decorator( |
170 | "bar", | |
83c7162d | 171 | Box::new( |
9fa01778 XL |
172 | |d: &Directive, |
173 | _: &Registry, | |
174 | _: &Context, | |
175 | rc: &mut RenderContext| | |
176 | -> Result<(), RenderError> { | |
83c7162d | 177 | // modify value |
9fa01778 XL |
178 | let v = d |
179 | .param(0) | |
83c7162d XL |
180 | .and_then(|v| Context::wraps(v.value()).ok()) |
181 | .unwrap_or(Context::null()); | |
9fa01778 | 182 | rc.set_context(v); |
83c7162d XL |
183 | Ok(()) |
184 | }, | |
185 | ), | |
ea8adc8c XL |
186 | ); |
187 | handlebars | |
188 | .register_template_string("t1", "{{this}}{{*bar 1}}{{this}}".to_string()) | |
7cac9316 | 189 | .unwrap(); |
ea8adc8c XL |
190 | assert_eq!( |
191 | handlebars.render("t1", &data2).ok().unwrap(), | |
192 | "01".to_string() | |
193 | ); | |
194 | ||
195 | handlebars | |
196 | .register_template_string( | |
197 | "t2", | |
198 | "{{this}}{{*bar \"string_literal\"}}{{this}}".to_string(), | |
199 | ) | |
7cac9316 | 200 | .unwrap(); |
ea8adc8c XL |
201 | assert_eq!( |
202 | handlebars.render("t2", &data2).ok().unwrap(), | |
203 | "0string_literal".to_string() | |
204 | ); | |
8bb4bdeb | 205 | |
ea8adc8c XL |
206 | handlebars |
207 | .register_template_string("t3", "{{this}}{{*bar}}{{this}}".to_string()) | |
208 | .unwrap(); | |
209 | assert_eq!( | |
210 | handlebars.render("t3", &data2).ok().unwrap(), | |
211 | "0".to_string() | |
212 | ); | |
8bb4bdeb XL |
213 | } |
214 | ||
215 | #[test] | |
216 | fn test_local_helper_with_decorator() { | |
217 | let mut handlebars = Registry::new(); | |
ea8adc8c XL |
218 | handlebars |
219 | .register_template_string( | |
220 | "t0", | |
221 | "{{distance 4.5}},{{*foo \"miles\"}}{{distance 10.1}},{{*bar}}{{distance 3.4}}" | |
222 | .to_string(), | |
223 | ) | |
224 | .unwrap(); | |
225 | ||
226 | handlebars.register_helper( | |
227 | "distance", | |
83c7162d | 228 | Box::new( |
9fa01778 XL |
229 | |h: &Helper, |
230 | _: &Registry, | |
231 | _: &Context, | |
232 | _: &mut RenderContext, | |
416331ca | 233 | out: &mut dyn Output| |
9fa01778 | 234 | -> Result<(), RenderError> { |
ea8adc8c | 235 | let s = format!( |
83c7162d | 236 | "{}m", |
9fa01778 XL |
237 | h.param(0) |
238 | .as_ref() | |
239 | .map(|v| v.value()) | |
240 | .unwrap_or(&to_json(0)) | |
ea8adc8c | 241 | ); |
9fa01778 | 242 | out.write(s.as_ref())?; |
ea8adc8c | 243 | Ok(()) |
83c7162d XL |
244 | }, |
245 | ), | |
246 | ); | |
247 | handlebars.register_decorator( | |
248 | "foo", | |
249 | Box::new( | |
9fa01778 XL |
250 | |d: &Directive, |
251 | _: &Registry, | |
252 | _: &Context, | |
253 | rc: &mut RenderContext| | |
254 | -> Result<(), RenderError> { | |
255 | let new_unit = d | |
256 | .param(0) | |
257 | .as_ref() | |
83c7162d XL |
258 | .and_then(|v| as_string(v.value())) |
259 | .unwrap_or("") | |
260 | .to_owned(); | |
261 | let new_helper = move |h: &Helper, | |
262 | _: &Registry, | |
9fa01778 XL |
263 | _: &Context, |
264 | _: &mut RenderContext, | |
416331ca | 265 | out: &mut dyn Output| |
83c7162d XL |
266 | -> Result<(), RenderError> { |
267 | let s = format!( | |
268 | "{}{}", | |
9fa01778 XL |
269 | h.param(0) |
270 | .as_ref() | |
271 | .map(|v| v.value()) | |
272 | .unwrap_or(&to_json(0)), | |
83c7162d XL |
273 | new_unit |
274 | ); | |
9fa01778 | 275 | out.write(s.as_ref())?; |
83c7162d XL |
276 | Ok(()) |
277 | }; | |
278 | ||
279 | rc.register_local_helper("distance", Box::new(new_helper)); | |
280 | Ok(()) | |
281 | }, | |
282 | ), | |
ea8adc8c XL |
283 | ); |
284 | handlebars.register_decorator( | |
285 | "bar", | |
83c7162d | 286 | Box::new( |
9fa01778 XL |
287 | |_: &Directive, |
288 | _: &Registry, | |
289 | _: &Context, | |
290 | rc: &mut RenderContext| | |
291 | -> Result<(), RenderError> { | |
83c7162d XL |
292 | rc.unregister_local_helper("distance"); |
293 | Ok(()) | |
294 | }, | |
295 | ), | |
ea8adc8c XL |
296 | ); |
297 | assert_eq!( | |
298 | handlebars.render("t0", &0).ok().unwrap(), | |
299 | "4.5m,10.1miles,3.4m".to_owned() | |
300 | ); | |
8bb4bdeb XL |
301 | } |
302 | } |