98db6e5d
main: let runScenarios defers run before process exit
a73x 2026-04-19 13:36
Switches runScenarios return type from !void to !u8 and replaces every std.process.exit call with `return N`. main() exits with the returned code after control returns. Fixes a leak of Failure.label strings (via rc.deinit) on the success path and every mismatch path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
diff --git a/src/main.zig b/src/main.zig index e73dc0e..22028e4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -180,7 +180,8 @@ pub fn main() !void { } if (args.len >= 2 and std.mem.eql(u8, args[1], "--scenario")) { return runScenarios(alloc, args[1..]); const code = try runScenarios(alloc, args[1..]); std.process.exit(code); } return runTerminal(alloc, null); @@ -205,23 +206,23 @@ fn basenameNoExt(path: []const u8) []const u8 { /// 4 — one or more captures mismatched (unless WAYSTTY_SCENARIO_UPDATE=1) /// 5 — Vulkan-side timeout (mapped from CallbackFailed conservatively) /// 6 — other runtime error pub fn runScenarios(alloc: std.mem.Allocator, argv: []const [:0]const u8) !void { pub fn runScenarios(alloc: std.mem.Allocator, argv: []const [:0]const u8) !u8 { if (argv.len < 2) { std.debug.print("usage: waystty --scenario <path>\n", .{}); std.process.exit(@intFromEnum(scenario_runtime.ExitCode.parse_error)); return @intFromEnum(scenario_runtime.ExitCode.parse_error); } const scenario_path = argv[1]; const source = std.fs.cwd().readFileAlloc(alloc, scenario_path, 1 * 1024 * 1024) catch |err| { std.debug.print("scenario: cannot read {s}: {s}\n", .{ scenario_path, @errorName(err) }); std.process.exit(@intFromEnum(scenario_runtime.ExitCode.parse_error)); return @intFromEnum(scenario_runtime.ExitCode.parse_error); }; defer alloc.free(source); var diag: scenario.Diagnostic = .{}; var parsed = scenario.parse(alloc, source, &diag) catch { std.debug.print("scenario: {s}:{d}: {s}\n", .{ scenario_path, diag.line, diag.message }); std.process.exit(@intFromEnum(scenario_runtime.ExitCode.parse_error)); return @intFromEnum(scenario_runtime.ExitCode.parse_error); }; defer parsed.deinit(); @@ -238,13 +239,13 @@ pub fn runScenarios(alloc: std.mem.Allocator, argv: []const [:0]const u8) !void runTerminal(alloc, &rc) catch |err| { std.debug.print("scenario {s}: runtime error: {s}\n", .{ scenario_name, @errorName(err) }); std.process.exit(@intFromEnum(scenario_runtime.ExitCode.other_error)); return @intFromEnum(scenario_runtime.ExitCode.other_error); }; if (rc.tick_fatal) |err| { const code = scenario_runtime.mapTickError(err); std.debug.print("scenario {s}: {s}\n", .{ scenario_name, @errorName(err) }); std.process.exit(@intFromEnum(code)); return @intFromEnum(code); } if (rc.failures.items.len > 0) { @@ -263,11 +264,11 @@ pub fn runScenarios(alloc: std.mem.Allocator, argv: []const [:0]const u8) !void .{m.label}, ), }; if (!update) std.process.exit(@intFromEnum(scenario_runtime.ExitCode.assertion_mismatch)); if (!update) return @intFromEnum(scenario_runtime.ExitCode.assertion_mismatch); } std.debug.print("scenario {s}: OK\n", .{scenario_name}); std.process.exit(0); return 0; } fn runTerminal(alloc: std.mem.Allocator, tick_ctx: ?*scenario_runtime.RunContext) !void {