Skip to content

Commit 778e126

Browse files
chrisbbreuerclaude
andcommitted
fix: use Zig 0.15.1 APIs for compatibility with pantry
Replace std.Io.Dir/File, readStreaming, writePositionalAll, and std.process.run with std.fs.cwd(), readToEndAlloc, writeAll, and std.process.Child.run for Zig 0.15.1 compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2e9da72 commit 778e126

2 files changed

Lines changed: 51 additions & 77 deletions

File tree

src/config_loader.zig

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,7 @@ const EnvProcessor = @import("services/env_processor.zig").EnvProcessor;
66
const merge = @import("merge.zig");
77
const utils = @import("utils.zig");
88

9-
// Zig 0.16+ IO helper
10-
var io_instance: std.Io.Threaded = .init_single_threaded;
11-
fn getIo() std.Io {
12-
return io_instance.io();
13-
}
14-
15-
/// Get the absolute path of a file relative to a tmpDir (Zig 0.16 compat)
9+
/// Get the absolute path of a file relative to a tmpDir
1610
fn tmpDirRealPath(allocator: std.mem.Allocator, tmp: *std.testing.TmpDir, sub_path: []const u8) ![]const u8 {
1711
// tmpDir is at .zig-cache/tmp/{sub_path} relative to cwd
1812
const c_realpath = std.c.realpath;
@@ -52,9 +46,9 @@ pub const ConfigLoader = struct {
5246
self: *ConfigLoader,
5347
options: types.LoadOptions,
5448
) !types.UntypedConfigResult {
55-
var sources = std.ArrayList(types.SourceInfo){};
56-
try sources.ensureTotalCapacity(self.allocator, 4);
57-
defer sources.deinit(self.allocator);
49+
var sources = std.ArrayList(types.SourceInfo).init(self.allocator);
50+
try sources.ensureTotalCapacity(4);
51+
defer sources.deinit();
5852

5953
// Determine CWD - allocate if not provided
6054
var cwd_buf: [std.fs.max_path_bytes]u8 = undefined;
@@ -79,7 +73,7 @@ pub const ConfigLoader = struct {
7973
final_config = parsed.value;
8074
parsed_json = parsed;
8175
primary_source = .file_local;
82-
try sources.append(self.allocator, types.SourceInfo{
76+
try sources.append(types.SourceInfo{
8377
.source = .file_local,
8478
.path = null,
8579
.priority = 3,
@@ -92,7 +86,7 @@ pub const ConfigLoader = struct {
9286
final_config = parsed.value;
9387
parsed_json = parsed;
9488
primary_source = .file_home;
95-
try sources.append(self.allocator, types.SourceInfo{
89+
try sources.append(types.SourceInfo{
9690
.source = .file_home,
9791
.path = null,
9892
.priority = 2,
@@ -104,7 +98,7 @@ pub const ConfigLoader = struct {
10498
if (final_config == null and options.defaults != null) {
10599
final_config = try utils.cloneJsonValue(self.allocator, options.defaults.?);
106100
primary_source = .defaults;
107-
try sources.append(self.allocator, types.SourceInfo{
101+
try sources.append(types.SourceInfo{
108102
.source = .defaults,
109103
.path = null,
110104
.priority = 0,
@@ -136,7 +130,7 @@ pub const ConfigLoader = struct {
136130
// Check if env vars made changes
137131
const config_was_modified = try self.configsAreDifferent(final_config.?, with_env);
138132
if (config_was_modified) {
139-
try sources.append(self.allocator, types.SourceInfo{
133+
try sources.append(types.SourceInfo{
140134
.source = .env_vars,
141135
.path = null,
142136
.priority = 1,
@@ -157,7 +151,7 @@ pub const ConfigLoader = struct {
157151
return types.UntypedConfigResult{
158152
.config = with_env,
159153
.source = primary_source,
160-
.sources = try sources.toOwnedSlice(self.allocator),
154+
.sources = try sources.toOwnedSlice(),
161155
.loaded_at = getCurrentTimestamp(),
162156
.allocator = self.allocator,
163157
.parsed_json = parsed_json,
@@ -321,9 +315,9 @@ test "loadConfig loads typed config from file" {
321315
var tmp = std.testing.tmpDir(.{});
322316
defer tmp.cleanup();
323317

324-
const file = try tmp.dir.createFile(getIo(), "test.json", .{});
325-
defer file.close(getIo());
326-
try file.writePositionalAll(getIo(), "{\"loaded\": true, \"count\": 42}", 0);
318+
const file = try tmp.dir.createFile("test.json", .{});
319+
defer file.close();
320+
try file.writeAll("{\"loaded\": true, \"count\": 42}");
327321

328322
const cwd = try tmpDirRealPath(allocator, &tmp, ".");
329323
defer allocator.free(cwd);
@@ -351,9 +345,9 @@ test "loadConfig extracts nested key from package.json" {
351345
defer tmp.cleanup();
352346

353347
// Create a package.json with a "den" section
354-
const file = try tmp.dir.createFile(getIo(), "package.json", .{});
355-
defer file.close(getIo());
356-
try file.writePositionalAll(getIo(),
348+
const file = try tmp.dir.createFile("package.json", .{});
349+
defer file.close();
350+
try file.writeAll(
357351
\\{
358352
\\ "name": "my-project",
359353
\\ "version": "1.0.0",
@@ -362,7 +356,7 @@ test "loadConfig extracts nested key from package.json" {
362356
\\ "port": 3000
363357
\\ }
364358
\\}
365-
, 0);
359+
);
366360

367361
const cwd = try tmpDirRealPath(allocator, &tmp, ".");
368362
defer allocator.free(cwd);
@@ -389,9 +383,9 @@ test "loadConfig extracts deeply nested key" {
389383
var tmp = std.testing.tmpDir(.{});
390384
defer tmp.cleanup();
391385

392-
const file = try tmp.dir.createFile(getIo(), "config.json", .{});
393-
defer file.close(getIo());
394-
try file.writePositionalAll(getIo(),
386+
const file = try tmp.dir.createFile("config.json", .{});
387+
defer file.close();
388+
try file.writeAll(
395389
\\{
396390
\\ "tooling": {
397391
\\ "shell": {
@@ -400,7 +394,7 @@ test "loadConfig extracts deeply nested key" {
400394
\\ }
401395
\\ }
402396
\\}
403-
, 0);
397+
);
404398

405399
const cwd = try tmpDirRealPath(allocator, &tmp, ".");
406400
defer allocator.free(cwd);

src/services/file_loader.zig

Lines changed: 31 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,18 @@ const types = @import("../types.zig");
44
const errors = @import("../errors.zig");
55
const utils = @import("../utils.zig");
66

7-
// Zig 0.16+ IO helper
8-
var io_instance: std.Io.Threaded = .init_single_threaded;
9-
fn getIo() std.Io {
10-
return io_instance.io();
11-
}
12-
13-
/// Check if a file exists using cross-platform Io.Dir
7+
/// Check if a file exists
148
fn fileExists(path: []const u8) bool {
15-
const file = std.Io.Dir.cwd().openFile(getIo(), path, .{ .mode = .read_only }) catch return false;
16-
file.close(getIo());
9+
const file = std.fs.cwd().openFile(path, .{}) catch return false;
10+
file.close();
1711
return true;
1812
}
1913

2014
/// Strip single-line (//) and multi-line (/* */) comments from JSON content
2115
fn stripJsonComments(allocator: std.mem.Allocator, content: []const u8) ![]const u8 {
22-
var result = std.ArrayList(u8){};
23-
try result.ensureTotalCapacity(allocator, content.len);
24-
errdefer result.deinit(allocator);
16+
var result = std.ArrayList(u8).init(allocator);
17+
try result.ensureTotalCapacity(content.len);
18+
errdefer result.deinit();
2519

2620
var i: usize = 0;
2721
var in_string = false;
@@ -33,12 +27,12 @@ fn stripJsonComments(allocator: std.mem.Allocator, content: []const u8) ![]const
3327
// Handle string literals (comments inside strings should not be stripped)
3428
if (c == '"' and !escape_next) {
3529
in_string = !in_string;
36-
try result.append(allocator, c);
30+
try result.append(c);
3731
continue;
3832
}
3933

4034
if (in_string) {
41-
try result.append(allocator, c);
35+
try result.append(c);
4236
escape_next = (c == '\\' and !escape_next);
4337
continue;
4438
}
@@ -50,7 +44,7 @@ fn stripJsonComments(allocator: std.mem.Allocator, content: []const u8) ![]const
5044
// Skip until end of line
5145
i += 2;
5246
while (i < content.len and content[i] != '\n') : (i += 1) {}
53-
if (i < content.len) try result.append(allocator, '\n'); // Preserve newline
47+
if (i < content.len) try result.append('\n'); // Preserve newline
5448
continue;
5549
}
5650

@@ -67,10 +61,10 @@ fn stripJsonComments(allocator: std.mem.Allocator, content: []const u8) ![]const
6761
continue;
6862
}
6963

70-
try result.append(allocator, c);
64+
try result.append(c);
7165
}
7266

73-
return result.toOwnedSlice(allocator);
67+
return try result.toOwnedSlice();
7468
}
7569

7670
/// File loader service for discovering and loading configuration files
@@ -156,33 +150,18 @@ pub const FileLoader = struct {
156150
return self.loadTypeScriptConfig(path);
157151
}
158152

159-
// Use cross-platform Io.Dir for file access
160-
const file = std.Io.Dir.cwd().openFile(getIo(), path, .{ .mode = .read_only }) catch |err| {
153+
// Open file
154+
const file = std.fs.cwd().openFile(path, .{}) catch |err| {
161155
return switch (err) {
162156
error.FileNotFound => errors.ZigConfigError.ConfigFileNotFound,
163157
error.AccessDenied => errors.ZigConfigError.ConfigFilePermissionDenied,
164158
else => errors.ZigConfigError.ConfigFileInvalid,
165159
};
166160
};
167-
defer file.close(getIo());
168-
169-
// Read file content using Io.File (cross-platform)
170-
var content = std.ArrayList(u8).empty;
171-
defer content.deinit(self.allocator);
172-
173-
var buf: [4096]u8 = undefined;
174-
while (true) {
175-
const bufs = [_][]u8{&buf};
176-
const n = file.readStreaming(getIo(), &bufs) catch {
177-
return errors.ZigConfigError.ConfigFileInvalid;
178-
};
179-
if (n == 0) break;
180-
content.appendSlice(self.allocator, buf[0..n]) catch {
181-
return errors.ZigConfigError.ConfigFileInvalid;
182-
};
183-
}
161+
defer file.close();
184162

185-
const owned_content = content.toOwnedSlice(self.allocator) catch {
163+
// Read file content
164+
const owned_content = file.readToEndAlloc(self.allocator, 1024 * 1024) catch {
186165
return errors.ZigConfigError.ConfigFileInvalid;
187166
};
188167
defer self.allocator.free(owned_content);
@@ -226,8 +205,9 @@ pub const FileLoader = struct {
226205
) catch return errors.ZigConfigError.ConfigFileInvalid;
227206
defer self.allocator.free(script);
228207

229-
// Use cross-platform std.process.run to execute bun
230-
const result = std.process.run(self.allocator, getIo(), .{
208+
// Use std.process.Child to execute bun
209+
const result = std.process.Child.run(.{
210+
.allocator = self.allocator,
231211
.argv = &.{ "bun", "-e", script },
232212
}) catch {
233213
return errors.ZigConfigError.ConfigFileInvalid;
@@ -260,12 +240,12 @@ pub const FileLoader = struct {
260240
/// Get file modification time for cache invalidation
261241
pub fn getModTime(self: *FileLoader, path: []const u8) !i64 {
262242
_ = self;
263-
// Open file using cross-platform Io.Dir and use File.stat
264-
const file = std.Io.Dir.cwd().openFile(getIo(), path, .{ .mode = .read_only }) catch return error.FileNotFound;
265-
defer file.close(getIo());
266-
const stat = try file.stat(getIo());
267-
// mtime is in nanoseconds, convert to seconds
268-
return @intCast(@divTrunc(stat.mtime.nanoseconds, std.time.ns_per_s));
243+
// Open file and use stat
244+
const file = std.fs.cwd().openFile(path, .{}) catch return error.FileNotFound;
245+
defer file.close();
246+
const stat = try file.stat();
247+
// mtime is in nanoseconds (i128), convert to seconds
248+
return @intCast(@divTrunc(stat.mtime, std.time.ns_per_s));
269249
}
270250
};
271251

@@ -294,9 +274,9 @@ test "FileLoader.findConfigFile finds in project root" {
294274
var tmp = std.testing.tmpDir(.{});
295275
defer tmp.cleanup();
296276

297-
const file = try tmp.dir.createFile(getIo(), "test.json", .{});
298-
defer file.close(getIo());
299-
try file.writePositionalAll(getIo(), "{}", 0);
277+
const file = try tmp.dir.createFile("test.json", .{});
278+
defer file.close();
279+
try file.writeAll("{}");
300280

301281
const cwd = try tmpDirRealPath(allocator, &tmp, ".");
302282
defer allocator.free(cwd);
@@ -330,9 +310,9 @@ test "FileLoader.loadConfigFile parses JSON" {
330310
var tmp = std.testing.tmpDir(.{});
331311
defer tmp.cleanup();
332312

333-
const file = try tmp.dir.createFile(getIo(), "test.json", .{});
334-
defer file.close(getIo());
335-
try file.writePositionalAll(getIo(), "{\"key\": \"value\"}", 0);
313+
const file = try tmp.dir.createFile("test.json", .{});
314+
defer file.close();
315+
try file.writeAll("{\"key\": \"value\"}");
336316

337317
const path = try tmpDirRealPath(allocator, &tmp, "test.json");
338318
defer allocator.free(path);

0 commit comments

Comments
 (0)