1 | use super::block_util::create_block; |
2 | use crate::block::BlockParams; |
3 | use crate::context::Context; |
4 | use crate::error::RenderError; |
5 | use crate::helpers::{HelperDef, HelperResult}; |
6 | use crate::json::value::JsonTruthy; |
7 | use crate::output::Output; |
8 | use crate::registry::Registry; |
9 | use crate::render::{Helper, RenderContext, Renderable}; |
10 | |
11 | #[derive (Clone, Copy)] |
12 | pub struct WithHelper; |
13 | |
14 | impl HelperDef for WithHelper { |
15 | fn call<'reg: 'rc, 'rc>( |
16 | &self, |
17 | h: &Helper<'reg, 'rc>, |
18 | r: &'reg Registry<'reg>, |
19 | ctx: &'rc Context, |
20 | rc: &mut RenderContext<'reg, 'rc>, |
21 | out: &mut dyn Output, |
22 | ) -> HelperResult { |
23 | let param = h |
24 | .param(0) |
25 | .ok_or_else(|| RenderError::new("Param not found for helper \"with \"" ))?; |
26 | |
27 | if param.value().is_truthy(false) { |
28 | let mut block = create_block(param); |
29 | |
30 | if let Some(block_param) = h.block_param() { |
31 | let mut params = BlockParams::new(); |
32 | if param.context_path().is_some() { |
33 | params.add_path(block_param, Vec::with_capacity(0))?; |
34 | } else { |
35 | params.add_value(block_param, param.value().clone())?; |
36 | } |
37 | |
38 | block.set_block_params(params); |
39 | } |
40 | |
41 | rc.push_block(block); |
42 | |
43 | if let Some(t) = h.template() { |
44 | t.render(r, ctx, rc, out)?; |
45 | }; |
46 | |
47 | rc.pop_block(); |
48 | Ok(()) |
49 | } else if let Some(t) = h.inverse() { |
50 | t.render(r, ctx, rc, out) |
51 | } else if r.strict_mode() { |
52 | Err(RenderError::strict_error(param.relative_path())) |
53 | } else { |
54 | Ok(()) |
55 | } |
56 | } |
57 | } |
58 | |
59 | pub static WITH_HELPER: WithHelper = WithHelper; |
60 | |
61 | #[cfg (test)] |
62 | mod test { |
63 | use crate::registry::Registry; |
64 | |
65 | #[derive (Serialize)] |
66 | struct Address { |
67 | city: String, |
68 | country: String, |
69 | } |
70 | |
71 | #[derive (Serialize)] |
72 | struct Person { |
73 | name: String, |
74 | age: i16, |
75 | addr: Address, |
76 | titles: Vec<String>, |
77 | } |
78 | |
79 | #[test ] |
80 | fn test_with() { |
81 | let addr = Address { |
82 | city: "Beijing" .to_string(), |
83 | country: "China" .to_string(), |
84 | }; |
85 | |
86 | let person = Person { |
87 | name: "Ning Sun" .to_string(), |
88 | age: 27, |
89 | addr, |
90 | titles: vec!["programmer" .to_string(), "cartographier" .to_string()], |
91 | }; |
92 | |
93 | let mut handlebars = Registry::new(); |
94 | assert!(handlebars |
95 | .register_template_string("t0" , "{{#with addr}}{{city}}{{/with}}" ) |
96 | .is_ok()); |
97 | assert!(handlebars |
98 | .register_template_string("t1" , "{{#with notfound}}hello{{else}}world{{/with}}" ) |
99 | .is_ok()); |
100 | assert!(handlebars |
101 | .register_template_string("t2" , "{{#with addr/country}}{{this}}{{/with}}" ) |
102 | .is_ok()); |
103 | |
104 | let r0 = handlebars.render("t0" , &person); |
105 | assert_eq!(r0.ok().unwrap(), "Beijing" .to_string()); |
106 | |
107 | let r1 = handlebars.render("t1" , &person); |
108 | assert_eq!(r1.ok().unwrap(), "world" .to_string()); |
109 | |
110 | let r2 = handlebars.render("t2" , &person); |
111 | assert_eq!(r2.ok().unwrap(), "China" .to_string()); |
112 | } |
113 | |
114 | #[test ] |
115 | fn test_with_block_param() { |
116 | let addr = Address { |
117 | city: "Beijing" .to_string(), |
118 | country: "China" .to_string(), |
119 | }; |
120 | |
121 | let person = Person { |
122 | name: "Ning Sun" .to_string(), |
123 | age: 27, |
124 | addr, |
125 | titles: vec!["programmer" .to_string(), "cartographier" .to_string()], |
126 | }; |
127 | |
128 | let mut handlebars = Registry::new(); |
129 | assert!(handlebars |
130 | .register_template_string("t0" , "{{#with addr as |a|}}{{a.city}}{{/with}}" ) |
131 | .is_ok()); |
132 | assert!(handlebars |
133 | .register_template_string("t1" , "{{#with notfound as |c|}}hello{{else}}world{{/with}}" ) |
134 | .is_ok()); |
135 | assert!(handlebars |
136 | .register_template_string("t2" , "{{#with addr/country as |t|}}{{t}}{{/with}}" ) |
137 | .is_ok()); |
138 | |
139 | let r0 = handlebars.render("t0" , &person); |
140 | assert_eq!(r0.ok().unwrap(), "Beijing" .to_string()); |
141 | |
142 | let r1 = handlebars.render("t1" , &person); |
143 | assert_eq!(r1.ok().unwrap(), "world" .to_string()); |
144 | |
145 | let r2 = handlebars.render("t2" , &person); |
146 | assert_eq!(r2.ok().unwrap(), "China" .to_string()); |
147 | } |
148 | |
149 | #[test ] |
150 | fn test_with_in_each() { |
151 | let addr = Address { |
152 | city: "Beijing" .to_string(), |
153 | country: "China" .to_string(), |
154 | }; |
155 | |
156 | let person = Person { |
157 | name: "Ning Sun" .to_string(), |
158 | age: 27, |
159 | addr, |
160 | titles: vec!["programmer" .to_string(), "cartographier" .to_string()], |
161 | }; |
162 | |
163 | let addr2 = Address { |
164 | city: "Beijing" .to_string(), |
165 | country: "China" .to_string(), |
166 | }; |
167 | |
168 | let person2 = Person { |
169 | name: "Ning Sun" .to_string(), |
170 | age: 27, |
171 | addr: addr2, |
172 | titles: vec!["programmer" .to_string(), "cartographier" .to_string()], |
173 | }; |
174 | |
175 | let people = vec![person, person2]; |
176 | |
177 | let mut handlebars = Registry::new(); |
178 | assert!(handlebars |
179 | .register_template_string( |
180 | "t0" , |
181 | "{{#each this}}{{#with addr}}{{city}}{{/with}}{{/each}}" |
182 | ) |
183 | .is_ok()); |
184 | assert!(handlebars |
185 | .register_template_string( |
186 | "t1" , |
187 | "{{#each this}}{{#with addr}}{{../age}}{{/with}}{{/each}}" |
188 | ) |
189 | .is_ok()); |
190 | assert!(handlebars |
191 | .register_template_string( |
192 | "t2" , |
193 | "{{#each this}}{{#with addr}}{{@../index}}{{/with}}{{/each}}" |
194 | ) |
195 | .is_ok()); |
196 | |
197 | let r0 = handlebars.render("t0" , &people); |
198 | assert_eq!(r0.ok().unwrap(), "BeijingBeijing" .to_string()); |
199 | |
200 | let r1 = handlebars.render("t1" , &people); |
201 | assert_eq!(r1.ok().unwrap(), "2727" .to_string()); |
202 | |
203 | let r2 = handlebars.render("t2" , &people); |
204 | assert_eq!(r2.ok().unwrap(), "01" .to_string()); |
205 | } |
206 | |
207 | #[test ] |
208 | fn test_path_up() { |
209 | let mut handlebars = Registry::new(); |
210 | assert!(handlebars |
211 | .register_template_string("t0" , "{{#with a}}{{#with b}}{{../../d}}{{/with}}{{/with}}" ) |
212 | .is_ok()); |
213 | let data = json!({ |
214 | "a" : { |
215 | "b" : [{"c" : [1]}] |
216 | }, |
217 | "d" : 1 |
218 | }); |
219 | |
220 | let r0 = handlebars.render("t0" , &data); |
221 | assert_eq!(r0.ok().unwrap(), "1" .to_string()); |
222 | } |
223 | |
224 | #[test ] |
225 | fn test_else_context() { |
226 | let reg = Registry::new(); |
227 | let template = "{{#with list}}A{{else}}{{foo}}{{/with}}" ; |
228 | let input = json!({"list" : [], "foo" : "bar" }); |
229 | let rendered = reg.render_template(template, &input).unwrap(); |
230 | assert_eq!("bar" , rendered); |
231 | } |
232 | |
233 | #[test ] |
234 | fn test_derived_value() { |
235 | let hb = Registry::new(); |
236 | let data = json!({"a" : {"b" : {"c" : "d" }}}); |
237 | let template = "{{#with (lookup a.b \"c \")}}{{this}}{{/with}}" ; |
238 | assert_eq!("d" , hb.render_template(template, &data).unwrap()); |
239 | } |
240 | |
241 | #[test ] |
242 | fn test_nested_derived_value() { |
243 | let hb = Registry::new(); |
244 | let data = json!({"a" : {"b" : {"c" : "d" }}}); |
245 | let template = "{{#with (lookup a \"b \")}}{{#with this}}{{c}}{{/with}}{{/with}}" ; |
246 | assert_eq!("d" , hb.render_template(template, &data).unwrap()); |
247 | } |
248 | |
249 | #[test ] |
250 | fn test_strict_with() { |
251 | let mut hb = Registry::new(); |
252 | |
253 | assert_eq!( |
254 | hb.render_template("{{#with name}}yes{{/with}}" , &json!({})) |
255 | .unwrap(), |
256 | "" |
257 | ); |
258 | assert_eq!( |
259 | hb.render_template("{{#with name}}yes{{else}}no{{/with}}" , &json!({})) |
260 | .unwrap(), |
261 | "no" |
262 | ); |
263 | |
264 | hb.set_strict_mode(true); |
265 | |
266 | assert!(hb |
267 | .render_template("{{#with name}}yes{{/with}}" , &json!({})) |
268 | .is_err()); |
269 | assert_eq!( |
270 | hb.render_template("{{#with name}}yes{{else}}no{{/with}}" , &json!({})) |
271 | .unwrap(), |
272 | "no" |
273 | ); |
274 | } |
275 | } |
276 | |