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
pub struct Tag {
pub name: &'static str,
pub attrs: Vec<(&'static str, Box<dyn std::fmt::Display>)>,
pub children: Vec<Box<dyn std::fmt::Display>>,
}
impl Tag {
pub fn with_children<V: std::fmt::Display + 'static, T: IntoIterator<Item = V>>(
mut self,
children: T,
) -> Self {
self.children.clear();
self.add_children(children)
}
pub fn add_children<V: std::fmt::Display + 'static, T: IntoIterator<Item = V>>(
mut self,
children: T,
) -> Self {
self.children.extend(
children
.into_iter()
.map(|v| -> Box<dyn std::fmt::Display> { Box::new(v) }),
);
self
}
}
impl std::fmt::Display for Tag {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use std::fmt::Write;
write!(f, "<{}", self.name)?;
let mut buf = String::new();
for (k, v) in self.attrs.iter() {
buf.clear();
write!(&mut buf, "{}", v)?;
if buf.is_empty() {
continue;
}
write!(f, " {}=\"{}\"", k, buf)?;
}
match &self.children[..] {
[] if self.name == "script" => write!(f, "></{}>", self.name),
[] => write!(f, "/>"),
[v] => {
buf.clear();
write!(&mut buf, "{}", v)?;
if buf.is_empty() {
return write!(f, "/>");
}
write!(f, ">{}</{}>", buf, self.name)
}
children => {
write!(f, ">")?;
for child in children {
write!(f, "{}", child)?;
}
write!(f, "</{}>", self.name)
}
}
}
}
#[macro_export]
macro_rules! tag {
($name:literal[$($k:literal=$v:expr)+] $($children:expr)*) => {
$crate::Tag {
name: $name,
attrs: vec![$(($k, Box::new($v))),*],
children: vec![$(Box::new($children)),*],
}
};
($name:literal $($children:expr)*) => {
$crate::Tag {
name: $name,
attrs: vec![],
children: vec![$(Box::new($children)),*],
}
};
}
#[macro_export]
macro_rules! _maybe {
($v:literal) => {
Some($v)
};
($v:expr) => {
$v
};
}
#[macro_export]
macro_rules! class {
($($v:expr),*) => {
(&[$($crate::_maybe!($v)),*]).into_iter().flatten().join(" ")
};
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}