1use std::fmt::{self, Display, Formatter};
2
3#[derive(Debug, Clone)]
4pub struct Tag {
5 pub name: &'static str,
6 pub args: Vec<(&'static str, String)>,
7 pub children: Vec<Element>,
8 pub self_closing: bool,
9}
10
11#[derive(Debug, Clone)]
12pub enum Element {
13 Tag(Tag),
14 String(String),
15}
16
17#[derive(Clone, Copy)]
18pub struct Indent(u32);
19
20impl Tag {
21 pub fn new(name: &'static str) -> Self {
22 Self {
23 name,
24 args: Vec::new(),
25 children: Vec::new(),
26 self_closing: false,
27 }
28 }
29}
30
31impl Element {
32 pub fn render(&self, f: &mut Formatter<'_>, ind: Indent) -> fmt::Result {
33 match self {
34 Self::Tag(Tag {
35 name,
36 args,
37 children,
38 self_closing,
39 }) => {
40 write!(f, "{ind}<{name}")?;
41 for (name, value) in args {
42 write!(f, " {name}={value:?}")?;
43 }
44 write!(f, ">")?;
45 match &children[..] {
46 [] if *self_closing => writeln!(f),
47 [] => writeln!(f, "</{name}>"),
48 [Element::String(s)] => writeln!(f, "{s}</{name}>"),
49 _ => {
50 writeln!(f)?;
51
52 for child in children {
53 child.render(f, ind.next())?;
54 }
55
56 writeln!(f, "{ind}</{name}>")?;
57 Ok(())
58 }
59 }
60 }
61 Self::String(s) if s.is_empty() => Ok(()),
62 Self::String(s) => writeln!(f, "{ind}{s}"),
63 }
64 }
65}
66
67impl From<&str> for Element {
68 fn from(value: &str) -> Self {
69 Self::String(value.into())
70 }
71}
72
73impl From<String> for Element {
74 fn from(value: String) -> Self {
75 Self::String(value)
76 }
77}
78
79impl Display for Element {
80 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
81 self.render(f, Indent(0))
82 }
83}
84
85impl Display for Indent {
86 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
87 for _ in 0..self.0 {
88 write!(f, "\t")?;
89 }
90 Ok(())
91 }
92}
93
94impl Indent {
95 fn next(self) -> Self {
96 Self(self.0 + 1)
97 }
98}
99
100#[macro_export]
101macro_rules! html {
102 ($name:ident $([ $($an:tt = $av:expr),* $(,)? ])? { $($tk:tt)* }) => {
103 {
104 #[allow(unused_mut)]
105 let mut tag = $crate::Tag {
106 name: stringify!($name),
107 args: vec! [
108 $(
109 $(
110 (html!(@ $an), $av.to_string())
111 ),*
112 )?
113 ],
114 children: Vec::new(),
115 self_closing: false,
116 };
117
118 html! {
119 ! tag $($tk)*
120 }
121
122 $crate::Element::Tag(tag)
123 }
124 };
125 (! $parent:ident $name:ident $([$($args:tt)*])? { $($tk:tt)* } $($rest:tt)*) => {
126 $parent.children.push(html! { $name $([$($args)*])? { $($tk)* } });
127
128 html! {
129 ! $parent $($rest)*
130 }
131 };
132 (! $parent:ident $name:ident $([$($an:ident = $av:expr),*])? ; $($rest:tt)*) => {
133 $parent.children.push(
134 $crate::Tag($crate::Tag {
135 name: stringify!($name),
136 args: vec! [
137 $(
138 $(
139 (html! { @$an}, $av.to_string())
140 ),*
141 )?
142 ],
143 children: Vec::new(),
144 self_closing: true,
145 })
146 );
147
148 html! {
149 ! $parent $($rest)*
150 }
151 };
152 (! $parent:ident $name:ident = $val:expr ; $($rest:tt)*) => {
153 $parent.children.push($crate::Element::Tag($crate::Tag {
154 name: stringify!($name),
155 args: Vec::new(),
156 children: vec![
157 $crate::Element::String($val.into())
158 ],
159 self_closing: false,
160 }));
161
162 html! {
163 ! $parent $($rest)*
164 }
165 };
166 (! $parent:ident $s:literal ; $($rest:tt)*) => {
167 $parent.children.push($crate::Element::String(format!($s)));
168
169 html! {
170 ! $parent $($rest)*
171 }
172 };
173 (! $parent:ident { $e:expr } $($rest:tt)*) => {
174 $parent.children.push($e.into());
175
176 html! {
177 ! $parent $($rest)*
178 }
179 };
180 (! $parent:ident ? { $e:expr } $($rest:tt)*) => {
181 if let Some(e) = $e {
182 $parent.children.push(e.into());
183 }
184
185 html! {
186 ! $parent $($rest)*
187 }
188 };
189 (! $parent:ident [ $e:expr ] $($rest:tt)*) => {
190 for e in $e {
191 $parent.children.push(e.into());
192 }
193
194 html! {
195 ! $parent $($rest)*
196 }
197 };
198 (! $parent:ident) => {};
199 (@ $i:ident) => { stringify!($i) };
200 (@ $s:literal) => { $s };
201}