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
pub trait FormatAttribute { fn format_attribute(&self) -> String; } impl<T> FormatAttribute for Vec<T> where T: FormatAttribute { fn format_attribute(&self) -> String { let mut out = String::new(); let mut it = self.iter().peekable(); while let Some(next) = it.next() { out.push_str(&next.format_attribute()); if !it.peek().is_none() { out.push_str(" "); } } out } } impl<'a> FormatAttribute for &'a str { fn format_attribute(&self) -> String { (*self).to_owned() } } impl FormatAttribute for String { fn format_attribute(&self) -> String { self.clone() } } #[macro_export] macro_rules! html { (@open $out:ident, $element:ident {$($key:ident => $value:expr),*}) => {{ write!($out, "<{}", stringify!($element))?; $( write!($out, " {}=\"", stringify!($key))?; $out.write_str(&$value.format_attribute())?; write!($out, "\"")?; )* write!($out, ">")?; }}; (@close $out:ident, $element:ident) => {{ write!($out, "</{}>", stringify!($element))?; }}; ($out:ident, $element:ident {$($key:ident => $value:expr),*} => $body:block) => {{ html!(@open $out, $element {$($key=> $value),*}); $out.new_line()?; $out.indent(); $body; $out.new_line_unless_empty()?; $out.unindent(); html!(@close $out, $element); $out.new_line()?; }}; ($out:ident, $element:ident {$($key:ident => $value:expr),*} ~ $body:expr) => {{ html!(@open $out, $element {$($key=> $value),*}); write!($out, "{}", $body)?; html!(@close $out, $element); $out.new_line()?; }}; ($out:ident, $element:ident {$($key:ident => $value:expr),*}) => { html!($element {$($key=> $value),*}, $out => {}) }; ($out:ident, $element:ident $body:expr) => { html!($element {} $body) }; }