1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
//! # Horrorshow
//!
//! An html templating library.
//!
//! ## Example:
//!
//! ```
//! # #[macro_use] extern crate horrorshow;
//! # fn main() {
//! use horrorshow::prelude::*;
//! use horrorshow::helper::doctype;
//! let actual = html! {
//!     : doctype::HTML;
//!     html {
//!         head {
//!             title : "Hello world!";
//!         }
//!         body {
//!             // attributes
//!             h1(id="heading") {
//!                 // Insert escaped text
//!                 : "Hello! This is <html />"
//!             }
//!             p {
//!                 // Insert raw text (unescaped)
//!                 : Raw("Let's <i>count</i> to 10!")
//!             }
//!             ol(id="count") {
//!                 // You can embed for loops, while loops, and if statements.
//!                 @ for i in 0..10 {
//!                     li(first? = (i == 0)) {
//!                         // Format some text.
//!                         : format_args!("{}", i+1)
//!                     }
//!                 }
//!             }
//!             // You need semi-colons for tags without children.
//!             br; br;
//!             p {
//!                 // You can also embed closures.
//!                 |tmpl| {
//!                     tmpl << "Easy!";
//!                 }
//!             }
//!         }
//!     }
//! }.into_string().unwrap();
//!
//! let expected = "\
//! <!DOCTYPE html>\
//! <html>\
//!   <head>\
//!     <title>Hello world!</title>\
//!   </head>\
//!   <body>\
//!     <h1 id=\"heading\">Hello! This is &lt;html /&gt;</h1>\
//!     <p>Let's <i>count</i> to 10!</p>\
//!     <ol id=\"count\">\
//!       <li first>1</li>\
//!       <li>2</li>\
//!       <li>3</li>\
//!       <li>4</li>\
//!       <li>5</li>\
//!       <li>6</li>\
//!       <li>7</li>\
//!       <li>8</li>\
//!       <li>9</li>\
//!       <li>10</li>\
//!     </ol>\
//!     <br /><br />\
//!     <p>Easy!</p>\
//!   </body>\
//! </html>";
//! assert_eq!(expected, actual);
//!
//! # }
//! ```
//!
//! ## Usage
//!
//! ```
//! #[macro_use]
//! extern crate horrorshow;
//! # fn main() {}
//! ```
//!
//! Inside an html template, the following expressions are valid:
//!
//! * `some_tag;` -- Insert a the tag `some_tag`.
//!
//! * `some_tag(attr=rust_expresion,...);` -- Insert a the tag `some_tag` with the specified
//!    attributes. The attribute values will be evaluated as rust expressions at runtime and they
//!    must implement `RenderOnce` (already implemented on &str, String, other templates, etc.).
//!
//! * `some_tag(attr,...);` -- You can also omit the value.
//!
//! * `some_tag(attr? = Some("test"),...);` -- You can optionally include an attribute.
//!
//! * `some_tag(attr? = some_boolean,...);` -- You can optionally include an attribute without a value.
//!
//! * `some_tag { ... }` -- Insert the tag `some_tag` and recursively evaluate the `...`.
//!
//! * `some_tag(...) { ... }` -- Same as above but with custom attributes.
//!
//! * `: rust_expression`, `: { rust_code }` -- Evaluate the expression or block and insert result
//! current position. To insert a literal html (unescaped), mark it as raw with the `Raw` marker type.
//!
//! * `|tmpl| rust_expression`, `|tmpl| { rust_code }` -- Evaluate the expression or block. This is
//! actually a closure so the block/expression can append to the current template through `tmpl`
//! (of type `&mut TemplateBuffer`).
//!
//! * `@ for ...`, `@ while ...`, `@ if ...` -- you can embed basic control flow expressions.
//!
//! ## Traits, traits oh-my!
//!
//! You will likely notice that there are four render traits:
//!
//! 1. `RenderOnce`
//! 2. `RenderMut`
//! 3. `Render`
//! 4. `RenderBox`
//!
//! These three traits map to the four `Fn` traits and reflect the fact that some templates need
//! exclusive access (`RenderMut`) in order to be rendered and others might even consume their
//! environment (`RenderOnce`).
//!
//! In general, just import `Template` into your environment (or import the prelude).
//!
//! ## Error Handling
//!
//! Both render and IO errors are handled in the background. If an io (or fmt) error occurs,
//! template rendering will continue but no more data will be written and the original `write_to_*`
//! call will return the error when rendering terminates. If you need to record a render error, use
//! `TemplateBuffer::record_error`. As with IO errors, custom errors DO NOT cause rendering to be
//! aborted. Instead, all recorded errors (if any) are returned when rendering completes.
//!
//! TL;DR: There is no way to abort rendering but you can report errors.
//!
//! ## Escaping
//!
//! This library does HTML escaping by default. However, it doesn't do any javascript/URL escaping.
//! Furthermore, it will do html escaping in any literal javascript you might include.
//!
//! For example, the following will display an alert:
//!
//! ```
//! # #[macro_use]
//! # extern crate horrorshow;
//! # fn main() {
//! html! {
//!   script {
//!     : "alert('hello');"
//!   }
//! }
//! # ;
//! # }
//! ```
//!
//! The following will break due to html escaping (the `"` will be escaped to `&quot;`):
//!
//! ```
//! # #[macro_use]
//! # extern crate horrorshow;
//! # fn main() {
//! html! {
//!   script {
//!     : "alert(\"hello\");"
//!   }
//! }
//! # ;
//! # }
//! ```
//!
//! And the following will display as-is (but won't run any javascript) due to the HTML escaping:
//!
//! ```
//! # #[macro_use]
//! # extern crate horrorshow;
//! # fn main() {
//! html! {
//!     : "<script>alert(\"hello\");</script>"
//! }
//! # ;
//! # }
//! ```
//!
//! Output:
//!
//! ```html
//! &lt;script&gt;alert(&quot;hello&quot;);&lt;/script&gt;
//! ```
//!
//! ## Returning Templates
//!
//! To return a template directly, you have to create it using the `box_html!` macro instead of the
//! `html!` macro. The template type will be one of `Box<RenderBox>` (can be rendered once),
//! `Box<RenderMut>`, or `Box<Render>` depending on how the template affects its environment.
//!
//! ```
//! #[macro_use]
//! extern crate horrorshow;
//!
//! use horrorshow::{RenderOnce, RenderBox, RenderMut, Render};
//!
//! // Consume the environment
//! fn identity<T: RenderOnce + 'static>(something: T) -> Box<RenderBox + 'static> {
//!     box_html! {
//!         : something
//!     }
//! }
//!
//! // Mutate the environment
//! fn counter() -> Box<RenderMut> {
//!     let mut counter = 0;
//!     box_html! {
//!         |t| {
//!             write!(t, "{}", counter);
//!             counter += 1;
//!         }
//!     }
//! }
//!
//! // Borrow the environment.
//! fn say_hello(name: String) -> Box<Render> {
//!     let mut counter = 0;
//!     box_html! {
//!         span {
//!             : "Hello ";
//!             : &name;
//!             : ".";
//!         }
//!     }
//! }
//!
//! # fn main() {}
//! ```
//!
//! *Note*: To avoid allocating, you can implement render manually instead of returning a boxed
//! template:
//!
//! ```
//! #[macro_use]
//! extern crate horrorshow;
//!
//! use horrorshow::{RenderOnce, TemplateBuffer, Template};
//!
//! struct Page<C> {
//!     title: String,
//!     content: C,
//! }
//!
//! impl Page<String> {
//!     fn from_string_content(title: String, content: String) -> Self {
//!         Page { title: title, content: content }
//!     }
//! }
//!
//! impl<C> RenderOnce for Page<C> where C: RenderOnce {
//!     fn render_once(self, tmpl: &mut TemplateBuffer) {
//!         let Page {title, content} = self;
//!         // The actual template:
//!         tmpl << html! {
//!             article {
//!                 header {
//!                     h1 : title
//!                 }
//!                 section : content
//!             }
//!         };
//!     }
//! }
//!
//! fn main() {
//!   let page = Page::from_string_content(String::from("My title"),
//!                                        String::from("Some content."));
//!   assert_eq!(page.into_string().unwrap(),
//!              "<article>\
//!                 <header><h1>My title</h1></header>\
//!                 <section>Some content.</section>\
//!               </article>");
//! }
//! ```
//!
//! # Examples
//!
//! See the test cases.
#[macro_use]
mod macros;

#[cfg(feature = "ops")]
mod ops;

mod error;
pub use error::Error;

mod template;
pub use template::{TemplateBuffer, Template};
mod render;
pub use render::{RenderOnce, RenderMut, Render, RenderBox, FnRenderer, Raw};

/// Traits that should always be imported.
pub mod prelude;

pub mod helper;

/// Helper trait for dispatching `attr ?= `.
///
/// attr ?= Some("test") -> attr="test"
/// attr ?= true -> attr
#[doc(hidden)]
pub trait BoolOption: Sized {
    type Value;
    fn bool_option(self) -> (bool, Option<Self::Value>);
}

impl<T> BoolOption for Option<T> {
    type Value = T;
    #[inline]
    fn bool_option(self) -> (bool, Option<T>) {
        (false, self)
    }
}

// Need &str because Value needs to implement RenderOnce (even though we never actually render
// it...)
impl BoolOption for bool {
    type Value = &'static str;
    #[inline]
    fn bool_option(self) -> (bool, Option<&'static str>) {
        (true, if self { Some("") } else { None })
    }
}