Skip to content

Commit 46faba4

Browse files
committed
convert to Zig 0.16 (except tests, coming soon)
1 parent 07ee890 commit 46faba4

9 files changed

Lines changed: 78 additions & 79 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.zig-cache/
2+
zig-pkg/
23
zig-out/
3-
.DS_Store
4+
.DS_Store

.tool-versions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
zig latest
1+
zig 0.16.0

build.zig

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@ pub fn build(b: *std.Build) void {
1515
.root_source_file = b.path("src/main.zig"),
1616
.target = target,
1717
.optimize = optimize,
18+
.link_libc = true,
1819
});
1920

2021
const exe = b.addExecutable(.{
2122
.name = exe_name,
2223
.root_module = exe_mod,
24+
.use_llvm = true,
2325
});
2426

2527
for (dependencies) |dependency| {
2628
const dep = b.dependency(dependency, .{ .target = target, .optimize = optimize });
2729
exe.root_module.addImport(dependency, dep.module(dependency));
2830
}
2931

30-
exe.linkLibC();
31-
3232
b.installArtifact(exe);
3333

3434
const run_cmd = b.addRunArtifact(exe);
@@ -85,10 +85,13 @@ pub fn build(b: *std.Build) void {
8585
}
8686

8787
fn findZigFiles(b: *std.Build, files: *std.ArrayList([]const u8), dir_path: []const u8) !void {
88-
var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true });
89-
defer dir.close();
88+
var threaded = std.Io.Threaded.init_single_threaded;
89+
const io = threaded.io();
90+
const cwd = std.Io.Dir.cwd();
91+
var dir = try std.Io.Dir.openDir(cwd, io, dir_path, .{ .iterate = true });
92+
defer dir.close(io);
9093
var it = dir.iterate();
91-
while (try it.next()) |entry| {
94+
while (try it.next(io)) |entry| {
9295
if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".zig")) {
9396
const full_path = try std.fs.path.join(b.allocator, &[_][]const u8{ dir_path, entry.name });
9497
try files.append(b.allocator, full_path);

build.zig.zon

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,16 @@
3737
// internet connectivity.
3838
.dependencies = .{
3939
.clap = .{
40-
.url = "git+https://github.com/Hejsil/zig-clap#5289e0753cd274d65344bef1c114284c633536ea",
41-
.hash = "clap-0.11.0-oBajB-HnAQDPCKYzwF7rO3qDFwRcD39Q0DALlTSz5H7e",
40+
.url = "git+https://github.com/Hejsil/zig-clap?ref=HEAD#8d97efa1ee1e575443c7888d5c38e1c3fc145cf5",
41+
.hash = "clap-0.12.0-oBajB7foAQDqlSwaSG5g0yq7xGbQARUsBk5T64gAOqP5",
4242
},
4343
.regex = .{
44-
.url = "git+https://github.com/bradcypert/zig-regex.git#05f641c08939f131bb82195855efa7afaf7566eb",
45-
.hash = "regex-0.1.2-axC350bnAQCyrPkH2IEfri8hebJO6IujGY1rhcgZBXpL",
44+
.url = "git+https://github.com/tiehuis/zig-regex?ref=HEAD#49c5694c8b3ee4ae4a7131035a44a69841ef4740",
45+
.hash = "regex-0.1.3-axC357jaAQBRENglwG9NTcuej8pYz1IZmfwER_AXMlHZ",
4646
},
4747
.curl = .{
48-
.url = "git+https://github.com/jiacai2050/zig-curl/?ref=HEAD#1a23d311582c1e72bdb9ec3020f82b8e2a4b8c8a",
49-
.hash = "curl-0.3.1-P4tT4cPNAAAZsLz5AsgitsBp3W0uVpoFT7IeNX2qOQlP",
48+
.url = "git+https://github.com/jiacai2050/zig-curl?ref=HEAD#f701e602d962e149338521e9848528a372c05966",
49+
.hash = "curl-0.5.1-P4tT4VsCAQBQ_kA6SBylS7sDyKXdcnuv-13p21ZNx8Az",
5050
},
5151
},
5252
.paths = .{

src/httpfile/assertion_checker.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ fn checkAssertion(
189189
.equal => {
190190
if (std.ascii.eqlIgnoreCase(assertion.key, "status")) {
191191
const assert_status_code = try std.fmt.parseInt(u16, assertion.value, 10);
192-
const expected_status = try std.meta.intToEnum(http.Status, assert_status_code);
192+
const expected_status = std.enums.fromInt(http.Status, assert_status_code) orelse return error.UnsupportedStatusCode;
193193
if (response.status != expected_status) {
194194
const actual_str = try std.fmt.allocPrint(diagnostic.allocator, "{d}", .{@intFromEnum(response.status.?)});
195195
defer diagnostic.allocator.free(actual_str);
@@ -214,7 +214,7 @@ fn checkAssertion(
214214
.not_equal => {
215215
if (std.ascii.eqlIgnoreCase(assertion.key, "status")) {
216216
const assert_status_code = try std.fmt.parseInt(u16, assertion.value, 10);
217-
const expected_status = try std.meta.intToEnum(http.Status, assert_status_code);
217+
const expected_status = std.enums.fromInt(http.Status, assert_status_code) orelse return error.UnsupportedStatusCode;
218218
if (response.status == expected_status) {
219219
const actual_str = try std.fmt.allocPrint(diagnostic.allocator, "{d}", .{@intFromEnum(response.status.?)});
220220
defer diagnostic.allocator.free(actual_str);

src/httpfile/http_client.zig

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ pub const HttpClient = struct {
4040
allocator: Allocator,
4141
client: http.Client,
4242

43-
pub fn init(allocator: Allocator) HttpClient {
43+
pub fn init(io: std.Io, allocator: Allocator) HttpClient {
4444
return .{
4545
.allocator = allocator,
46-
.client = http.Client{ .allocator = allocator },
46+
.client = http.Client{ .allocator = allocator, .io = io },
4747
};
4848
}
4949

@@ -65,9 +65,9 @@ pub const HttpClient = struct {
6565

6666
/// Executes a single HTTP request and returns the response.
6767
pub fn execute(self: *HttpClient, request: *const httpfiles.HttpRequest) !HttpResponse {
68-
const ca_bundle = try curl.allocCABundle(self.allocator);
69-
defer ca_bundle.deinit();
70-
const easy = try curl.Easy.init(.{
68+
var ca_bundle = try curl.allocCABundle(self.allocator, self.client.io);
69+
defer ca_bundle.deinit(self.allocator);
70+
var easy = try curl.Easy.init(.{
7171
.ca_bundle = ca_bundle,
7272
});
7373

src/httpfile/parser.zig

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,12 @@ pub const HttpRequest = struct {
8686
}
8787
};
8888

89-
pub fn parseFile(allocator: Allocator, file_path: []const u8) !ArrayList(HttpRequest) {
90-
const file = try std.fs.cwd().openFile(file_path, .{});
91-
defer file.close();
92-
const file_content = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
89+
pub fn parseFile(io: std.Io, allocator: Allocator, file_path: []const u8) !ArrayList(HttpRequest) {
90+
const cwd = std.Io.Dir.cwd();
91+
const file = try cwd.openFile(io, file_path, .{});
92+
defer file.close(io);
93+
var file_reader = file.reader(io, &.{});
94+
const file_content = try file_reader.interface.allocRemaining(allocator, std.Io.Limit.unlimited);
9395
defer allocator.free(file_content);
9496
return try parseContent(allocator, file_content);
9597
}

src/main.zig

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,27 @@ const Client = @import("./httpfile/http_client.zig");
66
const AssertionChecker = @import("./httpfile/assertion_checker.zig");
77
const TestReporter = @import("./reporters/test_reporter.zig");
88

9-
pub fn main() !void {
9+
pub fn main(init: std.process.Init) !void {
1010
// Use a debug allocator for leak detection.
11-
var debug = std.heap.DebugAllocator(.{}){};
12-
defer _ = debug.deinit();
13-
const allocator = debug.allocator();
11+
const allocator = init.gpa;
1412

1513
var stdout_buffer: [1024]u8 = undefined;
16-
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
14+
var stdout_writer = std.Io.File.stdout().writer(init.io, &stdout_buffer);
1715
const stdout = &stdout_writer.interface;
1816
defer stdout.flush() catch {};
1917

2018
var stderr_buffer: [1024]u8 = undefined;
21-
var stderr_writer = std.fs.File.stderr().writer(&stderr_buffer);
19+
var stderr_writer = std.Io.File.stderr().writer(init.io, &stderr_buffer);
2220
const stderr = &stderr_writer.interface;
2321
defer stderr.flush() catch {};
2422

25-
// Determine thread count from environment.
26-
const threads = std.process.parseEnvVarInt("HTTP_THREAD_COUNT", usize, 10) catch 1;
27-
2823
// Parse CLI arguments.
2924
const params = comptime clap.parseParamsComptime(
3025
\\-h, --help Display this help and exit
3126
\\<str>... Executes the HTTP specs in the provided files (if omitted, all files in subdirectories will be ran instead)
3227
);
3328
var diag = clap.Diagnostic{};
34-
var res = clap.parse(clap.Help, &params, clap.parsers.default, .{
29+
var res = clap.parse(clap.Help, &params, clap.parsers.default, init.minimal.args, .{
3530
.diagnostic = &diag,
3631
.allocator = allocator,
3732
}) catch |err| {
@@ -51,38 +46,33 @@ pub fn main() !void {
5146
for (files.items) |file| allocator.free(file);
5247
files.deinit(allocator);
5348
}
54-
try collectSpecFiles(allocator, &files, res);
49+
try collectSpecFiles(init.io, allocator, &files, res);
5550

56-
// Set up thread pool and reporter.
57-
var pool: std.Thread.Pool = undefined;
58-
try pool.init(.{
59-
.allocator = allocator,
60-
.n_jobs = threads,
61-
});
62-
defer pool.deinit();
51+
var group: std.Io.Group = .init;
52+
errdefer group.cancel(init.io);
6353

64-
var wg: std.Thread.WaitGroup = .{};
6554
var reporter = TestReporter.BasicReporter.init();
6655

6756
// Run all tests in parallel.
6857
for (files.items) |path| {
69-
pool.spawnWg(&wg, runTest, .{ allocator, &reporter, path, stderr });
58+
group.async(init.io, runTest, .{ init.io, allocator, &reporter, path, stderr });
7059
}
71-
wg.wait();
60+
try group.await(init.io);
7261

7362
// Print summary.
7463
reporter.report(stdout);
7564
}
7665

7766
/// Collects all HTTP spec files to run, based on CLI args.
7867
fn collectSpecFiles(
68+
io: std.Io,
7969
allocator: std.mem.Allocator,
8070
files: *std.ArrayList([]const u8),
8171
res: anytype,
8272
) !void {
8373
if (res.positionals[0].len == 0) {
8474
// No args: find all .http/.httpspec files recursively from cwd.
85-
const http_files = try listHttpFiles(allocator, ".");
75+
const http_files = try listHttpFiles(io, allocator, ".");
8676
defer allocator.free(http_files);
8777
for (http_files) |file| try files.append(allocator, file);
8878
} else {
@@ -93,9 +83,10 @@ fn collectSpecFiles(
9383
{
9484
try files.append(allocator, try allocator.dupe(u8, pos));
9585
} else {
96-
const file_info = try std.fs.cwd().statFile(pos);
86+
const cwd = std.Io.Dir.cwd();
87+
const file_info = try cwd.statFile(io, pos, .{});
9788
if (file_info.kind != .directory) return error.InvalidPositionalArgument;
98-
const http_files = try listHttpFiles(allocator, pos);
89+
const http_files = try listHttpFiles(io, allocator, pos);
9990
defer allocator.free(http_files);
10091
for (http_files) |file| try files.append(allocator, file);
10192
}
@@ -105,38 +96,39 @@ fn collectSpecFiles(
10596

10697
/// Runs all requests in a spec file and updates the reporter.
10798
fn runTest(
99+
io: std.Io,
108100
base_allocator: std.mem.Allocator,
109101
reporter: *TestReporter.BasicReporter,
110102
path: []const u8,
111-
stderr: *std.io.Writer,
112-
) void {
103+
stderr: *std.Io.Writer,
104+
) !void {
113105
// Create arena allocator for this test to provide memory isolation
114106
var arena = std.heap.ArenaAllocator.init(base_allocator);
115107
defer arena.deinit(); // Automatically frees all test allocations
116108
const allocator = arena.allocator();
117109

118110
var has_failure = false;
119-
reporter.incTestCount();
111+
try reporter.incTestCount(io);
120112

121-
var items = HttpParser.parseFile(allocator, path) catch |err| {
122-
reporter.incTestInvalid();
113+
var items = HttpParser.parseFile(io, allocator, path) catch |err| {
114+
try reporter.incTestInvalid(io);
123115
std.debug.print("Failed to parse file {s}: {s}\n", .{ path, @errorName(err) });
124116
return;
125117
};
126118
const owned_items = items.toOwnedSlice(allocator) catch |err| {
127-
reporter.incTestInvalid();
119+
try reporter.incTestInvalid(io);
128120
std.debug.print("Failed to convert items to owned slice in file {s}: {s}\n", .{ path, @errorName(err) });
129121
return;
130122
};
131123
// Note: No need to manually free owned_items since arena will handle it
132124

133-
var client = Client.HttpClient.init(allocator);
125+
var client = Client.HttpClient.init(io, allocator);
134126
defer client.deinit();
135127

136128
for (owned_items) |*owned_item| {
137129
// Note: No need to manually deinit owned_item since arena will handle it
138130
var responses = client.execute(owned_item) catch |err| {
139-
reporter.incTestInvalid();
131+
try reporter.incTestInvalid(io);
140132
std.debug.print("Failed to execute request in file {s}: {s}\n", .{ path, @errorName(err) });
141133
return;
142134
};
@@ -151,33 +143,34 @@ fn runTest(
151143
}
152144
}
153145
if (!has_failure) {
154-
reporter.incTestPass();
146+
try reporter.incTestPass(io);
155147
} else {
156-
reporter.incTestFail();
148+
try reporter.incTestFail(io);
157149
}
158150
}
159151

160152
/// Recursively finds all .http/.httpspec files in a directory.
161-
fn listHttpFiles(allocator: std.mem.Allocator, dir: []const u8) ![][]const u8 {
153+
fn listHttpFiles(io: std.Io, allocator: std.mem.Allocator, dir_path: []const u8) ![][]const u8 {
162154
var files: std.ArrayList([]const u8) = .empty;
163155
defer files.deinit(allocator);
164156

165-
var dir_entry = try std.fs.cwd().openDir(dir, .{ .iterate = true });
166-
defer dir_entry.close();
157+
const cwd = std.Io.Dir.cwd();
158+
var dir_entry = try std.Io.Dir.openDir(cwd, io, dir_path, .{ .iterate = true });
159+
defer dir_entry.close(io);
167160

168161
var it = dir_entry.iterate();
169-
while (try it.next()) |entry| {
162+
while (try it.next(io)) |entry| {
170163
if (entry.kind == .directory) {
171164
if (std.mem.eql(u8, entry.name, ".") or std.mem.eql(u8, entry.name, "..")) continue;
172-
const subdir = try std.fs.path.join(allocator, &[_][]const u8{ dir, entry.name });
165+
const subdir = try std.fs.path.join(allocator, &[_][]const u8{ dir_path, entry.name });
173166
defer allocator.free(subdir);
174-
const sub_files = try listHttpFiles(allocator, subdir);
167+
const sub_files = try listHttpFiles(io, allocator, subdir);
175168
defer allocator.free(sub_files);
176169
for (sub_files) |file| try files.append(allocator, file);
177170
} else if (std.mem.eql(u8, std.fs.path.extension(entry.name), ".http") or
178171
std.mem.eql(u8, std.fs.path.extension(entry.name), ".httpspec"))
179172
{
180-
const file_path = try std.fs.path.join(allocator, &[_][]const u8{ dir, entry.name });
173+
const file_path = try std.fs.path.join(allocator, &[_][]const u8{ dir_path, entry.name });
181174
try files.append(allocator, file_path);
182175
}
183176
}

src/reporters/test_reporter.zig

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ pub const BasicReporter = struct {
55
test_pass: usize,
66
test_fail: usize,
77
test_invalid: usize,
8-
m: std.Thread.Mutex,
8+
m: std.Io.Mutex,
99

1010
pub fn init() BasicReporter {
1111
return .{
1212
.test_count = 0,
1313
.test_pass = 0,
1414
.test_fail = 0,
1515
.test_invalid = 0,
16-
.m = std.Thread.Mutex{},
16+
.m = .init,
1717
};
1818
}
1919

@@ -31,24 +31,24 @@ pub const BasicReporter = struct {
3131
};
3232
}
3333

34-
pub fn incTestCount(self: *BasicReporter) void {
35-
self.m.lock();
36-
defer self.m.unlock();
34+
pub fn incTestCount(self: *BasicReporter, io: std.Io) !void {
35+
try self.m.lock(io);
36+
defer self.m.unlock(io);
3737
self.test_count += 1;
3838
}
39-
pub fn incTestPass(self: *BasicReporter) void {
40-
self.m.lock();
41-
defer self.m.unlock();
39+
pub fn incTestPass(self: *BasicReporter, io: std.Io) !void {
40+
try self.m.lock(io);
41+
defer self.m.unlock(io);
4242
self.test_pass += 1;
4343
}
44-
pub fn incTestFail(self: *BasicReporter) void {
45-
self.m.lock();
46-
defer self.m.unlock();
44+
pub fn incTestFail(self: *BasicReporter, io: std.Io) !void {
45+
try self.m.lock(io);
46+
defer self.m.unlock(io);
4747
self.test_fail += 1;
4848
}
49-
pub fn incTestInvalid(self: *BasicReporter) void {
50-
self.m.lock();
51-
defer self.m.unlock();
49+
pub fn incTestInvalid(self: *BasicReporter, io: std.Io) !void {
50+
try self.m.lock(io);
51+
defer self.m.unlock(io);
5252
self.test_invalid += 1;
5353
}
5454
};

0 commit comments

Comments
 (0)