]>
Commit | Line | Data |
---|---|---|
8bb4bdeb XL |
1 | //! # Handlebars |
2 | //! | |
3 | //! [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). | |
4 | //! | |
5 | //! And this is handlebars Rust implementation, designed for general purpose text generation. | |
6 | //! | |
7 | //! ## Quick Start | |
8 | //! | |
9 | //! ``` | |
10 | //! use std::collections::BTreeMap; | |
11 | //! use handlebars::Handlebars; | |
12 | //! | |
13 | //! fn main() { | |
14 | //! // create the handlebars registry | |
15 | //! let mut handlebars = Handlebars::new(); | |
16 | //! | |
17 | //! // register the template. The template string will be verified and compiled. | |
18 | //! let source = "hello {{world}}"; | |
19 | //! assert!(handlebars.register_template_string("t1", source).is_ok()); | |
20 | //! | |
21 | //! // Prepare some data. | |
22 | //! // | |
041b39d2 | 23 | //! // The data type should implements `serde::Serialize` |
8bb4bdeb XL |
24 | //! let mut data = BTreeMap::new(); |
25 | //! data.insert("world".to_string(), "世界!".to_string()); | |
26 | //! assert_eq!(handlebars.render("t1", &data).unwrap(), "hello 世界!"); | |
27 | //! } | |
28 | //! ``` | |
29 | //! | |
30 | //! In this example, we created a template registry and registered a template named `t1`. | |
31 | //! Then we rendered a `BTreeMap` with an entry of key `world`, the result is just what | |
32 | //! we expected. | |
33 | //! | |
34 | //! I recommend you to walk through handlebars.js' [intro page](http://handlebarsjs.com) | |
35 | //! if you are not quite familiar with the template language itself. | |
36 | //! | |
37 | //! ## Rational: Why (this) Handlebars? | |
38 | //! | |
39 | //! Handlebars is a real-world templating system that you can use to build | |
40 | //! your application without pain. | |
41 | //! | |
42 | //! ### Features | |
43 | //! | |
44 | //! #### Isolation of Rust and HTML | |
45 | //! | |
46 | //! This library doesn't attempt to use some macro magic to allow you to | |
47 | //! write your template within your rust code. I admit that it's fun (and feel cool) to do | |
48 | //! that but it doesn't fit real-world use case in my opinion. | |
49 | //! | |
50 | //! #### Limited but essential control structure built-in | |
51 | //! | |
52 | //! Only essential control directive `if` and `each` were built-in. This | |
53 | //! prevents you to put too much application logic into your template. | |
54 | //! | |
55 | //! #### Extensible helper system | |
56 | //! | |
57 | //! You can write your own helper with Rust! It can be a block helper or | |
58 | //! inline helper. Put you logic into the helper and don't repeat | |
59 | //! yourself. | |
60 | //! | |
61 | //! #### Template inheritance | |
62 | //! | |
63 | //! Every time I look into a templating system, I will investigate its | |
64 | //! support for [template | |
65 | //! inheritance](https://docs.djangoproject.com/en/1.9/ref/templates/language/#template-inh | |
66 | //! eritance). | |
67 | //! | |
68 | //! Template include is not enough. In most case you will need a skeleton | |
69 | //! of page as parent (header, footer, etc.), and embed you page into this | |
70 | //! parent. | |
71 | //! | |
72 | //! You can find a real example for template inheritance in | |
73 | //! `examples/partials.rs`, and templates used by this file. | |
74 | //! | |
75 | //! ### Limitations | |
76 | //! | |
77 | //! #### Compatibility with JavaScript version | |
78 | //! | |
79 | //! This implementation is **not fully compatible** with the original javascript version. | |
80 | //! | |
81 | //! First of all, mustache block is not supported. I suggest you to use `#if` and `#each` for | |
82 | //! same functionality. | |
83 | //! | |
84 | //! There are some other minor features missing: | |
85 | //! | |
86 | //! * Chained else [#12](https://github.com/sunng87/handlebars-rust/issues/12) | |
87 | //! | |
88 | //! Feel free to fire an issue on [github](https://github.com/sunng87/handlebars-rust/issues) if | |
89 | //! you find missing features. | |
90 | //! | |
91 | //! #### Static typed | |
92 | //! | |
93 | //! As a static typed language, it's a little verbose to use handlebars. | |
041b39d2 XL |
94 | //! Handlebars templating language is designed against JSON data type. In rust, |
95 | //! we will convert user's structs, vectors or maps to JSON type in order to use | |
96 | //! in template. You have to make sure your data implements the `Serialize` trait | |
97 | //! from the [Serde](https://serde.rs) project. | |
8bb4bdeb XL |
98 | //! |
99 | //! ## Usage | |
100 | //! | |
101 | //! ### Template Creation and Registration | |
102 | //! | |
103 | //! Templates are created from String and registered to `Handlebars` with a name. | |
104 | //! | |
105 | //! ``` | |
106 | //! | |
107 | //! extern crate handlebars; | |
108 | //! | |
109 | //! use handlebars::Handlebars; | |
110 | //! | |
111 | //! fn main() { | |
112 | //! let mut handlebars = Handlebars::new(); | |
113 | //! let source = "hello {{world}}"; | |
114 | //! | |
115 | //! assert!(handlebars.register_template_string("t1", source).is_ok()) | |
116 | //! } | |
117 | //! ``` | |
118 | //! | |
119 | //! On registeration, the template is parsed, compiled and cached in the registry. So further | |
120 | //! usage will benifite from the one-time work. Also features like include, inheritance | |
121 | //! that involves template reference requires you to register those template first with | |
122 | //! a name so the registry can find it. | |
123 | //! | |
124 | //! If you template is small or just to expirement, you can use `template_render` API | |
125 | //! without registration. | |
126 | //! | |
127 | //! ``` | |
128 | //! use handlebars::Handlebars; | |
129 | //! use std::collections::BTreeMap; | |
130 | //! | |
131 | //! fn main() { | |
132 | //! let mut handlebars = Handlebars::new(); | |
133 | //! let source = "hello {{world}}"; | |
134 | //! | |
135 | //! let mut data = BTreeMap::new(); | |
136 | //! data.insert("world".to_string(), "世界!".to_string()); | |
137 | //! assert_eq!(handlebars.template_render(source, &data).unwrap(),"hello 世界!".to_owned()); | |
138 | //! } | |
139 | //! ``` | |
140 | //! | |
141 | //! ### Rendering Something | |
142 | //! | |
041b39d2 | 143 | //! 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. |
8bb4bdeb | 144 | //! |
041b39d2 | 145 | //! 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. |
8bb4bdeb XL |
146 | //! |
147 | //! You can use default `render` function to render a template into `String`. From 0.9, there's `renderw` to render text into anything of `std::io::Write`. | |
148 | //! | |
149 | //! ```ignore | |
8bb4bdeb XL |
150 | //! use std::collections::BTreeMap; |
151 | //! | |
152 | //! use handlebars::Handlebars; | |
153 | //! | |
041b39d2 | 154 | //! #[derive(Serialize)] |
8bb4bdeb XL |
155 | //! struct Person { |
156 | //! name: String, | |
157 | //! age: i16, | |
158 | //! } | |
159 | //! | |
8bb4bdeb XL |
160 | //! fn main() { |
161 | //! let source = "Hello, {{name}}"; | |
162 | //! | |
163 | //! let mut handlebars = Handlebars::new(); | |
164 | //! assert!(handlebars.register_template_string("hello", source).is_ok()); | |
165 | //! | |
166 | //! | |
167 | //! let data = Person { | |
168 | //! name: "Ning Sun".to_string(), | |
169 | //! age: 27 | |
170 | //! }; | |
171 | //! assert_eq!(handlebars.render("hello", &data).unwrap(), "Hello, Ning Sun".to_owned()); | |
172 | //! } | |
173 | //! ``` | |
174 | //! | |
175 | //! Or if you don't need the template to be cached or referenced by other ones, you can | |
176 | //! simply render it without registering. | |
177 | //! | |
178 | //! ```ignore | |
179 | //! fn main() { | |
180 | //! let source = "Hello, {{name}}"; | |
181 | //! | |
182 | //! let mut handlebars = Handlebars::new(); | |
183 | //! | |
184 | //! let data = Person { | |
185 | //! name: "Ning Sun".to_string(), | |
186 | //! age: 27 | |
187 | //! }; | |
188 | //! assert_eq!(handlebars.template_render("Hello, {{name}}", &data).unwrap(), | |
189 | //! "Hello, Ning Sun".to_owned()); | |
190 | //! } | |
191 | //! ``` | |
192 | //! | |
193 | //! #### Escaping | |
194 | //! | |
195 | //! 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. | |
196 | //! | |
197 | //! ### Custom Helper | |
198 | //! | |
199 | //! 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. | |
200 | //! | |
201 | //! ``` | |
202 | //! use std::io::Write; | |
203 | //! use handlebars::{Handlebars, HelperDef, RenderError, RenderContext, Helper, Context, JsonRender}; | |
204 | //! | |
205 | //! // implement by a structure impls HelperDef | |
206 | //! #[derive(Clone, Copy)] | |
207 | //! struct SimpleHelper; | |
208 | //! | |
209 | //! impl HelperDef for SimpleHelper { | |
210 | //! fn call(&self, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> { | |
211 | //! let param = h.param(0).unwrap(); | |
212 | //! | |
213 | //! try!(rc.writer.write("1st helper: ".as_bytes())); | |
214 | //! try!(rc.writer.write(param.value().render().into_bytes().as_ref())); | |
215 | //! Ok(()) | |
216 | //! } | |
217 | //! } | |
218 | //! | |
219 | //! // implement via bare function | |
220 | //! fn another_simple_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> { | |
221 | //! let param = h.param(0).unwrap(); | |
222 | //! | |
223 | //! try!(rc.writer.write("2nd helper: ".as_bytes())); | |
224 | //! try!(rc.writer.write(param.value().render().into_bytes().as_ref())); | |
225 | //! Ok(()) | |
226 | //! } | |
227 | //! | |
228 | //! | |
229 | //! fn main() { | |
230 | //! let mut handlebars = Handlebars::new(); | |
231 | //! handlebars.register_helper("simple-helper", Box::new(SimpleHelper)); | |
232 | //! handlebars.register_helper("another-simple-helper", Box::new(another_simple_helper)); | |
233 | //! // via closure | |
234 | //! handlebars.register_helper("closure-helper", | |
235 | //! Box::new(|h: &Helper, r: &Handlebars, rc: &mut RenderContext| -> Result<(), RenderError>{ | |
236 | //! let param = h.param(0).unwrap(); | |
237 | //! | |
238 | //! try!(rc.writer.write("3rd helper: ".as_bytes())); | |
239 | //! try!(rc.writer.write(param.value().render().into_bytes().as_ref())); | |
240 | //! Ok(()) | |
241 | //! })); | |
242 | //! | |
243 | //! let tpl = "{{simple-helper 1}}\n{{another-simple-helper 2}}\n{{closure-helper 3}}"; | |
244 | //! assert_eq!(handlebars.template_render(tpl, &()).unwrap(), | |
245 | //! "1st helper: 1\n2nd helper: 2\n3rd helper: 3".to_owned()); | |
246 | //! } | |
247 | //! ``` | |
248 | //! Data available to helper can be found in [Helper](struct.Helper.html). And there are more | |
249 | //! examples in [HelperDef](trait.HelperDef.html) page. | |
250 | //! | |
251 | //! You can learn more about helpers by looking into source code of built-in helpers. | |
252 | //! | |
253 | //! #### Built-in Helpers | |
254 | //! | |
255 | //! * `{{#raw}} ... {{/raw}}` escape handlebars expression within the block | |
256 | //! * `{{#if ...}} ... {{else}} ... {{/if}}` if-else block | |
257 | //! * `{{#unless ...}} ... {{else}} .. {{/unless}}` if-not-else block | |
258 | //! * `{{#each ...}} ... {{/each}}` iterates over an array or object. Handlebar-rust doesn't support mustach iteration syntax so use this instead. | |
259 | //! * `{{#with ...}} ... {{/with}}` change current context. Similar to {{#each}}, used for replace corresponding mustach syntax. | |
260 | //! * `{{lookup ... ...}}` get value from array by `@index` or `@key` | |
261 | //! * `{{> ...}}` include template with name | |
262 | //! * `{{log ...}}` log value with rust logger, default level: INFO. Currently you cannot change the level. | |
263 | //! | |
264 | //! ### Template inheritance | |
265 | //! | |
266 | //! Handlebarsjs partial system is fully supported in this implementation. | |
267 | //! Check [example](https://github.com/sunng87/handlebars-rust/blob/master/examples/partials.rs#L49) for detail. | |
268 | //! | |
269 | //! | |
270 | ||
271 | #![allow(dead_code)] | |
272 | #![recursion_limit = "200"] | |
273 | ||
274 | #[macro_use] | |
275 | extern crate log; | |
276 | #[macro_use] | |
277 | extern crate quick_error; | |
278 | #[macro_use] | |
279 | extern crate pest; | |
280 | #[macro_use] | |
281 | extern crate lazy_static; | |
282 | ||
8bb4bdeb XL |
283 | #[cfg(test)] |
284 | #[macro_use] | |
285 | extern crate maplit; | |
041b39d2 XL |
286 | #[cfg(test)] |
287 | #[macro_use] | |
288 | extern crate serde_derive; | |
8bb4bdeb | 289 | |
041b39d2 XL |
290 | extern crate regex; |
291 | extern crate serde; | |
292 | #[allow(unused_imports)] | |
293 | #[macro_use] | |
8bb4bdeb XL |
294 | extern crate serde_json; |
295 | ||
296 | pub use self::template::Template; | |
297 | pub use self::error::{TemplateError, TemplateFileError, TemplateRenderError}; | |
298 | pub use self::registry::{EscapeFn, no_escape, html_escape, Registry as Handlebars}; | |
299 | pub use self::render::{Renderable, Evaluable, RenderError, RenderContext, Helper, ContextJson, | |
300 | Directive as Decorator}; | |
301 | pub use self::helpers::HelperDef; | |
302 | pub use self::directives::DirectiveDef as DecoratorDef; | |
303 | pub use self::context::{Context, JsonRender, to_json}; | |
304 | ||
305 | mod grammar; | |
306 | mod template; | |
307 | mod error; | |
308 | mod registry; | |
309 | mod render; | |
310 | mod helpers; | |
311 | mod context; | |
312 | mod support; | |
313 | mod directives; | |
314 | #[cfg(not(feature="partial_legacy"))] | |
315 | mod partial; |