1 | #![doc (html_root_url = "https://docs.rs/handlebars/4.3.7" )] |
2 | #![cfg_attr (docsrs, feature(doc_cfg))] |
3 | #![allow (unknown_lints)] |
4 | #![allow (clippy::result_large_err)] |
5 | //! # Handlebars |
6 | //! |
7 | //! [Handlebars](http://handlebarsjs.com/) is a modern and extensible templating solution originally created in the JavaScript world. It's used by many popular frameworks like [Ember.js](http://emberjs.com) and Chaplin. It's also ported to some other platforms such as [Java](https://github.com/jknack/handlebars.java). |
8 | //! |
9 | //! And this is handlebars Rust implementation, designed for general purpose text generation. |
10 | //! |
11 | //! ## Quick Start |
12 | //! |
13 | //! ``` |
14 | //! use std::collections::BTreeMap; |
15 | //! use handlebars::Handlebars; |
16 | //! |
17 | //! fn main() { |
18 | //! // create the handlebars registry |
19 | //! let mut handlebars = Handlebars::new(); |
20 | //! |
21 | //! // register the template. The template string will be verified and compiled. |
22 | //! let source = "hello {{world}}" ; |
23 | //! assert!(handlebars.register_template_string("t1" , source).is_ok()); |
24 | //! |
25 | //! // Prepare some data. |
26 | //! // |
27 | //! // The data type should implements `serde::Serialize` |
28 | //! let mut data = BTreeMap::new(); |
29 | //! data.insert("world" .to_string(), "世界!" .to_string()); |
30 | //! assert_eq!(handlebars.render("t1" , &data).unwrap(), "hello 世界!" ); |
31 | //! } |
32 | //! ``` |
33 | //! |
34 | //! In this example, we created a template registry and registered a template named `t1`. |
35 | //! Then we rendered a `BTreeMap` with an entry of key `world`, the result is just what |
36 | //! we expected. |
37 | //! |
38 | //! I recommend you to walk through handlebars.js' [intro page](http://handlebarsjs.com) |
39 | //! if you are not quite familiar with the template language itself. |
40 | //! |
41 | //! ## Features |
42 | //! |
43 | //! Handlebars is a real-world templating system that you can use to build |
44 | //! your application without pain. |
45 | //! |
46 | //! ### Isolation of Rust and HTML |
47 | //! |
48 | //! This library doesn't attempt to use some macro magic to allow you to |
49 | //! write your template within your rust code. I admit that it's fun to do |
50 | //! that but it doesn't fit real-world use cases. |
51 | //! |
52 | //! ### Limited but essential control structures built-in |
53 | //! |
54 | //! Only essential control directives `if` and `each` are built-in. This |
55 | //! prevents you from putting too much application logic into your template. |
56 | //! |
57 | //! ### Extensible helper system |
58 | //! |
59 | //! Helper is the control system of handlebars language. In the original JavaScript |
60 | //! version, you can implement your own helper with JavaScript. |
61 | //! |
62 | //! Handlebars-rust offers similar mechanism that custom helper can be defined with |
63 | //! rust function, or [rhai](https://github.com/jonathandturner/rhai) script. |
64 | //! |
65 | //! The built-in helpers like `if` and `each` were written with these |
66 | //! helper APIs and the APIs are fully available to developers. |
67 | //! |
68 | //! ### Auto-reload in dev mode |
69 | //! |
70 | //! By turning on `dev_mode`, handlebars auto reloads any template and scripts that |
71 | //! loaded from files or directory. This can be handy for template development. |
72 | //! |
73 | //! ### Template inheritance |
74 | //! |
75 | //! Every time I look into a templating system, I will investigate its |
76 | //! support for [template inheritance][t]. |
77 | //! |
78 | //! [t]: https://docs.djangoproject.com/en/3.2/ref/templates/language/#template-inheritance |
79 | //! |
80 | //! Template include is not sufficient for template reuse. In most cases |
81 | //! you will need a skeleton of page as parent (header, footer, etc.), and |
82 | //! embed your page into this parent. |
83 | //! |
84 | //! You can find a real example of template inheritance in |
85 | //! `examples/partials.rs` and templates used by this file. |
86 | //! |
87 | //! ### Strict mode |
88 | //! |
89 | //! Handlebars, the language designed to work with JavaScript, has no |
90 | //! strict restriction on accessing nonexistent fields or indexes. It |
91 | //! generates empty strings for such cases. However, in Rust we want to be |
92 | //! a little stricter sometimes. |
93 | //! |
94 | //! By enabling `strict_mode` on handlebars: |
95 | //! |
96 | //! ``` |
97 | //! # use handlebars::Handlebars; |
98 | //! # let mut handlebars = Handlebars::new(); |
99 | //! handlebars.set_strict_mode(true); |
100 | //! ``` |
101 | //! |
102 | //! You will get a `RenderError` when accessing fields that do not exist. |
103 | //! |
104 | //! ## Limitations |
105 | //! |
106 | //! ### Compatibility with original JavaScript version |
107 | //! |
108 | //! This implementation is **not fully compatible** with the original JavaScript version. |
109 | //! |
110 | //! First of all, mustache blocks are not supported. I suggest you to use `#if` and `#each` for |
111 | //! the same functionality. |
112 | //! |
113 | //! There are some other minor features missing: |
114 | //! |
115 | //! * Chained else [#12](https://github.com/sunng87/handlebars-rust/issues/12) |
116 | //! |
117 | //! Feel free to file an issue on [github](https://github.com/sunng87/handlebars-rust/issues) if |
118 | //! you find missing features. |
119 | //! |
120 | //! ### Types |
121 | //! |
122 | //! As a static typed language, it's a little verbose to use handlebars. |
123 | //! Handlebars templating language is designed against JSON data type. In rust, |
124 | //! we will convert user's structs, vectors or maps into Serde-Json's `Value` type |
125 | //! in order to use in templates. You have to make sure your data implements the |
126 | //! `Serialize` trait from the [Serde](https://serde.rs) project. |
127 | //! |
128 | //! ## Usage |
129 | //! |
130 | //! ### Template Creation and Registration |
131 | //! |
132 | //! Templates are created from `String`s and registered to `Handlebars` with a name. |
133 | //! |
134 | //! ``` |
135 | //! # extern crate handlebars; |
136 | //! |
137 | //! use handlebars::Handlebars; |
138 | //! |
139 | //! # fn main() { |
140 | //! let mut handlebars = Handlebars::new(); |
141 | //! let source = "hello {{world}}" ; |
142 | //! |
143 | //! assert!(handlebars.register_template_string("t1" , source).is_ok()) |
144 | //! # } |
145 | //! ``` |
146 | //! |
147 | //! On registration, the template is parsed, compiled and cached in the registry. So further |
148 | //! usage will benefit from the one-time work. Also features like include, inheritance |
149 | //! that involves template reference requires you to register those template first with |
150 | //! a name so the registry can find it. |
151 | //! |
152 | //! If you template is small or just to experiment, you can use `render_template` API |
153 | //! without registration. |
154 | //! |
155 | //! ``` |
156 | //! # use std::error::Error; |
157 | //! use handlebars::Handlebars; |
158 | //! use std::collections::BTreeMap; |
159 | //! |
160 | //! # fn main() -> Result<(), Box<dyn Error>> { |
161 | //! let mut handlebars = Handlebars::new(); |
162 | //! let source = "hello {{world}}" ; |
163 | //! |
164 | //! let mut data = BTreeMap::new(); |
165 | //! data.insert("world" .to_string(), "世界!" .to_string()); |
166 | //! assert_eq!(handlebars.render_template(source, &data)?, "hello 世界!" .to_owned()); |
167 | //! # Ok(()) |
168 | //! # } |
169 | //! ``` |
170 | //! |
171 | //! #### Additional features for loading template from |
172 | //! |
173 | //! * Feature `dir_source` enables template loading |
174 | //! `register_templates_directory` from given directory. |
175 | //! * Feature `rust-embed` enables template loading |
176 | //! `register_embed_templates` from embedded resources in rust struct |
177 | //! generated with `RustEmbed`. |
178 | //! |
179 | //! ### Rendering Something |
180 | //! |
181 | //! Since handlebars is originally based on JavaScript type system. It supports dynamic features like duck-typing, truthy/falsey values. But for a static language like Rust, this is a little difficult. As a solution, we are using the `serde_json::value::Value` internally for data rendering. |
182 | //! |
183 | //! That means, if you want to render something, you have to ensure the data type implements the `serde::Serialize` trait. Most rust internal types already have that trait. Use `#derive[Serialize]` for your types to generate default implementation. |
184 | //! |
185 | //! You can use default `render` function to render a template into `String`. From 0.9, there's `render_to_write` to render text into anything of `std::io::Write`. |
186 | //! |
187 | //! ``` |
188 | //! # use std::error::Error; |
189 | //! # #[macro_use ] |
190 | //! # extern crate serde_derive; |
191 | //! # extern crate handlebars; |
192 | //! |
193 | //! use handlebars::Handlebars; |
194 | //! |
195 | //! #[derive(Serialize)] |
196 | //! struct Person { |
197 | //! name: String, |
198 | //! age: i16, |
199 | //! } |
200 | //! |
201 | //! # fn main() -> Result<(), Box<dyn Error>> { |
202 | //! let source = "Hello, {{name}}" ; |
203 | //! |
204 | //! let mut handlebars = Handlebars::new(); |
205 | //! assert!(handlebars.register_template_string("hello" , source).is_ok()); |
206 | //! |
207 | //! |
208 | //! let data = Person { |
209 | //! name: "Ning Sun" .to_string(), |
210 | //! age: 27 |
211 | //! }; |
212 | //! assert_eq!(handlebars.render("hello" , &data)?, "Hello, Ning Sun" .to_owned()); |
213 | //! # Ok(()) |
214 | //! # } |
215 | //! # |
216 | //! ``` |
217 | //! |
218 | //! Or if you don't need the template to be cached or referenced by other ones, you can |
219 | //! simply render it without registering. |
220 | //! |
221 | //! ``` |
222 | //! # use std::error::Error; |
223 | //! # #[macro_use ] |
224 | //! # extern crate serde_derive; |
225 | //! # extern crate handlebars; |
226 | //! use handlebars::Handlebars; |
227 | //! # #[derive(Serialize)] |
228 | //! # struct Person { |
229 | //! # name: String, |
230 | //! # age: i16, |
231 | //! # } |
232 | //! |
233 | //! # fn main() -> Result<(), Box<dyn Error>> { |
234 | //! let source = "Hello, {{name}}" ; |
235 | //! |
236 | //! let mut handlebars = Handlebars::new(); |
237 | //! |
238 | //! let data = Person { |
239 | //! name: "Ning Sun" .to_string(), |
240 | //! age: 27 |
241 | //! }; |
242 | //! assert_eq!(handlebars.render_template("Hello, {{name}}" , &data)?, |
243 | //! "Hello, Ning Sun" .to_owned()); |
244 | //! # Ok(()) |
245 | //! # } |
246 | //! ``` |
247 | //! |
248 | //! #### Escaping |
249 | //! |
250 | //! As per the handlebars spec, output using `{{expression}}` is escaped by default (to be precise, the characters `&"<>` are replaced by their respective html / xml entities). However, since the use cases of a rust template engine are probably a bit more diverse than those of a JavaScript one, this implementation allows the user to supply a custom escape function to be used instead. For more information see the `EscapeFn` type and `Handlebars::register_escape_fn()` method. In particular, `no_escape()` can be used as the escape function if no escaping at all should be performed. |
251 | //! |
252 | //! ### Custom Helper |
253 | //! |
254 | //! Handlebars is nothing without helpers. You can also create your own helpers with rust. Helpers in handlebars-rust are custom struct implements the `HelperDef` trait, concretely, the `call` function. For your convenience, most of stateless helpers can be implemented as bare functions. |
255 | //! |
256 | //! ``` |
257 | //! use std::io::Write; |
258 | //! # use std::error::Error; |
259 | //! use handlebars::{Handlebars, HelperDef, RenderContext, Helper, Context, JsonRender, HelperResult, Output, RenderError}; |
260 | //! |
261 | //! // implement by a structure impls HelperDef |
262 | //! #[derive(Clone, Copy)] |
263 | //! struct SimpleHelper; |
264 | //! |
265 | //! impl HelperDef for SimpleHelper { |
266 | //! fn call<'reg: 'rc, 'rc>(&self, h: &Helper, _: &Handlebars, _: &Context, rc: &mut RenderContext, out: &mut dyn Output) -> HelperResult { |
267 | //! let param = h.param(0).unwrap(); |
268 | //! |
269 | //! out.write("1st helper: " )?; |
270 | //! out.write(param.value().render().as_ref())?; |
271 | //! Ok(()) |
272 | //! } |
273 | //! } |
274 | //! |
275 | //! // implement via bare function |
276 | //! fn another_simple_helper (h: &Helper, _: &Handlebars, _: &Context, rc: &mut RenderContext, out: &mut dyn Output) -> HelperResult { |
277 | //! let param = h.param(0).unwrap(); |
278 | //! |
279 | //! out.write("2nd helper: " )?; |
280 | //! out.write(param.value().render().as_ref())?; |
281 | //! Ok(()) |
282 | //! } |
283 | //! |
284 | //! |
285 | //! # fn main() -> Result<(), Box<dyn Error>> { |
286 | //! let mut handlebars = Handlebars::new(); |
287 | //! handlebars.register_helper("simple-helper" , Box::new(SimpleHelper)); |
288 | //! handlebars.register_helper("another-simple-helper" , Box::new(another_simple_helper)); |
289 | //! // via closure |
290 | //! handlebars.register_helper("closure-helper" , |
291 | //! Box::new(|h: &Helper, r: &Handlebars, _: &Context, rc: &mut RenderContext, out: &mut dyn Output| -> HelperResult { |
292 | //! let param = h.param(0).ok_or(RenderError::new("param not found" ))?; |
293 | //! |
294 | //! out.write("3rd helper: " )?; |
295 | //! out.write(param.value().render().as_ref())?; |
296 | //! Ok(()) |
297 | //! })); |
298 | //! |
299 | //! let tpl = "{{simple-helper 1}} \n{{another-simple-helper 2}} \n{{closure-helper 3}}" ; |
300 | //! assert_eq!(handlebars.render_template(tpl, &())?, |
301 | //! "1st helper: 1 \n2nd helper: 2 \n3rd helper: 3" .to_owned()); |
302 | //! # Ok(()) |
303 | //! # } |
304 | //! |
305 | //! ``` |
306 | //! |
307 | //! Data available to helper can be found in [Helper](struct.Helper.html). And there are more |
308 | //! examples in [HelperDef](trait.HelperDef.html) page. |
309 | //! |
310 | //! You can learn more about helpers by looking into source code of built-in helpers. |
311 | //! |
312 | //! |
313 | //! ### Script Helper |
314 | //! |
315 | //! Like our JavaScript counterparts, handlebars allows user to define simple helpers with |
316 | //! a scripting language, [rhai](https://docs.rs/crate/rhai/). This can be enabled by |
317 | //! turning on `script_helper` feature flag. |
318 | //! |
319 | //! A sample script: |
320 | //! |
321 | //! ```handlebars |
322 | //! {{percent 0.34 label="%"}} |
323 | //! ``` |
324 | //! |
325 | //! ```rhai |
326 | //! // percent.rhai |
327 | //! // get first parameter from `params` array |
328 | //! let value = params[0]; |
329 | //! // get key value pair `label` from `hash` map |
330 | //! let label = hash["label"]; |
331 | //! |
332 | //! // compute the final string presentation |
333 | //! (value * 100).to_string() + label |
334 | //! ``` |
335 | //! |
336 | //! A runnable [example](https://github.com/sunng87/handlebars-rust/blob/master/examples/script.rs) can be find in the repo. |
337 | //! |
338 | //! #### Built-in Helpers |
339 | //! |
340 | //! * `{{{{raw}}}} ... {{{{/raw}}}}` escape handlebars expression within the block |
341 | //! * `{{#if ...}} ... {{else}} ... {{/if}}` if-else block |
342 | //! (See [the handlebarjs documentation](https://handlebarsjs.com/guide/builtin-helpers.html#if) on how to use this helper.) |
343 | //! * `{{#unless ...}} ... {{else}} .. {{/unless}}` if-not-else block |
344 | //! (See [the handlebarjs documentation](https://handlebarsjs.com/guide/builtin-helpers.html#unless) on how to use this helper.) |
345 | //! * `{{#each ...}} ... {{/each}}` iterates over an array or object. Handlebars-rust doesn't support mustache iteration syntax so use `each` instead. |
346 | //! (See [the handlebarjs documentation](https://handlebarsjs.com/guide/builtin-helpers.html#each) on how to use this helper.) |
347 | //! * `{{#with ...}} ... {{/with}}` change current context. Similar to `{{#each}}`, used for replace corresponding mustache syntax. |
348 | //! (See [the handlebarjs documentation](https://handlebarsjs.com/guide/builtin-helpers.html#with) on how to use this helper.) |
349 | //! * `{{lookup ... ...}}` get value from array by `@index` or `@key` |
350 | //! (See [the handlebarjs documentation](https://handlebarsjs.com/guide/builtin-helpers.html#lookup) on how to use this helper.) |
351 | //! * `{{> ...}}` include template by its name |
352 | //! * `{{log ...}}` log value with rust logger, default level: INFO. Currently you cannot change the level. |
353 | //! * Boolean helpers that can be used in `if` as subexpression, for example `{{#if (gt 2 1)}} ...`: |
354 | //! * `eq` |
355 | //! * `ne` |
356 | //! * `gt` |
357 | //! * `gte` |
358 | //! * `lt` |
359 | //! * `lte` |
360 | //! * `and` |
361 | //! * `or` |
362 | //! * `not` |
363 | //! * `{{len ...}}` returns length of array/object/string |
364 | //! |
365 | //! ### Template inheritance |
366 | //! |
367 | //! Handlebars.js' partial system is fully supported in this implementation. |
368 | //! Check [example](https://github.com/sunng87/handlebars-rust/blob/master/examples/partials.rs#L49) for details. |
369 | //! |
370 | //! |
371 | |
372 | #![allow (dead_code, clippy::upper_case_acronyms)] |
373 | #![warn (rust_2018_idioms)] |
374 | #![recursion_limit = "200" ] |
375 | |
376 | #[cfg (not(feature = "no_logging" ))] |
377 | #[macro_use ] |
378 | extern crate log; |
379 | |
380 | #[macro_use ] |
381 | extern crate pest_derive; |
382 | #[cfg (test)] |
383 | #[macro_use ] |
384 | extern crate serde_derive; |
385 | |
386 | #[allow (unused_imports)] |
387 | #[macro_use ] |
388 | extern crate serde_json; |
389 | |
390 | pub use self::block::{BlockContext, BlockParams}; |
391 | pub use self::context::Context; |
392 | pub use self::decorators::DecoratorDef; |
393 | pub use self::error::{RenderError, TemplateError}; |
394 | pub use self::helpers::{HelperDef, HelperResult}; |
395 | pub use self::json::path::Path; |
396 | pub use self::json::value::{to_json, JsonRender, PathAndJson, ScopedJson}; |
397 | pub use self::output::{Output, StringOutput}; |
398 | pub use self::registry::{html_escape, no_escape, EscapeFn, Registry as Handlebars}; |
399 | pub use self::render::{Decorator, Evaluable, Helper, RenderContext, Renderable}; |
400 | pub use self::template::Template; |
401 | |
402 | #[doc (hidden)] |
403 | pub use self::serde_json::Value as JsonValue; |
404 | |
405 | #[macro_use ] |
406 | mod macros; |
407 | mod block; |
408 | mod context; |
409 | mod decorators; |
410 | mod error; |
411 | mod grammar; |
412 | mod helpers; |
413 | mod json; |
414 | mod local_vars; |
415 | mod output; |
416 | mod partial; |
417 | mod registry; |
418 | mod render; |
419 | mod sources; |
420 | mod support; |
421 | pub mod template; |
422 | mod util; |
423 | |