Horrorshow is a macro-based HTML templating library. For those of you who saw the last post, horrorshow has become quite a bit more useful.
Links
Project: GitHub, crates.io
Docs: horrorshow - Rust
Features
- Works on stable.
- Built-in html escaping (that should be audited by someone who knows what they are doing...).
- Can render into existing strings and io writers.
- fast (compiles to assembly).
- Composable.
Non features:
- Really unhelpful errors. Unfortunately, there's no way to tell rust: "don't look in this ugly macro code for type errors, assume I meant every word of this macro code and the error is elsewhere."
- To be able to return a template, it needs to be boxed. This is because templates are actually closures. However, this is only true of returned templates; you don't need to allocate when rendering locally.
Example
#[macro_use]
extern crate horrorshow;
use horrorshow::{RenderBox, Template};
fn render_post(post: Post) -> Box<RenderBox> {
let Post { title, body, tags } = post;
box_html! {
article {
header(class="post-header") {
h1 : title;
ul {
|t| tags.iter().fold(t, |t, tag| t << html! {
li : tag
});
/*
// You should be able to write the following but rust doesn't
// re-borrow when using binary operators (gh#25753).
|t| for tag in tags {
t << html! { li : tag };
}
*/
}
}
section(class="post-body") : body;
}
}
}
fn render<I: Iterator<Item=Post>>(title: &str, posts: I) -> String {
(html! {
: raw!("<!DOCTYPE html>");
html {
head {
title : title
}
body {
main {
header { h1 : title }
section(id="posts") {
|t| posts.fold(t, |t, post| t << render_post(post));
}
}
}
}
}).into_string()
}
struct Post {
title: String,
tags: Vec<String>,
body: String,
}
fn main() {
let posts = vec![
Post {
title: String::from("First Post"),
tags: vec![String::from("first post")],
body: String::from("My Test Post"),
},
Post {
title: String::from("Second Post"),
tags: vec![],
body: String::from("My Second Test Post"),
},
];
println!("{}", render("my blog", posts.into_iter()));
}
Output:
<!DOCTYPE html>
<html>
<head>
<title>my blog</title>
</head>
<body>
<main>
<header>
<h1>my blog</h1>
</header>
<section id="posts">
<article>
<header class="post-header">
<h1>First Post</h1>
<ul>
<li>first post</li>
</ul>
</header>
<section class="post-body">My Test Post</section>
</article>
<article>
<header class="post-header">
<h1>Second Post</h1>
<ul></ul>
</header>
<section class="post-body">My Second Test Post</section>
</article>
</section>
</main>
</body>
</html>
If you have any comments on the current error handling design, please post them here: Error Handling Design in Horrorshow