markup.rs

A blazing fast, type-safe template engine for Rust.

markup.rs is a template engine for Rust powered by procedural macros which parses the template at compile time and generates optimal Rust code to render the template at run time. The templates may embed Rust code which is type checked by the Rust compiler enabling full type-safety.

Quick Start

Add the markup crate to your dependencies:

toml [dependencies] markup = "0.1"

Define your template using the markup::define! macro:

rust markup::define! { Hello(name: &'static str) { {markup::Doctype} html { head { title { "Hello " {name} } } body { p#greeting { "Hello " span.name { {name} } } } } } }

Render your template by either:

  1. Writing it to any instance of std::io::Write:

    rust write!(writer, "{}", Hello { name: "Ferris" });

  2. Converting it to a string and using it however you like:

    rust let string = Hello { name: "Ferris" }.to_string();

The above template compiles to:

```rust pub struct Hello { pub name: &'static str, }

impl std::fmt::Display for Hello { fn fmt(&self, _writer: &mut std::fmt::Formatter) -> std::fmt::Result { use std::fmt::Display; let Hello { name } = self; markup::Render::render(&(markup::Doctype), _writer)?; _writer.writestr("Hello ")?; markup::Render::render(&(name), <em>_writer)?; _</em>writer.write<em>str("

Hello ")?; markup::Render::render(&(name), _writer)?; _writer.writestr("

")?; Ok(()) } } ```

Rendering the template produces (manually prettified):

html <!DOCTYPE html> <html> <head> <title>Hello Ferris</title> </head> <body> <p id="greeting">Hello <span class="name">Ferris</span></p> </body> </html>

Syntax

Define

rust markup::define! { First { "First!" } Second { "Second!" } }

rust println!("{}", First); println!("{}", Second.to_string());

html First! Second!

Literal Strings and Expressions

rust markup::define! { Hello { "Hello," " " "world!\n" {1 + 2} {format!("{}{}", 3, 4)} } } rust println!("{}", Hello {}); html Hello, world! 334

Elements

Normal and Void

rust markup::define! { Hello { div br; } } rust println!("{}", Hello {}); ```html


``` #### id and class shorthands ```rust markup::define! { Hello { button#go.button."button-blue" button#"go-back".{1 + 2}.{2 + 3} } } ``` ```rust println!("{}", Hello {}); ``` ```html ``` #### Attributes with and without values ```rust markup::define! { Hello { div[a = 1, b = "2", c? = true, d? = false, "e-f" = 3, {"g".to_string() + "-h"} = 4] br[i = 5]; } } ``` ```rust println!("{}", Hello {}); ``` ```html

``` #### Children ```rust markup::define! { Hello { div[a = 1] { "One" {0 + 1} } div { "Two" {1 + 1} } } } ``` ```rust println!("{}", Hello {}); ``` ```html
One1
Two2

```

Disable Automatic HTML Escaping

rust markup::define! { Hello { "<&\">" {markup::Raw("<span></span>")} } } rust println!("{}", Hello {}); html &lt;&amp;&quot;&gt;<span></span>

Arguments

rust markup::define! { Hello(foo: u32, bar: u32, string: String) { div { {foo + bar} {string} } } } rust println!("{}", Hello { foo: 1, bar: 2, string: String::from("hello") }); ```html

3hello

```

rust markup::define! { Hello<'a, T: std::fmt::Debug, U>(arg: T, arg2: U, str: &'a str) where U: std::fmt::Display { div { {format!("{:?}", arg)} {format!("{}", arg2)} {str} } } } rust println!("{}", Hello { arg: (1, 2), arg2: "arg2", str: "str" }); ```html

(1, 2)arg2str

```

Embed Other Templates

rust markup::define! { Add(a: u32, b: u32) { span { {a + b} } } Hello { {Add { a: 1, b: 2 }} {Add { a: 3, b: 4 }} } } rust println!("{}", Hello {}); html <span>3</span><span>7</span>

If

rust markup::define! { Classify(value: i32) { {value} " is " @if *value < 0 { "negative" } else if *value == 0 { "zero" } else { "positive" } ".\n" } Main { {Classify { value: -42 }} " " {Classify { value: 0 }} " " {Classify { value: 42 }} } } rust println!("{}", Main {}); html -42 is negative. 0 is zero. 42 is positive.

For

rust markup::define! { Main { @for i in 1..5 { {i} " * 2 = " {i * 2} ";\n" } } } rust println!("{}", Main {}); html 1 * 2 = 2; 2 * 2 = 4; 3 * 2 = 6; 4 * 2 = 8;