Skip to content
This repository was archived by the owner on Mar 24, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frameworks/Zig/dusty/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.zig-cache
zig-out
25 changes: 25 additions & 0 deletions frameworks/Zig/dusty/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

# [Dusty](https://github.com/lalinsky/dusty) - Zig HTTP client/server library

## Description

Zig HTTP client/server library built on top of zio (coroutine/async engine)

## Test URLs

### Test 1: JSON Encoding

http://localhost:3000/json

### Test 2: Plaintext

http://localhost:3000/plaintext

### Test 2: Single Row Query

http://localhost:3000/db

### Test 4: Fortunes (Template rendering)

http://localhost:3000/fortunes

26 changes: 26 additions & 0 deletions frameworks/Zig/dusty/benchmark_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"framework": "dusty",
"tests": [{
"default": {
"json_url": "/json",
"plaintext_url": "/plaintext",
"db_url": "/db",
"fortune_url": "/fortunes",
"port": 3000,
"approach": "Realistic",
"classification": "Fullstack",
"database": "Postgres",
"framework": "dusty",
"language": "Zig",
"flavor": "None",
"orm": "raw",
"platform": "None",
"webserver": "None",
"os": "Linux",
"database_os": "Linux",
"display_name": "Dusty (Zig)",
"notes": "",
"versus": ""
}
}]
}
58 changes: 58 additions & 0 deletions frameworks/Zig/dusty/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const std = @import("std");

pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

const dep_opts = .{ .target = target, .optimize = optimize };

const root_module = b.addModule("root_mod", .{
.target = target,
.optimize = optimize,
.root_source_file = b.path("src/main.zig"),
});

const dusty_dep = b.dependency("dusty", dep_opts);
const dusty_module = dusty_dep.module("dusty");
const zio_module = dusty_dep.builder.dependency("zio", dep_opts).module("zio");
const pg_module = b.dependency("pg", dep_opts).module("pg");
const datetimez_module = b.dependency("datetimez", dep_opts).module("datetime");

const exe = b.addExecutable(.{
.name = "dusty",
.root_module = root_module,
});

exe.root_module.addImport("dusty", dusty_module);
exe.root_module.addImport("zio", zio_module);
exe.root_module.addImport("pg", pg_module);
exe.root_module.addImport("datetimez", datetimez_module);

// This declares intent for the executable to be installed into the
// standard location when the user invokes the "install" step (the default
// step when running `zig build`).
b.installArtifact(exe);

// This *creates* a Run step in the build graph, to be executed when another
// step is evaluated that depends on it. The next line below will establish
// such a dependency.
const run_cmd = b.addRunArtifact(exe);

// By making the run step depend on the install step, it will be run from the
// installation directory rather than directly from within the cache directory.
// This is not necessary, however, if the application depends on other installed
// files, this ensures they will be present and in the expected location.
run_cmd.step.dependOn(b.getInstallStep());

// This allows the user to pass arguments to the application in the build
// command itself, like this: `zig build run -- arg1 arg2 etc`
if (b.args) |args| {
run_cmd.addArgs(args);
}

// This creates a build step. It will be visible in the `zig build --help` menu,
// and can be selected like this: `zig build run`
// This will evaluate the `run` step rather than the default, which is "install".
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}
24 changes: 24 additions & 0 deletions frameworks/Zig/dusty/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.{
.name = .dusty_testing,
.fingerprint = 0x402c0022f1133ce0,
.version = "0.1.1",
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
.dependencies = .{
.pg = .{
.url = "git+https://github.com/karlseguin/pg.zig.git#f8d4892387fbad2abdf775783e101e50a7114335",
.hash = "pg-0.0.0-Wp_7gag6BgD_QAZrPhNNEGpnUZR_LEkKT40Ura3p-4yX",
},
.dusty = .{
.url = "git+https://github.com/lalinsky/dusty.git#8aaedf71a069758bd88437d117ccd89069dc9bf8",
.hash = "dusty-0.0.0-Qdw7Rqh_CQDJNptlxOVIRgT4DxHnAKT9KohxfNhSH9bC",
},
.datetimez = .{
.url = "git+https://github.com/frmdstryr/zig-datetime.git#3a39a21e6e34dcb0ade0ff828d0914d40ba535f3",
.hash = "datetime-0.8.0-cJNXzP_YAQBxQ5hkNNP6ScnG5XsqciJmeP5RVV4xwCBA",
},
},
}
38 changes: 38 additions & 0 deletions frameworks/Zig/dusty/dusty.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
FROM debian:12.9 AS build

WORKDIR /app

COPY src src
COPY build.zig.zon build.zig.zon
COPY build.zig build.zig

ARG ZIG_VER=0.15.2

RUN apt-get update && apt-get install -y wget xz-utils ca-certificates git

RUN wget https://ziglang.org/download/${ZIG_VER}/zig-$(uname -m)-linux-${ZIG_VER}.tar.xz

