Skip to main content

Crate avosetta

Crate avosetta 

Source
Expand description

§avosetta

A fast, minimal html templating language for Rust.

§about

avosetta is a minimal templating library for that utilises procedural macros to generate as close to optimal code as possible for rendering html content at runtime. It has no unsafe code, only a handful of dependencies, and does not allocate any values on the heap.

We implement a terse, simple syntax for specifying templates that is straightforward to parse, has little ambiguity and integrates into Rust code better. And unlike other templating libraries such as maud, our syntax typically only has a single way of writing various constructs, reducing code-style clashing.

Optimisations include automatically escaping static string literals at compile-time and collapsing contiguous String::push_str calls into a single one. Therefore, if your html fragment is entirely static, the generated code will just be a single String::push_str with a &str.

§getting started

To start using avosetta, you’ll first need to add our package to your Cargo.toml manifest:

cargo add avosetta

Then you can start writing html templates directly in your Rust source code.

use avosetta::prelude::*;

fn main() {
  let mut s = String::new();
  index().write(&mut s);

  println!("{s}");
}

fn index() -> impl Html {
  html! {
    @layout(
      html! {
        title { "avosetta" }
      },

      html! {
        h1 { "Hello, World!" }
      },
    )
  }
}

fn layout(
  head: impl Html,
  body: impl Html,
) -> impl Html {
  html! {
    "!DOCTYPE"[html];
    html[lang="en"] {
      head {
        meta[charset="UTF-8"];
        meta[name="viewport", content="width=device-width,initial-scale=1"];

        @head
      }

      body {
        main {
          @body
        }
      }
    }
  }
}

§reference

The syntax that this macro accepts is unique to avosetta, however, it shares some major similarities with crates such as maud and markup. All of these crates implement a terse, pug-like syntax which integrates into rust code better and is less error-prone.

Unlike the other crates, however, avosetta has a more minimal syntax.

§elements

There are two types of elements that can be defined: normal and void. void elements cannot have a body and must be terminated with a semicolon.

html! {
  // A `normal` element that will be rendered as: `<article></article>`
  article {}

  // A `void` element that will be rendered as: `<br>`
  br;

  // Element names can also be string literals:
  "x-custom-element" {}

  "x-custom-element";
};

§attributes

Elements can also have attributes, which is a comma delimited list of key=value pairs. Note, that attribute values can be dynamic (interpolated at runtime), where as attribute names must be known at compile time.

html! {
  // A `meta` element with an attribute:
  meta[charset="UTF-8"];

  // Elements can have multiple attributes:
  meta[name="viewport", content="width=device-width,initial-scale=1"];

  // Attribute names can also be string literals:
  div["x-data"="Hello, World!"] {}

  // Attribute values can be any `Rust` expression:
  input[value={4 + 3}];

  // Attributes without a value are implicitly a `true` boolean attribute:
  input["type"="checkbox", checked];
  input["type"="checkbox", checked=true]; // These two elements are equivalent.
};

§interpolation

The process of “injecting” or writing dynamic content into the html is called interpolation. This might be used for displaying a local variable containing a username, or for performing a conditional if check before rendering some sub-content.

All interpolations start with an @, however, depending on the context, different interpolations will be generated in the Html implementation.

let x = 9;

html! {
  // The most basic interpolation of a simple expression:
  @x

  // More complicated expressions can also be interpolated:
  @x + 2

  // Depending on what you're interpolating, it may remove ambiguity to use
  // a block expression:
  @{ x + 2 }

  // `Html` is implemented for `()`, therefore, expressions don't need to
  // return any html content:
  @{
    // This will be executed when the [`Html`] is written to a [`String`].
    println!("Hello, World!");
  }

  // You can conditionally render content using the normal `Rust` syntax:
  @if x > 8 {
    // Notice how these arms take [`html!`] syntax and not `Rust` syntax.
    h1 { "Hello, World!" }
  } else if x < 2 {
    h2 { "Hello, World!" }
  } else {
    h3 { "Hello, World!" }
  }

  // The same concept applies to both the `match` and `for` keywords:
  @match x {
    // Each arm must be wrapped in braces.
    8 => {
      h1 { "Hello, World!" }
    }

    // Except for simple string literals.
    _ => "Hello, World!"
  }

  @for i in 0..24 {
    // Nested interpolation works as you'd expect.
    span { @i }
  }
};

§string literals

Static string literals can be written directly without the need for an interpolation. This form of “non-interpolated” static string is always escaped at compile time, whereas, the interpolated variant will only be escaped at compile time if the expression is simple enough for avosetta to parse.

html! {
  // All of these statements will result in the same output.
  h1 { @"Hello, World!" }

  h1 { "Hello, World!" }

  // This will not be escaped at compile-time because the extra braces make
  // this expression too complex for `avosetta` to try and parse.
  h1 { @{ "Hello, World!" } }
};

Modules§

prelude

Macros§

html

Structs§

Attr
A html attribute, containing both a key and value.
Raw
Text that should not be escaped at runtime, such as dynamically generated html, or some content that has already been escaped.

Traits§

Html
Represents a fragment of valid html that can be written to a String.

Functions§

raw
Text that should not be escaped at runtime, such as dynamically generated html, or some content that has already been escaped.