avosetta 0.1.0

A fast, minimal html templating language for Rust.
Documentation
# 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
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. For more information, read the full syntax reference
[here](#reference).

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

## getting started

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

```toml
[dependencies]
avosetta = "0.1.0"
```

Then you can start writing `HTML` templates directly in your `Rust` source
code. We recommend that you import the `prelude` module to reduce unnecessary
qualifications, but that's up to you.

```rust
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`.

```rust
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.

```rust
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 `impl Html`.

```rust
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

Whilst most content can be interpolated into the `HTML` at runtime, there
is a specific optimisation made for static string literals. When used without an
`@`, the string literals are automatically escaped at compile-time and rendered
using `avosetta::raw`.

```rust
html! {
  // Both of these elements will render to the same `HTML`, however,
  // the first one will escape the string at runtime because it is an
  // interpolated expression.
  h1 { @"Hello, World!" }

  // This one will be pre-escaped at compile time, avoiding the performance
  // cost of runtime escaping.
  h1 { "Hello, World!" }
}
```