RUN tar -xvf zig-$(uname -m)-linux-${ZIG_VER}.tar.xz

RUN mv zig-$(uname -m)-linux-${ZIG_VER} /usr/local/zig
Comment thread
msmith-techempower marked this conversation as resolved.

ENV PATH="/usr/local/zig:$PATH"
RUN zig build -Doptimize=ReleaseFast


FROM debian:12-slim

ENV PG_USER=benchmarkdbuser
ENV PG_PASS=benchmarkdbpass
ENV PG_DB=hello_world
ENV PG_HOST=tfb-database
ENV PG_PORT=5432

RUN apt-get -qq update
RUN apt-get -qy install ca-certificates

COPY --from=build /app/zig-out/bin/dusty /server

EXPOSE 3000

ENTRYPOINT ["/server"]
136 changes: 136 additions & 0 deletions frameworks/Zig/dusty/src/endpoints.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
const std = @import("std");
const dusty = @import("dusty");
const pg = @import("pg");
const datetimez = @import("datetimez");

pub var date_str: [29]u8 = undefined;

pub const Global = struct {
pool: *pg.Pool,
rand: *std.Random,
};

const World = struct {
id: i32,
randomNumber: i32,
};

const Fortune = struct {
id: i32,
message: []const u8,
};

pub fn plaintext(_: *Global, _: *dusty.Request, res: *dusty.Response) !void {
try setHeaders(res);

try res.header("Content-Type", "text/plain");
res.body = "Hello, World!";
}

pub fn json(_: *Global, _: *dusty.Request, res: *dusty.Response) !void {
try setHeaders(res);

try res.json(.{ .message = "Hello, World!" }, .{});
}

pub fn db(global: *Global, _: *dusty.Request, res: *dusty.Response) !void {
try setHeaders(res);

const random_number = 1 + (global.rand.uintAtMostBiased(u32, 9999));

const world = getWorld(global.pool, random_number) catch |err| {
std.debug.print("Error querying database: {}\n", .{err});
return;
};

try res.json(world, .{});
}

pub fn fortune(global: *Global, _: *dusty.Request, res: *dusty.Response) !void {
try setHeaders(res);

const fortunes_html = try getFortunesHtml(res.arena, global.pool);

try res.header("Content-Type", "text/html; charset=utf-8");
res.body = fortunes_html;
}

fn getWorld(pool: *pg.Pool, random_number: u32) !World {
var conn = try pool.acquire();
defer conn.release();

const row_result = try conn.row("SELECT id, randomNumber FROM World WHERE id = $1", .{random_number});

var row = row_result.?;
defer row.deinit() catch {};

return World{ .id = row.get(i32, 0), .randomNumber = row.get(i32, 1) };
}

fn setHeaders(res: *dusty.Response) !void {
try res.header("Server", "Dusty");
try res.header("Date", try res.arena.dupe(u8, &date_str));
}

fn getFortunesHtml(allocator: std.mem.Allocator, pool: *pg.Pool) ![]const u8 {
const fortunes = try getFortunes(allocator, pool);

var sb = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0);

const writer = sb.writer(allocator);
try sb.appendSlice(allocator, "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>");

for (fortunes) |ft| {
try writer.print("<tr><td>{d}</td><td>{s}</td></tr>", .{
ft.id,
try escape_html(allocator, ft.message),
});
}

try sb.appendSlice(allocator, "</table></body></html>");

return sb.toOwnedSlice(allocator);
}

fn getFortunes(allocator: std.mem.Allocator, pool: *pg.Pool) ![]const Fortune {
var conn = try pool.acquire();
defer conn.release();

var rows = try conn.query("SELECT id, message FROM Fortune", .{});
defer rows.deinit();

var fortunes = try std.ArrayListUnmanaged(Fortune).initCapacity(allocator, 0);
defer fortunes.deinit(allocator);

while (try rows.next()) |row| {
const current_fortune = Fortune{ .id = row.get(i32, 0), .message = row.get([]const u8, 1) };
try fortunes.append(allocator, current_fortune);
}

const zero_fortune = Fortune{ .id = 0, .message = "Additional fortune added at request time." };
try fortunes.append(allocator, zero_fortune);

const fortunes_slice = try fortunes.toOwnedSlice(allocator);
std.mem.sort(Fortune, fortunes_slice, {}, cmpFortuneByMessage);

return fortunes_slice;
}

fn cmpFortuneByMessage(_: void, a: Fortune, b: Fortune) bool {
return std.mem.order(u8, a.message, b.message).compare(std.math.CompareOperator.lt);
}

fn escape_html(allocator: std.mem.Allocator, input: []const u8) ![]const u8 {
var output = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0);
defer output.deinit(allocator);

for (input) |char| {
switch (char) {
'<' => try output.appendSlice(allocator, "&lt;"),
'>' => try output.appendSlice(allocator, "&gt;"),
else => try output.append(allocator, char),
}
}

return output.toOwnedSlice(allocator);
}
Loading
Loading