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:
[dependencies]
markup = "0.1"
Define your template using the markup::define!
macro:
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:
-
Writing it to any instance of std::io::Write
:
write!(writer, "{}", Hello { name: "Ferris" });
-
Converting it to a string and using it however you like:
let string = Hello { name: "Ferris" }.to_string();
The above template compiles to:
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.write_str("<html><head><title>Hello ")?;
markup::Render::render(&(name), __writer)?;
__writer.write_str("</title></head><body><p id=\"greeting\">Hello <span class=\"name\">")?;
markup::Render::render(&(name), __writer)?;
__writer.write_str("</span></p></body></html>")?;
Ok(())
}
}
Rendering the template produces (manually prettified):
<!DOCTYPE html>
<html>
<head>
<title>Hello Ferris</title>
</head>
<body>
<p id="greeting">Hello <span class="name">Ferris</span></p>
</body>
</html>
Syntax
Define
markup::define! {
First {
"First!"
}
Second {
"Second!"
}
}
println!("{}", First);
println!("{}", Second.to_string());
First!
Second!
Literal Strings and Expressions
markup::define! {
Hello {
"Hello,"
" "
"world!\n"
{1 + 2}
{format!("{}{}", 3, 4)}
{if true { Some(5) } else { None }}
{if false { Some(6) } else { None }}
}
}
println!("{}", Hello {});
Hello, world!
3345
Elements
Normal and Void
markup::define! {
Hello {
div
br;
}
}
println!("{}", Hello {});
<div></div><br>
id and class shorthands
markup::define! {
Hello {
button#go.button."button-blue"
button#"go-back".{1 + 2}.{2 + 3}
}
}
println!("{}", Hello {});
<button id="go" class="button button-blue"></button><button id="go-back" class="3 5"></button>
Attributes with and without values
markup::define! {
Hello {
div[a = 1, b = "2", c? = true, d? = false, "e-f" = 3, {"g".to_string() + "-h"} = 4, i = None::<i32>, j = Some(5)]
br[k = 6];
}
}
println!("{}", Hello {});
<div a="1" b="2" c e-f="3" g-h="4" j="5"></div><br k="6">
Children
markup::define! {
Hello {
div[a = 1] {
"One"
{0 + 1}
}
div {
"Two"
{1 + 1}
}
}
}
println!("{}", Hello {});
<div a="1">One1</div><div>Two2</div>
Disable Automatic HTML Escaping
markup::define! {
Hello {
"<&\">"
{markup::Raw("<span></span>")}
}
}
println!("{}", Hello {});
<&"><span></span>
Arguments
markup::define! {
Hello(foo: u32, bar: u32, string: String) {
div {
{foo + bar}
{string}
}
}
}
println!("{}", Hello { foo: 1, bar: 2, string: String::from("hello") });
<div>3hello</div>
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}
}
}
}
println!("{}", Hello { arg: (1, 2), arg2: "arg2", str: "str" });
<div>(1, 2)arg2str</div>
Embed Other Templates
markup::define! {
Add(a: u32, b: u32) {
span { {a + b} }
}
Hello {
{Add { a: 1, b: 2 }}
{Add { a: 3, b: 4 }}
}
}
println!("{}", Hello {});
<span>3</span><span>7</span>
If
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 }}
}
}
println!("{}", Main {});
-42 is negative.
0 is zero.
42 is positive.
For
markup::define! {
Main {
@for i in 1..5 {
{i} " * 2 = " {i * 2} ";\n"
}
}
}
println!("{}", Main {});
1 * 2 = 2;
2 * 2 = 4;
3 * 2 = 6;
4 * 2 = 8;