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]
378extern crate log;
379
380#[macro_use]
381extern crate pest_derive;
382#[cfg(test)]
383#[macro_use]
384extern crate serde_derive;
385
386#[allow(unused_imports)]
387#[macro_use]
388extern crate serde_json;
389
390pub use self::block::{BlockContext, BlockParams};
391pub use self::context::Context;
392pub use self::decorators::DecoratorDef;
393pub use self::error::{RenderError, TemplateError};
394pub use self::helpers::{HelperDef, HelperResult};
395pub use self::json::path::Path;
396pub use self::json::value::{to_json, JsonRender, PathAndJson, ScopedJson};
397pub use self::output::{Output, StringOutput};
398pub use self::registry::{html_escape, no_escape, EscapeFn, Registry as Handlebars};
399pub use self::render::{Decorator, Evaluable, Helper, RenderContext, Renderable};
400pub use self::template::Template;
401
402#[doc(hidden)]
403pub use self::serde_json::Value as JsonValue;
404
405#[macro_use]
406mod macros;
407mod block;
408mod context;
409mod decorators;
410mod error;
411mod grammar;
412mod helpers;
413mod json;
414mod local_vars;
415mod output;
416mod partial;
417mod registry;
418mod render;
419mod sources;
420mod support;
421pub mod template;
422mod util;
423