Skip to main content

horrorshow/
macros.rs

1// rustfmt doesn't like this file (lines too long, too hard to fix)
2#![cfg_attr(rustfmt, rustfmt_skip)]
3
4/// Create a new HTML template.
5#[macro_export]
6macro_rules! html {
7    ($($inner:tt)*) => {{
8        // Define this up here to prevent rust from saying:
9        // Hey look, it's an FnOnce (this could be Fn/FnMut).
10        let f = |__tmpl: &mut $crate::TemplateBuffer| -> () {
11            $crate::append_html!(__tmpl, html, (), $($inner)*);
12        };
13        // Stringify the template content to get a hint at how much we should allocate...
14        $crate::FnRenderer::with_capacity(stringify!($($inner)*).len(), f)
15    }}
16}
17
18/// Create a new XML template.
19#[macro_export]
20macro_rules! xml {
21    ($($inner:tt)*) => {{
22        let f = |__tmpl: &mut $crate::TemplateBuffer| -> () {
23            $crate::append_html!(__tmpl, xml, (), $($inner)*);
24        };
25        $crate::FnRenderer::with_capacity(stringify!($($inner)*).len(), f)
26    }}
27}
28
29/// Create a new HTML template taking ownership of any variables used inside.
30#[macro_export]
31macro_rules! owned_html {
32    ($($inner:tt)*) => {{
33        // Define this up here to prevent rust from saying:
34        // Hey look, it's an FnOnce (this could be Fn/FnMut).
35        let f = move |__tmpl: &mut $crate::TemplateBuffer| -> () {
36            $crate::append_html!(__tmpl, html, (), $($inner)*);
37        };
38        // Stringify the template content to get a hint at how much we should allocate...
39        $crate::FnRenderer::with_capacity(stringify!($($inner)*).len(), f)
40    }}
41}
42
43/// Create a new XML template taking ownership of any variables used inside.
44#[macro_export]
45macro_rules! owned_xml {
46    ($($inner:tt)*) => {{
47        let f = move |__tmpl: &mut $crate::TemplateBuffer| -> () {
48            $crate::append_html!(__tmpl, xml, (), $($inner)*);
49        };
50        $crate::FnRenderer::with_capacity(stringify!($($inner)*).len(), f)
51    }}
52}
53
54/// Create a new owned html template.
55///
56/// This template will be boxed and will own it's environment. If you need to return a template
57/// from a function, use this.
58///
59/// Example:
60///
61/// ```rust
62/// # #[macro_use]
63/// # extern crate horrorshow;
64///
65/// # fn main() {
66/// fn post<'a>(title: &'a str) -> Box<horrorshow::RenderBox + 'a> {
67///     box_html! {
68///         article {
69///             title { h1 : title }
70///             p : "This is one paragraph.";
71///             p : "This is a second.";
72///         }
73///     }
74/// }
75/// println!("{}", html! {
76///     html {
77///         body {
78///             : post("First Post");
79///             |t| for i in 0..10 {
80///                 // Waiting for non-lexical borrows!!!!
81///                 let tmp = format!("Spam post {}", i);
82///                 let post = post(&tmp);
83///                 &mut *t << post;
84///             };
85///         }
86///     }
87/// });
88/// # }
89/// ```
90#[macro_export]
91macro_rules! box_html {
92    ($($inner:tt)*) => {{
93        Box::new($crate::owned_html!($($inner)*))
94    }}
95}
96
97/// Append html to the current template.
98/// Don't call this manually.
99#[macro_export]
100macro_rules! append_html {
101
102    // Nop out close-tags for void elements.
103    (@close_tag html area) => {">"};
104    (@close_tag html base) => {">"};
105    (@close_tag html br) => {">"};
106    (@close_tag html col) => {">"};
107    (@close_tag html embed) => {">"};
108    (@close_tag html hr) => {">"};
109    (@close_tag html img) => {">"};
110    (@close_tag html input) => {">"};
111    (@close_tag html link) => {">"};
112    (@close_tag html meta) => {">"};
113    (@close_tag html param) => {">"};
114    (@close_tag html source) => {">"};
115    (@close_tag html track) => {">"};
116    (@close_tag html wbr) => {">"};
117    (@close_tag html $($tag:ident)-+) => {
118        concat!("></", $crate::append_html!(@stringify_compressed $($tag)-+), ">");
119    };
120    (@close_tag xml $($tag:ident)-+) => {
121        "/>"
122    };
123    (@stringify_compressed $($tok:tt)*) => {
124        concat!($(stringify!($tok)),*)
125    };
126
127    (@block_identity $b:block) => { $b };
128    (@cont $tmpl:ident, $type:ident, ($s:stmt), $($next:tt)*) => {
129        $s;
130        $crate::append_html!($tmpl, $type, (), $($next)*);
131    };
132    (@write_const $tmpl:ident, $type:ident,) => {};
133    (@write_const $tmpl:ident, $type:ident, $($p:expr),+) => {
134        $tmpl.write_raw(concat!($($p),*));
135    };
136    (@expr_and_block $tmpl:ident, $type:ident, parse_match_block, (match $($prefix:tt)*), {$($pattern:pat => {$($inner:tt)*})*} $($next:tt)*) => {
137        $crate::append_html!(@parse_match_block $tmpl, $type, (match ($($prefix)*) {$($pattern => {$($inner)*})*}), $($next)*);
138    };
139    (@expr_and_block $tmpl:ident, $type:ident, $goto:ident, ($($prefix:tt)*), {$($inner:tt)*} $($next:tt)*) => {
140        $crate::append_html!(@$goto $tmpl, $type, ($($prefix)* {$crate::append_html!($tmpl, $type, (), $($inner)*);}), $($next)*);
141    };
142    (@expr_and_block $tmpl:ident, $type:ident, $goto:ident, ($($prefix:tt)*), $first:tt $($next:tt)*) => {
143        $crate::append_html!(@expr_and_block $tmpl, $type, $goto, ($($prefix)* $first), $($next)*);
144    };
145    (@append_attrs $tmpl:ident, $type:ident, ($($p:expr),*), $($($attr:ident)-+):+ ?= $value:expr, $($rest:tt)+) => {
146        $crate::append_html!(@append_attrs $tmpl, $type, ($($p),*), $($($attr)-+):+ ?= $value);
147        $crate::append_html!(@append_attrs $tmpl, $type, (), $($rest)+);
148    };
149    (@append_attrs $tmpl:ident, $type:ident, ($($p:expr),*), $($($attr:ident)-+):+ ?= $value:expr) => {
150        match $crate::BoolOption::bool_option($value) {
151            (_, None) => {
152                $crate::append_html!(@write_const $tmpl, $type, $($p),*);
153            },
154            (true, Some(_)) => { $crate::append_html!(@append_attrs $tmpl, $type, ($($p),*), $($($attr)-+):+); }
155            (false, Some(v)) => { $crate::append_html!(@append_attrs $tmpl, $type, ($($p),*), $($($attr)-+):+ = v); }
156        };
157    };
158    (@append_attrs $tmpl:ident, $type:ident, ($($p:expr),*), $($($attr:ident)-+):+ = $value:expr, $($rest:tt)+) => {
159        $crate::append_html!(@append_attrs $tmpl, $type, ($($p),*), $($($attr)-+):+ = $value);
160        $crate::append_html!(@append_attrs $tmpl, $type, (), $($rest)+);
161    };
162    (@append_attrs $tmpl:ident, $type:ident, ($($p:expr),*), $($($attr:ident)-+):+, $($rest:tt)+) => {
163        $crate::append_html!(@append_attrs $tmpl, $type, ($($p),*), $($($attr)-+):+);
164        $crate::append_html!(@append_attrs $tmpl, $type, (), $($rest)+);
165    };
166    (@append_attrs $tmpl:ident, $type:ident, ($($p:expr),*), $($($attr:ident)-+):+ = $value:expr) => {
167        $tmpl.write_raw(concat!($($p,)* " ", $crate::append_html!(@stringify_compressed $($($attr)-+):+), "=\""));
168        $crate::RenderOnce::render_once($value, $tmpl);
169        $tmpl.write_raw("\"");
170    };
171    (@append_attrs $tmpl:ident, html, ($($p:expr),*), $($($attr:ident)-+):+) => {
172        $tmpl.write_raw(concat!($($p,)* " ", $crate::append_html!(@stringify_compressed $($($attr)-+):+)));
173    };
174    (@append_attrs $tmpl:ident, xml, ($($p:expr),*), $($($attr:ident)-+):+) => {{
175        $tmpl.write_raw(concat!($($p,)* " ",
176            $crate::append_html!(@stringify_compressed $($($attr)-+):+),
177            "=\"",
178            $crate::append_html!(@stringify_compressed $($($attr)-+):+),
179            "\""
180        ));
181    }};
182    //////// IF CHAINS
183    //// Begin
184    (@parse_if $tmpl:ident, $type:ident, ($($prefix:tt)*), if let $v:pat = $e:tt $($next:tt)+) => {
185        $crate::append_html!(@expr_and_block $tmpl, $type, parse_if_block, ($($prefix)* if let $v = $e), $($next)+);
186    };
187    (@parse_if $tmpl:ident, $type:ident, ($($prefix:tt)*), if $e:tt $($next:tt)+) => {
188        $crate::append_html!(@expr_and_block $tmpl, $type, parse_if_block, ($($prefix)* if $e), $($next)+);
189    };
190    //// End
191    // Else if
192    (@parse_if_block $tmpl:ident, $type:ident, ($($prefix:tt)*), else if $($next:tt)*) => {
193        $crate::append_html!(@parse_if $tmpl, $type, ($($prefix)* else), if $($next)*);
194    };
195    // Else
196    (@parse_if_block $tmpl:ident, $type:ident, ($($prefix:tt)*), else {$($inner:tt)*} $($next:tt)*) => {
197        $crate::append_html!(@cont $tmpl, $type, ($($prefix)* else {$crate::append_html!($tmpl, $type, (), $($inner)*);}), $($next)*);
198    };
199    // No else.
200    (@parse_if_block $tmpl:ident, $type:ident, ($($prefix:tt)*), $($next:tt)*) => {
201        $crate::append_html!(@cont $tmpl, $type, ($($prefix)*), $($next)*);
202    };
203    //////// MATCH
204    (@parse_match_block $tmpl:ident, $type:ident, (match ($($prefix:tt)*) {$($pattern:pat => {$($inner:tt)*})*}), $($next:tt)*) => {
205        $crate::append_html!(@cont $tmpl, $type, (match ($($prefix)*) {$(
206            $pattern => { $crate::append_html!($tmpl, $type, (), $($inner)*); }
207        )*}), $($next)*);
208    };
209    //// Condition
210    ($tmpl:ident, $type:ident, ($($p:expr),*), @ if $($next:tt)+) => {
211        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
212        $crate::append_html!(@parse_if $tmpl, $type, (), if $($next)*);
213    };
214    ($tmpl:ident, $type:ident, ($($p:expr),*), @ for $v:pat in $e:tt $($next:tt)*) => {
215        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
216        $crate::append_html!(@expr_and_block $tmpl, $type, cont, (for $v in $e), $($next)*);
217    };
218    ($tmpl:ident, $type:ident, ($($p:expr),*), @ while let $v:pat = $e:tt $($next:tt)*) => {
219        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
220        $crate::append_html!(@expr_and_block $tmpl, $type, cont, (while let $v = $e), $($next)*);
221    };
222    ($tmpl:ident, $type:ident, ($($p:expr),*), @ while $e:tt $($next:tt)*) => {
223        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
224        $crate::append_html!(@expr_and_block $tmpl, $type, cont, (while $e), $($next)*);
225    };
226    ($tmpl:ident, $type:ident, ($($p:expr),*), @ match $e:tt $($next:tt)+) => {
227        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
228        $crate::append_html!(@expr_and_block $tmpl, $type, parse_match_block, (match $e), $($next)*);
229    };
230    ($tmpl:ident, $type:ident, ($($p:expr),*), : {$($code:tt)*} $($next:tt)*) => {
231        $crate::append_html!(@write_const, $tmpl, $type, $($p),*);
232        $crate::RenderOnce::render_once({$($code)*}, $tmpl);
233        $crate::append_html!($tmpl, $type, (), $($next)*);
234    };
235    ($tmpl:ident, $type:ident, ($($p:expr),*), : $code:expr; $($next:tt)* ) => {
236        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
237        $crate::RenderOnce::render_once($code, $tmpl);
238        $crate::append_html!($tmpl, $type, (), $($next)*);
239    };
240    ($tmpl:ident, $type:ident, ($($p:expr),*), : $code:expr ) => {
241        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
242        $crate::RenderOnce::render_once($code, $tmpl);
243    };
244    ($tmpl:ident, $type:ident, ($($p:expr),*), |$var:ident| {$($code:tt)*} $($next:tt)*) => {
245        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
246        {
247            let $var: &mut $crate::TemplateBuffer = &mut *$tmpl;
248            $crate::append_html!(@block_identity {$($code)*})
249        }
250        $crate::append_html!($tmpl, $type, (), $($next)*);
251    };
252    ($tmpl:ident, $type:ident, ($($p:expr),*), |mut $var:ident| {$($code:tt)*} $($next:tt)*) => {
253        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
254        {
255            let mut $var: &mut $crate::TemplateBuffer = &mut *$tmpl;
256            $crate::append_html!(@block_identity {$($code)*})
257        }
258        $crate::append_html!($tmpl, $type, (), $($next)*);
259    };
260    ($tmpl:ident, $type:ident, ($($p:expr),*), |$var:ident| $code:stmt; $($next:tt)* ) => {
261        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
262        {
263            let $var: &mut $crate::TemplateBuffer = &mut *$tmpl;
264            $code;
265        }
266        $crate::append_html!($tmpl, $type, (), $($next)*);
267    };
268    ($tmpl:ident, $type:ident, ($($p:expr),*), |mut $var:ident| $code:stmt; $($next:tt)* ) => {
269        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
270        {
271            let mut $var: &mut $crate::TemplateBuffer = &mut *$tmpl;
272            $code;
273        }
274        $crate::append_html!($tmpl, $type, (), $($next)*);
275    };
276    ($tmpl:ident, $type:ident, ($($p:expr),*), |$var:ident| $code:stmt ) => {{
277        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
278        let $var: &mut $crate::TemplateBuffer = &mut *$tmpl;
279        $code;
280    }};
281    ($tmpl:ident, $type:ident, ($($p:expr),*), |mut $var:ident| $code:stmt ) => {{
282        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
283        let mut $var: &mut $crate::TemplateBuffer = &mut *$tmpl;
284        $code;
285    }};
286    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+($($attrs:tt)+) { $($children:tt)* } $($next:tt)* ) => {
287        $crate::append_html!(@append_attrs $tmpl, $type, ($($p,)* "<", $crate::append_html!(@stringify_compressed $($tag)-+)), $($attrs)+);
288        $crate::append_html!($tmpl, $type, (">"), $($children)*);
289        $crate::append_html!($tmpl, $type, ("</", $crate::append_html!(@stringify_compressed $($tag)-+), ">"), $($next)*);
290    };
291    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+($($attr:tt)+) : $e:expr; $($next:tt)* ) => {
292        $crate::append_html!($tmpl, $type, ($($p),*), $($tag)-+($($attr)+) { : $e; } $($next)* );
293    };
294    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+($($attr:tt)+) : $e:expr) => {
295        $crate::append_html!($tmpl, $type, ($($p),*), $($tag)-+($($attr)+) { : $e });
296    };
297    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+($($attr:tt)+) : {$($code:tt)*} $($next:tt)* ) => {
298        $crate::append_html!($tmpl, $type, ($($p),*), $($tag)-+($($attr)+) { : {$($code)*} } $($next)* );
299    };
300    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+($($attrs:tt)+); $($next:tt)*) => {
301        $crate::append_html!(@append_attrs $tmpl, $type, ($($p,)* "<", $crate::append_html!(@stringify_compressed $($tag)-+)), $($attrs)+);
302        $crate::append_html!($tmpl, $type, ($crate::append_html!(@close_tag $type $($tag)-+)), $($next)*);
303    };
304    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+($($attrs:tt)+)) => {
305        $crate::append_html!(@append_attrs $tmpl, $type, ($($p,)* "<", $crate::append_html!(@stringify_compressed $($tag)-+)), $($attrs)+);
306        $tmpl.write_raw($crate::append_html!(@close_tag $type $($tag)-+));
307    };
308    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+ { $($children:tt)* } $($next:tt)* ) => {
309        $crate::append_html!($tmpl, $type, ($($p,)* "<", $crate::append_html!(@stringify_compressed $($tag)-+), ">"), $($children)*);
310        $crate::append_html!($tmpl, $type, ("</", $crate::append_html!(@stringify_compressed $($tag)-+), ">"), $($next)*);
311    };
312    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+ : $e:expr; $($next:tt)* ) => {
313        $crate::append_html!($tmpl, $type, ($($p),*), $($tag)-+ { : $e; } $($next)* );
314    };
315    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+ : {$($code:tt)*} $($next:tt)* ) => {
316        $crate::append_html!($tmpl, $type, ($($p),*), $($tag)-+ { : {$($code)*} } $($next)* );
317    };
318    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+; $($next:tt)*) => {
319        $crate::append_html!($tmpl, $type, ($($p,)* "<", $crate::append_html!(@stringify_compressed $($tag)-+), $crate::append_html!(@close_tag $type $($tag)-+)), $($next)*);
320    };
321    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+ : $e:expr) => {
322        $crate::append_html!($tmpl, $type, ($($p),*), $($tag)-+ { : $e; });
323    };
324    ($tmpl:ident, $type:ident, ($($p:expr),*), $($tag:ident)-+) => {
325        $crate::append_html!(@write_const $tmpl, $type, $($p,)* "<", $crate::append_html!(@stringify_compressed $($tag)-+), $crate::append_html!(@close_tag $type $($tag)-+));
326    };
327    ($tmpl:ident, $type:ident, ($($p:expr),*),) => {
328        $crate::append_html!(@write_const $tmpl, $type, $($p),*);
329    };
330    ($tmpl:ident, $type:ident, ($($p:expr),*), $t:tt $($tr:tt)*) => {
331        compile_error!(concat!("unexpected token tree: ", stringify!($t), "\n\nYou're probably missing a semicolon somewhere."));
332    }
333}
334
335/// Create a new template.
336///
337/// This allows you to declare a template as follows:
338///
339/// ```
340/// # #[macro_use]
341/// # extern crate horrorshow;
342/// template! {
343///     MyTemplate(name: &str, age: &u32) {
344///         p {
345///            : "Hello, my name is ";
346///            : name;
347///            : " and I am ";
348///            : age;
349///            : " years old.";
350///         }
351///     }
352/// }
353/// # fn main() { }
354/// ```
355///
356/// You can instantiate these templates by calling `new` on them:
357///
358/// ```
359/// # #[macro_use]
360/// # extern crate horrorshow;
361/// # template! {
362/// #     MyTemplate(name: &str, age: &u32) {
363/// #         p {
364/// #            : "Hello, my name is ";
365/// #            : name;
366/// #            : " and I am ";
367/// #            : age;
368/// #            : " years old.";
369/// #         }
370/// #     }
371/// # }
372///
373/// # fn main() {
374/// let age = 42;
375/// let tmpl = MyTemplate::new("Not Me", &age);
376/// # }
377/// ```
378///
379/// These templates never own their content, they just borrow it. This is one of the reasons I call
380/// this feature "experimental".
381#[macro_export]
382macro_rules! template {
383    ($name:ident ($($field:ident : &$typ:ty),*) { $($tmpl:tt)* } $($rest:tt)*) => {
384        struct $name<'a> { $( $field: &'a $typ),* }
385        impl<'a> $name<'a> {
386            pub fn new($($field: &'a $typ),*) -> Self {
387                $name { $( $field: $field),* }
388            }
389        }
390        impl<'a> $crate::RenderOnce for $name<'a> {
391            fn render_once(self, tmpl: &mut $crate::TemplateBuffer) {
392                $crate::Render::render(&self, tmpl);
393            }
394        }
395        impl<'a> $crate::RenderMut for $name<'a> {
396            fn render_mut(&mut self, tmpl: &mut $crate::TemplateBuffer) {
397                $crate::Render::render(self, tmpl);
398            }
399        }
400        impl<'a> $crate::Render for $name<'a> {
401            fn render(&self, tmpl: &mut $crate::TemplateBuffer) {
402                let &$name { $($field),* } = self;
403                tmpl << $crate::html! { $($tmpl)* };
404            }
405        }
406        $crate::template!($($rest)*);
407    };
408    (pub $name:ident ($($field:ident : &$typ:ty),*) { $($tmpl:tt)* } $($rest:tt)*) => {
409        pub struct $name<'a> { $( $field: &'a $typ),* }
410        impl<'a> $name<'a> {
411            pub fn new($($field: &'a $typ),*) -> Self {
412                $name { $( $field: $field),* }
413            }
414        }
415        impl<'a> $crate::RenderOnce for $name<'a> {
416            fn render_once(self, tmpl: &mut $crate::TemplateBuffer) {
417                $crate::Render::render(&self, tmpl);
418            }
419        }
420        impl<'a> $crate::RenderMut for $name<'a> {
421            fn render_mut(&mut self, tmpl: &mut $crate::TemplateBuffer) {
422                $crate::Render::render(self, tmpl);
423            }
424        }
425        impl<'a> $crate::Render for $name<'a> {
426            fn render(&self, tmpl: &mut $crate::TemplateBuffer) {
427                let &$name { $($field),* } = self;
428                tmpl << $crate::html! { $($tmpl)* };
429            }
430        }
431        $crate::template!($($rest)*);
432    };
433    () => {}
434}
435
436/// Utility macro for generating a space-delimited string from a set of labels;
437/// some of which may be conditionally included into the final string.
438/// Labels are anything that implements the `RenderOnce` trait (e.g. `String` or `&str`).
439///
440/// This macro is an alias of: `labels_sep_by!(" "; maybe_label,...)`
441///
442/// Usage: `labels!(maybe_label,...)`
443///
444/// * `maybe_label` -- Either `label_expression`, or `label_expression => cond_test`.
445///
446/// * `label_expression` -- An expression that returns a label that implements
447///   the `RenderOnce` trait (e.g. `String` or `&str`).
448///
449/// * `label_expression => cond_test` -- Conditionally include `label_expression` whenever `cond_test` is `true`.
450///   `cond_test` is an expression that returns either `true` or `false`.
451///
452/// This useful in generating class attribute as follows:
453///
454/// ```
455/// # #[macro_use]
456/// # extern crate horrorshow;
457/// # fn main() {
458/// html! {
459///     div(class = labels!("active" => true, "button-style")) {
460///         : "This is a button"
461///     }
462/// }
463/// # ;
464/// # }
465/// ```
466///
467#[macro_export]
468macro_rules! labels {
469
470    ($($tail:tt)+) => (
471        $crate::labels_sep_by!(" "; $($tail)*)
472    );
473
474}
475
476/// Utility macro for generating a delimited string from a set of labels;
477/// some of which may be conditionally included into the final string.
478/// The delimiter/seperator and labels are anything that implements
479/// the `RenderOnce` trait (e.g. `String` or `&str`).
480///
481///
482/// Usage: `labels_sep_by!(seperator; maybe_label,...)`
483///
484/// * `seperator` -- Delimiter/seperator that implements the `RenderOnce` trait (e.g. `String` or `&str`).
485///
486/// * `maybe_label` -- Either `label_expression`, or `label_expression => cond_test`.
487///
488/// * `label_expression` -- An expression that returns a label that implements
489///   the `RenderOnce` trait (e.g. `String` or `&str`).
490///
491/// * `label_expression => cond_test` -- Conditionally include `label_expression` whenever `cond_test` is `true`.
492///   `cond_test` is an expression that returns either `true` or `false`.
493///
494/// This useful in generating style attribute as follows:
495///
496/// ```
497/// # #[macro_use]
498/// # extern crate horrorshow;
499/// # fn main() {
500/// html! {
501///     div(style = labels_sep_by!(";"; "color: #000" => true, "font-weight: bold")) {
502///         : "This is a button"
503///     }
504/// }
505/// # ;
506/// # }
507/// ```
508///
509#[macro_export]
510macro_rules! labels_sep_by {
511
512    (@inner_expand $has_before:expr; $sep:expr; $tmpl:ident $item:expr) => {
513        if $has_before {
514            $crate::RenderOnce::render_once($sep, $tmpl);
515        }
516        $crate::RenderOnce::render_once($item, $tmpl);
517    };
518
519    (@inner_expand $has_before:expr; $sep:expr; $tmpl:ident $item:expr => $should_include:expr) => {
520        if $should_include {
521            if $has_before {
522                $crate::RenderOnce::render_once($sep, $tmpl);
523            }
524            $crate::RenderOnce::render_once($item, $tmpl);
525        }
526    };
527
528    (@inner_expand $has_before:expr; $sep:expr; $tmpl:ident $item:expr, $($tail:tt)+) => {
529        if $has_before {
530            $crate::RenderOnce::render_once($sep, $tmpl);
531        }
532        $crate::RenderOnce::render_once($item, $tmpl);
533        $crate::labels_sep_by!(@inner_expand true; $sep; $tmpl $($tail)*);
534    };
535
536    (@inner_expand $has_before:expr; $sep:expr; $tmpl:ident $item:expr => $should_include:expr, $($tail:tt)+) => {
537        if $should_include {
538            if $has_before {
539                $crate::RenderOnce::render_once($sep, $tmpl);
540            }
541            $crate::RenderOnce::render_once($item, $tmpl);
542        }
543        $crate::labels_sep_by!(@inner_expand $has_before || $should_include; $sep; $tmpl $($tail)*);
544    };
545
546    // entries
547
548    ($sep:expr; $item:expr) => {
549
550        $crate::FnRenderer::new(|tmpl| {
551            $crate::RenderOnce::render_once($item, tmpl);
552        })
553
554    };
555
556    ($sep:expr; $item:expr => $should_include:expr) => {
557
558        $crate::FnRenderer::new(|tmpl| {
559            if $should_include {
560                $crate::RenderOnce::render_once($item, tmpl);
561            }
562        })
563
564    };
565
566    ($sep:expr; $item:expr, $($tail:tt)+) => {
567
568        $crate::FnRenderer::new(|tmpl| {
569            $crate::RenderOnce::render_once($item, tmpl);
570            $crate::labels_sep_by!(@inner_expand true; $sep; tmpl $($tail)*);
571        })
572
573    };
574
575    ($sep:expr; $item:expr => $should_include:expr, $($tail:tt)+) => {
576
577        $crate::FnRenderer::new(|tmpl| {
578            if $should_include {
579                $crate::RenderOnce::render_once($item, tmpl);
580            }
581            $crate::labels_sep_by!(@inner_expand $should_include; $sep; tmpl $($tail)*);
582        })
583
584    };
585
586}