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)
    };
}