// The TechEmpower "fortunes" benchmark: read rows from a database, add one,
// sort them and render them as an HTML table.
//
// This does not compile yet: it needs database access, which is not built yet,
// and the `http` package has no `Server` with routes.
//
// Shows: an SQL query and an HTML template embedded in the same handler.
use http
use std::db::postgres
use std::html
// ---------------------------------------------------------------------------
// 1. Data
// ---------------------------------------------------------------------------
pub struct Fortune {
id: i32,
message: String,
}
// ---------------------------------------------------------------------------
// 2. The template, as a DSL
// ---------------------------------------------------------------------------
// The template binds immediately: `:rows` is taken from this function's scope
// where the template is written, unlike a SQL placeholder.
//
// Every `{…}` hole is escaped, and nothing at the hole turns that off. The only
// way to insert markup as it is is a value of type `html::Raw`. A hole where
// the template cannot escape, such as inside `<script>` or in a URL, is a
// compile error.
fn render(rows: Vec[Fortune]) -> String {
return dsl html {
<!DOCTYPE html>
<html>
<head><title>Fortunes</title></head>
<body>
<table>
<tr><th>id</th><th>message</th></tr>
<for row in :rows>
<tr><td>{row.id}</td><td>{row.message}</td></tr>
</for>
</table>
</body>
</html>
} eod
}
// ---------------------------------------------------------------------------
// 3. The handler
// ---------------------------------------------------------------------------
fn fortunes(db: Shared[postgres::Connection]) -> String throws {
// The query binds late: its parameters are filled when it runs, although
// this one has none. It is parsed at compile time, and its columns are
// checked against the schema. As a plain string, both checks would wait
// for the first request in production.
let all = dsl postgres::Sql {
SELECT id, message FROM fortune
} eod
// A network round-trip, written as a plain call: no `async` on the
// signature and no `await` here. While this request waits, the event loop
// serves other connections, which is what the benchmark measures.
//
// Nothing marks the failure either. `fetch` can fail, and because this
// function is `throws`, the error goes to the caller.
let mut rows = all.fetch(db)
// The benchmark requires one extra row added in memory, not in the query.
rows.push(Fortune { id: 0, message: "Additional fortune added at request time." })
// `sort_by` runs the comparison right away, so it can borrow `rows`
// instead of taking it. No copy is needed.
rows.sort_by fn(left, right) { left.message.cmp(right.message) }
return render(rows)
}
fn main() throws {
// One database connection, shared by every request.
let db = postgres::connect(env::var("DATABASE_URL") ?? "postgres://localhost/hello_world")
// A handler that does not mention the request takes no argument. This one
// does not need it.
http::Server::new()
.route("/fortunes") fn { fortunes(db) }
.listen(":8080")
}
The file itself · all the examples