fhtml

Crates.iofhtml
lib.rsfhtml
version0.4.1
sourcesrc
created_at2024-05-08 11:00:16.44946
updated_at2024-05-23 13:51:51.545966
descriptionFormatting macros tailored for HTML
homepagehttps://github.com/JonahLund/fhtml
repositoryhttps://github.com/JonahLund/fhtml
max_upload_size
id1233662
size15,908
Jonah Lund (JonahLund)

documentation

README

fhtml

Simple and efficient macros for writing HTML in Rust

Overview

fhtml Provides formatting macros for writing HTML without the annoyance of dealing with HTML inside string literals. A few highlights:

  • simplicity: no complex templating syntax, just plain HTML with embedded expressions and format specifiers
  • zero extra allocations: fhtml macros expand to their std counterpart with no indirections or added allocations
  • compatibility: since fhtml is simply a wrapper over std macros, meaning that you can easily use idiomatic Rust, such as implementing fmt::Display or fmt::Write for creating components, or integrate with existing libraries and tools
  • safety: fhtml provides an easy way to escape values (escaping is NOT done implicitly)

Installation

In your Cargo.toml:

[dependencies]
fhtml = "0.4"

Syntax

  • HTML is typed as-is, unquoted:
fhtml::format!(<input />);
  • Expressions are passed in using braces:
fhtml::format!(<div>{1 + 1}</div>);
  • Text nodes are quoted:
fhtml::format!(<p>"Some text"</p>);
  • Format specifiers are written after expressions:
fhtml::format!(<code>{vec![1, 2, 3]:?}</code>);
  • Escaping is done by using an exclamation mark ! as a format specifier:
fhtml::format!(<div>{"<b>Dangerous input</b>":!}</div>);

this being the only format specifier deviating from the std::fmt syntax

Usage

Writing to a buffer

let mut buffer = String::new();
fhtml::write!(buffer, <div>"Hello, World!"</div>);

Escaping

let user_input = "<b>Dangerous input</b>";
fhtml::format!(<div>{user_input:!}</div>); // "<div>&lt;b&gt;Dangerous input&lt;/b&gt;</div>"

Components

Since fhtml macros expands to their std counterpart, you are free to create components however you prefer

Function components

fn heading(label: &'static str) -> String {
    fhtml::format! {
        <h1>{label}</h1>
    }    
}

let page = fhtml::format! {
    <main>
        {heading("My Heading")}
        <div>"My Content"</div>
    </main>
};

Struct components

use std::fmt;

struct Product {
    name: &'static str,
    price: f32,
}

impl fmt::Display for Product {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fhtml::write! { f,
            <article>
                <h2>{self.name}</h2>
                <h3>"$" {self.price}</h3>
            </article>
        }
    }
}

let products = fhtml::format! {
    <h1>"Our products"</h1>
    {Product {
      name: "Arabica Coffee Beans",
      price: 3.99
    }}
    {Product {
      name: "Sourdough Bread",
      price: 2.49
    }}
};

Format specifiers

fhtml::format!(<code>{vec![1, 2, 3]:?}</code>); // "<code>[1, 2, 3]</code>"
fhtml::format!(<span>{10:#b}</span>);           // "<span>0b1010</span>"

Iterators

fhtml::format! {
    <ul>
        {
            (0..10).fold(String::new(), |mut f, i| {
                let _ = fhtml::write! { f,
                    <li>{i}</li>
                };
                f
            })
        }
    </ul>
}
Commit count: 30

cargo fmt