#html-string #generate-html #static-website #html-templating #structs #html-content

html_compile

A template engine for generating HTML strings within Rust for use with static websites

2 unstable releases

0.2.0 Mar 22, 2024
0.1.1 Mar 7, 2024
0.1.0 Mar 7, 2024

#191 in Template engine

Download history 2/week @ 2024-09-23

136 downloads per month

MIT license

23KB
215 lines

HTML Compile

A template engine for generating HTML strings within Rust for use with static websites.

HTML strings can be generated by inputting a concise syntax into provided macros or, alternatively, from Rust structs.

This engine prioritizes speed and should only be used with trusted HTML data.

Generating HTML using Macros

A complete example is provided on GitHub in the directory example. The below provides an explanation of different aspects of the syntax.

The syntax used is inspired by pug.js but has been adapted to better fit use within a Rust context.

Creating a Text Element

Using a String Literal

html!(div (style = "border: 1px solid black;" class = "Hello") "Hello World");

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"

"<div style=\"border: 1px solid black;\" class=\"Hello\">Hello World</div>"

The first token (here div) specifies the name of the element.

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.

The text in double quotation marks at the end of the content specifies the text content "Hello World".

Using a Variable

The text can also be derived from a variable. In this case surround the variable with curly brackets {}

let example_text = "Hello World";

html!(div (style = "border: 1px solid black;" class = "Hello") {example_text});

gives the same result as before:

"div style=\"border: 1px solid black;\" class=\"Hello\">Hello World</div>"

Creating an Element with No Attributes or Content

Both html!(div) and html!(div ()) will create a string of HTML consisting of an empty div with no styling

"<div></div>"

Void elements should not have end tags and this is handled accordingly. For example html!(hr) will return "<hr />"

Creating Elements that Contain other Elements

html!(ul () [el!(li () "First Sibling")] [el!(li () "Second Sibling")])

Will create the following string of HTML that consists of an unordered list with two items

"<ul><li>First Sibling</li><li>Second Sibling</li></ul>"

In the browser this renders as

  • First Sibling
  • Second Sibling

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.

The result from el!(li () "Second Sibling") could have been stored in a variable and the variable used instead as follows:

let second_sibling = el!(li () "Second Sibling");
let result = html!(ul()[el!(li () "First Sibling")][second_sibling]);

This would return the same HTML String.

Where Child Elements are Specified through a Vector or an Array

let example_list = [
  el!(li () "First Sibling"),
  el!(li () "Second Sibling")
];

html!(ul () vec[example_list])

Will create the following string of HTML that consists of an unordered list with two items

"<ul><li>First Sibling</li><li>Second Sibling</li></ul>"

In the browser this renders as

  • First Sibling
  • Second Sibling

Inserting the text vec before the square brackets [] tells the macro to expect a vector or array.

Generating HTML using Rust structs

This library also provides tooling that enables generating HTML strings from Rust structs using the function build_component().

use std::fs;

fn main() {
    use html_compile::compile::*;
    use html_compile::types::*;

    let item_list: Vec<String> = vec![1, 2, 3].iter().map(|x| format!("{}", x)).collect();

    let item_components: Vec<Box<Component>> = item_list
        .iter()
        .map(|item| {
            Box::new(Component {
                tag: "li",
                meta: None,
                child: Child::Text(item),
            })
        })
        .collect();

    let input_component = Component {
        tag: "section",
        meta: Some(vec![
	     Attribute {
                label: "class",
                value: "Example",
            },
        ]),
        child: Child::ComponentVec(vec![
            Box::new(Component {
                tag: "h2",
                meta: None,
                child: Child::Text("A List of Items"),
            }),
            Box::new(Component {
                tag: "p",
                meta: None,
                child: Child::Text("The list begins after the following line"),
            }),
            Box::new(Component {
                tag: "hr",
                meta: None,
                child: Child::NoChild,
            }),
            Box::new(Component {
                tag: "ul",
                meta: None,
                child: Child::ComponentVec(item_components),
            }),
        ]),
    };

    let output = build_component(&input_component);

    let write_output_path = "./output.html";
    fs::write(write_output_path, output).expect("Unable to write to file");
}

The file output.html looks like the following:

<section class="Example">
<h2>A List of Items</h2>
<p>The list begins after the following line</p><hr/><ul><li>1</li><li>2</li><li>3</li></ul>
</section> 

This will render in the browser as follows:

A List of Items

The list begins after the following line


  • 1
  • 2
  • 3

Inserting HTML into existing HTML string

html_compile provides a macro insert_html! and function insert_components that searches for the placeholder string {COMPONENT} and switches this for the HTML string generated.

macro

insert_html!({test_contents}, div () "Hello World")

function

let test_component: Component = Component {
    tag: "div",
    meta: None,
    child: Child::Text("Hello World"),
};

insert_components(test_contents, test_component)

Both search the String in the variable test_contents for the placeholder {COMPONENT} and swap the placeholder for <div>Hello World</div>.

Find in crates.io

Find code on GitHub

Note that in the code on GitHub there is a directory example that provides an example application using this library.

No runtime deps