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 all 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

27 changes: 27 additions & 0 deletions frameworks/Zig/dusty/benchmark_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"framework": "dusty",
"maintainers": ["dragosv"],
"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