html_compile/macros.rs
1/// Builds a Rust struct that represents an HTML component.
2///
3/// See documentation on `html!` for more details on the syntax to use
4#[macro_export]
5macro_rules! el {
6// Just specify element name
7 ($tag:tt) => {
8 Component {
9 tag: stringify!($tag),
10 meta: None,
11 child: Child::NoChild,
12 }
13 };
14
15// Specify element name and attributes
16($tag:tt ($( $label:tt=$value:literal )*)) => {
17 {
18 let mut temp_vec = Vec::new();
19 $(
20 temp_vec.push(
21 Attribute {
22 label: stringify!($label),
23 value: $value
24 }
25 );
26 )*
27
28 Component {
29 tag: stringify!($tag),
30 meta: Some(temp_vec),
31 child: Child::NoChild,
32 }
33 }
34 };
35
36// Specify element name, attributes and text
37
38($tag:tt ($( $label:tt=$value:literal )*) $content:literal) => {
39 {
40 let mut temp_vec = Vec::new();
41 $(
42 temp_vec.push(
43 Attribute {
44 label: stringify!($label),
45 value: $value
46 }
47 );
48 )*
49
50 Component {
51 tag: stringify!($tag),
52 meta: Some(temp_vec),
53 child: Child::Text($content),
54 }
55 }
56 };
57
58// Specify element name, attributes and text from a variable
59($tag:tt ($( $label:tt=$value:literal )*) {$content:expr}) => {
60 {
61 let mut temp_vec = Vec::new();
62 $(
63 temp_vec.push(
64 Attribute {
65 label: stringify!($label),
66 value: $value
67 }
68 );
69 )*
70
71 Component {
72 tag: stringify!($tag),
73 meta: Some(temp_vec),
74 child: Child::Text($content),
75 }
76 }
77 };
78
79// Specify element name, attributes and at least one child component
80($tag:tt ($( $label:tt=$value:literal )*) $([$component:expr])* ) => {
81 {
82 let mut attribute_vec = Vec::new();
83 $(
84 attribute_vec.push(
85 Attribute {
86 label: stringify!($label),
87 value: $value
88 }
89 );
90 )*
91
92 let mut component_vec = Vec::new();
93
94 $(
95
96 component_vec.push(Box::new($component));
97 )*
98
99
100 Component {
101 tag: stringify!($tag),
102 meta: Some(attribute_vec),
103 child: Child::ComponentVec(component_vec),
104 }
105 }
106 };
107
108// Specify element name, attributes and child components specified in a vector or array
109($tag:tt ($( $label:tt=$value:literal )*) vec[$component:expr] ) => {
110 {
111 let mut attribute_vec = Vec::new();
112 $(
113 attribute_vec.push(
114 Attribute {
115 label: stringify!($label),
116 value: $value
117 }
118 );
119 )*
120
121 let component_vec = $component.into_iter().map(|single| Box::new(single)).collect();
122
123 Component {
124 tag: stringify!($tag),
125 meta: Some(attribute_vec),
126 child: Child::ComponentVec(component_vec),
127 }
128 }
129 };
130}
131
132/// Generates an HTML string using the inputted syntax
133///### Creating a Text Element
134///#### Using a String Literal
135///
136/// ```text
137/// html!(div (style = "border: 1px solid black;" class = "Hello") "Hello World");
138/// ```
139///
140/// Will create the following string of HTML that consists of a `div` with a `style` attribute that creates a black border, a `class` attribute set to `Hello` and text set to `"Hello World"`
141///
142/// ```text
143/// "<div style=\"border: 1px solid black;\" class=\"Hello\">Hello World</div>"
144/// ```
145/// This renders as follows in the browser:
146///
147/// <div style="border: 1px solid black;" class="Hello">Hello World</div>
148///
149///
150/// The first token (here `div`) specifies the name of the element.
151///
152///The set of parentheses `()` contains the attributes for the element. The attribute name comes before the `=` and the attribute value after and is in double quotation marks. Different attributes are separated by whitespace.
153///
154///The text in double quotation marks at the end of the content specifies the text content `"Hello World"`.
155///
156///#### Using a Variable
157///The text can also be derived from a variable. In this case surround the variable with curly brackets `{}`
158///
159///```text
160///let example_text = "Hello World";
161///
162///html!(div (style = "border: 1px solid black;" class = "Hello") {example_text});
163///```
164///
165///gives the same result as before:
166///
167///```text
168///"<div style=\"border: 1px solid black;\" class=\"Hello\">Hello World</div>"
169///```
170///
171///### Creating an Element with No Attributes or Content
172///
173///Both `html!(div)` and `html!(div ())` will create a string of HTML consisting of an empty div with no styling
174///```text
175///"<div></div>"
176///```
177///
178///Void elements should not have end tags and this is handled accordingly. For example `html!(hr)` will return `"<hr />"`
179///
180///### Creating Elements that Contain other Elements
181///
182///```text
183///html!(ul () [el!(li () "First Sibling")] [el!(li () "Second Sibling")])
184///```
185///
186///Will create the following string of HTML that consists of an unordered list with two items
187///
188///```text
189///"<ul><li>First Sibling</li><li>Second Sibling</li></ul>"
190///```
191///
192///In the browser this renders as
193///
194///<ul><li>First Sibling</li><li>Second Sibling</li></ul>
195///
196///Each child component is surrounded by square brackets `[]` and is inputted into the macro `el!` which creates the component. Multiple specified child components are treated as siblings.
197///
198///The result from `el!(li () "Second Sibling")` could have been stored in a variable and the variable used instead as follows:
199///
200///```text
201///let second_sibling = el!(li () "Second Sibling");
202///let result = html!(ul()[el!(li () "First Sibling")][second_sibling]);
203///```
204/// This would return the same HTML String.
205///### Where Child Elements are Specified through a Vector or an Array
206///
207///```text
208///let example_list = [
209/// el!(li () "First Sibling"),
210/// el!(li () "Second Sibling")
211///];
212///
213///html!(ul () vec[example_list])
214///```
215///
216///Will create the following string of HTML that consists of an unordered list with two items
217///
218///```text
219///"<ul><li>First Sibling</li><li>Second Sibling</li></ul>"
220///```
221///
222///In the browser this renders as
223///
224///<ul><li>First Sibling</li><li>Second Sibling</li></ul>
225///
226///
227///Inserting the text `vec` before the square brackets `[]` tells the macro to expect a vector or array.
228///
229#[macro_export]
230macro_rules! html {
231 ($($x:tt)*) => {
232 {
233 let component = el!($($x)*);
234
235 build_component(&component)
236 }
237 }
238}
239
240/// Takes a String and swaps the placeholder text `{COMPONENT}` for the HTML String built using the provided syntax
241///
242/// First specify the String containing the placeholder text `{COMPONENT}`. This string should be inserted in curly brackets `{}` and have a comma inserted after the brackets.
243/// Second specify the syntax describing the element to be inserted.
244/// See documentation on `html!` for more details on the syntax to use.
245/// ```text
246/// let test_contents = String::from("<div>{COMPONENT}</div>");
247///
248/// let result = insert_html!({test_contents}, span () "Hello World");
249///
250/// assert_eq!(result, "<div><span>Hello World</span></div>");
251///
252/// ```
253#[macro_export]
254macro_rules! insert_html {
255 ({$contents:expr}, $($x:tt)*) => {
256 {
257 let component = el!($($x)*);
258
259 insert_components($contents, component)
260 }
261 }
262}