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}