Crate hypertext

Source
Expand description

A blazing fast type-checked HTML macro crate.

§Features

§Speed

The macros generate code that is as fast as writing HTML to a string by hand, and intelligently combines what would be multiple push_str calls into one if there is no dynamic content between them.

The entire crate is #![no_std] compatible, and allocation is completely optional if you don’t use any dynamic content.

The crate gives extreme importance to lazy rendering and minimizing allocation, so it will only render the HTML to a string when you finally call Renderable::render at the end. This makes composing nested HTML elements extremely cheap.

§Type-Checking

All macro invocations are validated at compile time, so you can’t ever misspell an element/attribute or use invalid attributes.

It does this by looking in your current namespace, or a module named hypertext_elements (all the valid HTML elements are defined in this crate already in hypertext_elements, but it doesn’t hard-code this module so you can define your own elements).

It then imports each element you use in your macro invocation, and then attempts to access the corresponding associated type for each attribute you use.

§Example

use hypertext::prelude::*;

maud! {
    div #main title="Main Div" {
        h1.important {
            "Hello, world!"
        }

        @for i in 1..=3 {
            p.{ "p-" (i) } {
                "This is paragraph number " (i)
            }
        }
    }
}

// expands to (roughly):

Lazy::<_, Node>::dangerously_create(move |buffer: &mut Buffer| {
    const _: fn() = || {
        use hypertext_elements::*;
        fn check_element<K: ElementKind>(_: impl Element<Kind = K>) {}
     
        check_element::<Normal>(h1);
        let _: Attribute = <h1>::class;
        check_element::<Normal>(p);
        let _: Attribute = <p>::class;
        check_element::<Normal>(div);
        let _: Attribute = <div>::id;
        let _: Attribute = <div>::title;
    };
    buffer
        .dangerously_get_string()
        .push_str(r#"<div id="main" title="Main Div">"#);
    {
        buffer
            .dangerously_get_string()
            .push_str(r#"<h1 class="important">Hello, world!</h1>"#);
        for i in 1..=3 {
            buffer.dangerously_get_string().push_str("<p class=\"p-");
            i.render_to(buffer.as_attribute_buffer());
            buffer
                .dangerously_get_string()
                .push_str(r#"">This is paragraph number "#);
            i.render_to(buffer);
            buffer.dangerously_get_string().push_str("</p>");
        }
    }
    buffer.dangerously_get_string().push_str("</div>");
})

This approach is also extremely extensible, as you can define your own traits to add attributes for your favourite libraries! In fact, this is exactly what GlobalAttributes does, and why it is required in the above example, as it defines the attributes that can be used on any element, for example id, class, and title. This library comes with built-in support for many popular frontend attribute-based frameworks in validation::attributes, such as HtmxAttributes and AlpineJsAttributes

Here’s an example of how you could define your own attributes for use with the wonderful frontend library htmx:

use hypertext::{
    prelude::*,
    validation::{Attribute, AttributeNamespace},
};

trait HtmxAttributes: GlobalAttributes {
    const hx_get: Attribute = Attribute;
    const hx_on: AttributeNamespace = AttributeNamespace;
    // ...
}

impl<T: GlobalAttributes> HtmxAttributes for T {}

assert_eq!(
    maud! {
        div hx-get="/api/endpoint" hx-on:click="alert('Hello, world!')" {
        //  ^^^^^^ note that it converts `-` to `_` for you during checking!
            "Hello, world!"
        }
    }
    .render()
    .as_inner(),
    r#"<div hx-get="/api/endpoint" hx-on:click="alert('Hello, world!')">Hello, world!</div>"#,
);

Wrapping an attribue name in quotes will bypass the type-checking, so you can use any attribute you want, even if it doesn’t exist in the current context.

use hypertext::prelude::*;

assert_eq!(
    maud! {
        div "custom-attribute"="value" { "Hello, world!" }
    }
    .render()
    .as_inner(),
    r#"<div custom-attribute="value">Hello, world!</div>"#,
);

This library also supports component structs, which are simply structs that implement Renderable. If an element name is capitalized, it will be treated as a component, with attributes representing the struct fields. The #[component] macro can be used to easily turn functions into components.

use hypertext::{Buffer, prelude::*};

struct Repeater<R: Renderable> {
    count: usize,
    children: R,
}

impl<R: Renderable> Renderable for Repeater<R> {
    fn render_to(&self, buffer: &mut Buffer) {
        maud! {
            @for i in 0..self.count {
                (self.children)
            }
        }
        .render_to(buffer);
    }
}

assert_eq!(
    maud! {
       div {
           Repeater count=3 {
               // children are passed as a `Lazy` to the `children` field
               p { "Hi!" }
           }
        }
    }
    .render()
    .as_inner(),
    "<div><p>Hi!</p><p>Hi!</p><p>Hi!</p></div>"
);

Modules§

context
The Context trait and its implementors.
prelude
Re-exported items for convenience.
validation
Types and traits used for validation of HTML elements and attributes.

Macros§

attributealloc
Generate an attribute value, returning a LazyAttribute.
attribute_borrowalloc
Generate an attribute value, borrowing the environment.
attribute_static
Generate static HTML attributes.
define_elements
Define custom elements.
define_void_elements
Define custom void elements.
maudalloc
Generate HTML using maud syntax, returning a Lazy.
maud_borrowalloc
Generate HTML using maud! syntax, borrowing the environment.
maud_static
Generate static HTML using maud syntax.
rsxalloc
Generate HTML using rsx syntax, returning a Lazy.
rsx_borrowalloc
Generate HTML using rsx! syntax, borrowing the environment.
rsx_static
Generate static HTML using rsx syntax.

Structs§

Bufferalloc
The buffer used for rendering HTML.
Debuggedalloc
A value rendered via its Debug implementation.
Displayedalloc
A value rendered via its Display implementation.
Lazyalloc
A value lazily rendered via a closure.
Raw
A raw pre-escaped string.
Rendered
A rendered HTML string.

Traits§

Renderablealloc
A type that can be rendered as an HTML node.
RenderableExtalloc
An extension trait for Renderable types.

Type Aliases§

AttributeBufferalloc
A buffer used for rendering attribute values.
LazyAttributealloc
An attribute value lazily rendered via a closure.
RawAttribute
A raw pre-escaped attribute value.

Attribute Macros§

componentalloc
Convert a function returning a Renderable into a component.

Derive Macros§

Renderablealloc
Derive Renderable for a type.