Skip to content

Commit 6cf58b5

Browse files
committed
feat: add Zig wws module (Fixes vmware-labs#218)
This commit introduces `kits/zig/wws`, with the intention of replacing `kits/zig/worker`. The design of the new SDK improves upon the previous one by exposing utilities for parsing wws requests and serializing wws responses, to give the user more control over memory while still keeping the SDK easy to use. The name was changed to make it more ergonomic within the Zig ecosystem (the developer will pull in a `wws` dependency to interface with wws, rather than a `worker` dependency).
1 parent 6d07f12 commit 6cf58b5

File tree

6 files changed

+514
-0
lines changed

6 files changed

+514
-0
lines changed

examples/zig-module/build.zig

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const std = @import("std");
2+
const wws = @import("wws");
3+
4+
pub fn build(b: *std.Build) !void {
5+
const target = wws.getTarget(b);
6+
const optimize = b.standardOptimizeOption(.{});
7+
8+
const wws_dep = b.dependency("wws", .{});
9+
10+
const exe = b.addExecutable(.{
11+
.name = "example",
12+
.root_source_file = .{ .path = "src/main.zig" },
13+
.target = target,
14+
.optimize = optimize,
15+
});
16+
exe.wasi_exec_model = .reactor;
17+
exe.root_module.addImport("wws", wws_dep.module("wws"));
18+
19+
b.installArtifact(exe);
20+
21+
const config =
22+
\\name = "example"
23+
\\version = "1"
24+
\\[data]
25+
\\[data.kv]
26+
\\namespace = "example"
27+
;
28+
const wf = b.addWriteFiles();
29+
const config_path = wf.add("example.toml", config);
30+
31+
const install_config = b.addInstallBinFile(config_path, "example.toml");
32+
33+
b.getInstallStep().dependOn(&install_config.step);
34+
}

examples/zig-module/build.zig.zon

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
.{
2+
.name = "zig-build",
3+
// This is a [Semantic Version](https://semver.org/).
4+
// In a future version of Zig it will be used for package deduplication.
5+
.version = "0.0.0",
6+
7+
// This field is optional.
8+
// This is currently advisory only; Zig does not yet do anything
9+
// with this value.
10+
//.minimum_zig_version = "0.11.0",
11+
12+
// This field is optional.
13+
// Each dependency must either provide a `url` and `hash`, or a `path`.
14+
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
15+
// Once all dependencies are fetched, `zig build` no longer requires
16+
// internet connectivity.
17+
.dependencies = .{
18+
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
19+
//.example = .{
20+
// // When updating this field to a new URL, be sure to delete the corresponding
21+
// // `hash`, otherwise you are communicating that you expect to find the old hash at
22+
// // the new URL.
23+
// .url = "https://example.com/foo.tar.gz",
24+
//
25+
// // This is computed from the file contents of the directory of files that is
26+
// // obtained after fetching `url` and applying the inclusion rules given by
27+
// // `paths`.
28+
// //
29+
// // This field is the source of truth; packages do not come from a `url`; they
30+
// // come from a `hash`. `url` is just one of many possible mirrors for how to
31+
// // obtain a package matching this `hash`.
32+
// //
33+
// // Uses the [multihash](https://multiformats.io/multihash/) format.
34+
// .hash = "...",
35+
//
36+
// // When this is provided, the package is found in a directory relative to the
37+
// // build root. In this case the package's hash is irrelevant and therefore not
38+
// // computed. This field and `url` are mutually exclusive.
39+
// .path = "foo",
40+
//},
41+
.wws = .{
42+
.path = "../../kits/zig/wws",
43+
},
44+
},
45+
46+
// Specifies the set of files and directories that are included in this package.
47+
// Only files and directories listed here are included in the `hash` that
48+
// is computed for this package.
49+
// Paths are relative to the build root. Use the empty string (`""`) to refer to
50+
// the build root itself.
51+
// A directory listed here means that all files within, recursively, are included.
52+
.paths = .{
53+
// This makes *all* files, recursively, included in this package. It is generally
54+
// better to explicitly list the files and directories instead, to insure that
55+
// fetching from tarballs, file system paths, and version control all result
56+
// in the same contents hash.
57+
"",
58+
// For example...
59+
//"build.zig",
60+
//"build.zig.zon",
61+
//"src",
62+
//"LICENSE",
63+
//"README.md",
64+
},
65+
}

examples/zig-module/src/main.zig

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
const std = @import("std");
2+
const wws = @import("wws");
3+
4+
pub fn main() !void {
5+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
6+
defer {
7+
switch (gpa.deinit()) {
8+
.ok => {},
9+
.leak => {},
10+
}
11+
}
12+
const allocator = gpa.allocator();
13+
14+
// Parse request from stdin
15+
var event = try wws.parseStream(allocator, .{});
16+
defer event.destroy();
17+
const wws_request = event.request;
18+
19+
// Prepare response
20+
var body = std.ArrayList(u8).init(allocator);
21+
defer body.deinit();
22+
23+
try body.writer().print("{any} {s}\n", .{ wws_request.method, wws_request.url });
24+
25+
{
26+
var it = wws_request.storage.iterator();
27+
while (it.next()) |entry| {
28+
try body.writer().print("kv.{s}: {s}\n", .{ entry.key_ptr.*, entry.value_ptr.* });
29+
}
30+
}
31+
32+
var headers = wws.Headers.init(allocator);
33+
defer headers.deinit();
34+
try headers.append("Content-Type", "text/plain");
35+
36+
var storage = std.StringHashMap([]const u8).init(allocator);
37+
defer storage.deinit();
38+
defer {
39+
var it = storage.iterator();
40+
while (it.next()) |entry| {
41+
allocator.free(entry.value_ptr.*);
42+
}
43+
}
44+
45+
var counter: usize = if (wws_request.storage.get("counter")) |v| try std.fmt.parseInt(usize, v, 10) else 0;
46+
47+
// Increment counter, save the result to storage
48+
counter += 1;
49+
50+
{
51+
var buf = std.ArrayList(u8).init(allocator);
52+
defer buf.deinit();
53+
try buf.writer().print("{d}", .{counter});
54+
try storage.put("counter", try buf.toOwnedSlice());
55+
}
56+
57+
const response = try wws.formatResponse(allocator, .{
58+
.data = body.items,
59+
.status = 200,
60+
.headers = &headers,
61+
.storage = &storage,
62+
});
63+
defer allocator.free(response);
64+
65+
const stdout = std.io.getStdOut();
66+
try stdout.writer().print("{s}", .{response});
67+
}

kits/zig/wws/build.zig

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const std = @import("std");
2+
3+
pub fn build(b: *std.Build) void {
4+
_ = b.standardTargetOptions(.{});
5+
_ = b.standardOptimizeOption(.{});
6+
7+
const module = b.addModule("wws", .{
8+
.root_source_file = .{ .path = "src/wws.zig" },
9+
.target = getTarget(b),
10+
});
11+
12+
_ = module;
13+
}
14+
15+
pub inline fn getTarget(b: *std.Build) std.Build.ResolvedTarget {
16+
return b.resolveTargetQuery(.{
17+
.cpu_arch = .wasm32,
18+
.os_tag = .wasi,
19+
});
20+
}
21+
22+
pub const WwsLibOptions = struct {
23+
name: []const u8,
24+
root_source_file: std.Build.LazyPath,
25+
optimize: std.builtin.OptimizeMode,
26+
imports: []const std.Build.Module.Import = &.{},
27+
};
28+
29+
pub fn addExecutable(b: *std.Build, options: WwsLibOptions) *std.Build.Step.Compile {
30+
const exe = b.addExecutable(.{
31+
.name = options.name,
32+
.root_source_file = options.root_source_file,
33+
.target = getTarget(b),
34+
.optimize = options.optimize,
35+
});
36+
37+
exe.wasi_exec_model = .reactor;
38+
39+
for (options.imports) |import| {
40+
exe.root_module.addImport(import.name, import.module);
41+
}
42+
43+
return exe;
44+
}

kits/zig/wws/build.zig.zon

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
.{
2+
.name = "wws",
3+
// This is a [Semantic Version](https://semver.org/).
4+
// In a future version of Zig it will be used for package deduplication.
5+
.version = "0.0.0",
6+
7+
// This field is optional.
8+
// This is currently advisory only; Zig does not yet do anything
9+
// with this value.
10+
//.minimum_zig_version = "0.11.0",
11+
12+
// This field is optional.
13+
// Each dependency must either provide a `url` and `hash`, or a `path`.
14+
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
15+
// Once all dependencies are fetched, `zig build` no longer requires
16+
// internet connectivity.
17+
.dependencies = .{
18+
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
19+
//.example = .{
20+
// // When updating this field to a new URL, be sure to delete the corresponding
21+
// // `hash`, otherwise you are communicating that you expect to find the old hash at
22+
// // the new URL.
23+
// .url = "https://example.com/foo.tar.gz",
24+
//
25+
// // This is computed from the file contents of the directory of files that is
26+
// // obtained after fetching `url` and applying the inclusion rules given by
27+
// // `paths`.
28+
// //
29+
// // This field is the source of truth; packages do not come from a `url`; they
30+
// // come from a `hash`. `url` is just one of many possible mirrors for how to
31+
// // obtain a package matching this `hash`.
32+
// //
33+
// // Uses the [multihash](https://multiformats.io/multihash/) format.
34+
// .hash = "...",
35+
//
36+
// // When this is provided, the package is found in a directory relative to the
37+
// // build root. In this case the package's hash is irrelevant and therefore not
38+
// // computed. This field and `url` are mutually exclusive.
39+
// .path = "foo",
40+
//},
41+
},
42+
43+
// Specifies the set of files and directories that are included in this package.
44+
// Only files and directories listed here are included in the `hash` that
45+
// is computed for this package.
46+
// Paths are relative to the build root. Use the empty string (`""`) to refer to
47+
// the build root itself.
48+
// A directory listed here means that all files within, recursively, are included.
49+
.paths = .{
50+
// This makes *all* files, recursively, included in this package. It is generally
51+
// better to explicitly list the files and directories instead, to insure that
52+
// fetching from tarballs, file system paths, and version control all result
53+
// in the same contents hash.
54+
"",
55+
// For example...
56+
//"build.zig",
57+
//"build.zig.zon",
58+
//"src",
59+
//"LICENSE",
60+
//"README.md",
61+
},
62+
}

0 commit comments

Comments
 (0)