Oh well. It looks like I tried to implement an asynchronous template system for Rust, but instead of actual templates I ended up manually writing a stream of Bytes
instances. It takes 10 times as much space as its output. But it’s asynchronous, and I expect that TTFB for, say, a large feed, would be much better with it.
For completeness, here is one template I wrote. It should’ve been an h-card.
pub fn card(mut mf2: serde_json::Value) -> impl Stream<Item = Bytes> + Unpin + Send {
let uid: String = match mf2["properties"]["uid"][0].take() {
serde_json::Value::String(uid) => uid,
_ => panic!("h-card without a uid")
};
let photo: Option<String> = match mf2["properties"]["photo"][0].take() {
serde_json::Value::String(url) => Some(url),
_ => None
};
let name: String = match mf2["properties"]["name"][0].take() {
serde_json::Value::String(name) => name,
_ => panic!("encountered h-card without a name")
};
chunk(b"<article class=\"h-card\">")
.chain(futures::stream::once(std::future::ready(
match photo {
Some(url) => {
let mut tag = Vec::new();
tag.extend_from_slice(b"<img class=\"u-photo\" src=\"");
html_escape::encode_double_quoted_attribute_to_vec(url, &mut tag);
tag.extend_from_slice(b"\" />");
Bytes::from(tag)
},
None => Bytes::new()
}
)))
.chain(futures::stream::once(std::future::ready({
let mut buf = Vec::new();
buf.extend_from_slice(b"<h1><a class=\"u-url u-uid p-name\" href=\"");
html_escape::encode_double_quoted_attribute_to_vec(uid, &mut buf);
buf.extend_from_slice(b"\">");
html_escape::encode_text_to_vec(&name, &mut buf);
buf.extend_from_slice(b"</a></h1>");
Bytes::from(buf)
})))
.chain(chunk(b"</article>"))
}
It’s huge. Here is the output it should produce (whitespace is mine):
<article class="h-card">
<img class="u-photo" src="https://example.com/media/me.png" />
<h1><a class="u-url u-uid p-name" href="https://example.com/">Jane Doe</a></h1>
</article>
I need some sort of macro system to work with these. The idea itself seems good, but the implementation... meh.