From eabde87a9e98069adfe53c2f4d2b4824b56ee5a7 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Mon, 13 Jul 2026 00:07:44 +0200 Subject: [PATCH 1/9] Add generated header helper with deterministic render and unit tests --- src/generators/generated_header.zig | 34 ++++++++++++++++++++++++++++ src/tests.zig | 2 ++ src/tests/generated_header_tests.zig | 15 ++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 src/generators/generated_header.zig create mode 100644 src/tests/generated_header_tests.zig diff --git a/src/generators/generated_header.zig b/src/generators/generated_header.zig new file mode 100644 index 0000000..2612f1d --- /dev/null +++ b/src/generators/generated_header.zig @@ -0,0 +1,34 @@ +const std = @import("std"); + +pub fn render(allocator: std.mem.Allocator, version: []const u8, timestamp: []const u8) ![]const u8 { + return try std.fmt.allocPrint(allocator, + \\// this code was generated by openapi2zig {s} on {s} + \\// changes to this file may cause incorrect behavior and will be lost if the code is regenerated + \\ + \\ + , .{ version, timestamp }); +} + +pub fn renderNow(allocator: std.mem.Allocator, version: []const u8) ![]const u8 { + const timestamp = try formatUtcTimestamp(allocator); + defer allocator.free(timestamp); + return try render(allocator, version, timestamp); +} + +fn formatUtcTimestamp(allocator: std.mem.Allocator) ![]const u8 { + const timestamp = std.time.timestamp(); + const epoch_seconds = std.time.epoch.EpochSeconds{ .secs = @as(u64, @intCast(timestamp)) }; + const epoch_day = epoch_seconds.getEpochDay(); + const day_seconds = epoch_seconds.getDaySeconds(); + const year_day = epoch_day.calculateYearDay(); + const month_day = year_day.calculateMonthDay(); + + return try std.fmt.allocPrint(allocator, "{d}-{d:0>2}-{d:0>2} {d:0>2}:{d:0>2}:{d:0>2} UTC", .{ + year_day.year, + month_day.month.numeric(), + month_day.day_index + 1, + day_seconds.getHoursIntoDay(), + day_seconds.getMinutesIntoHour(), + day_seconds.getSecondsIntoMinute(), + }); +} diff --git a/src/tests.zig b/src/tests.zig index 5349153..7192c11 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -10,6 +10,7 @@ const resource_wrapper_tests = @import("tests/resource_wrapper_tests.zig"); const model_typing_tests = @import("tests/model_typing_tests.zig"); const binary_payload_tests = @import("tests/binary_payload_tests.zig"); const schema_bounds_parsing_tests = @import("tests/schema_bounds_parsing_tests.zig"); +const generated_header_tests = @import("tests/generated_header_tests.zig"); const media_type = @import("media_type.zig"); const generator = @import("generator.zig"); const cli = @import("cli.zig"); @@ -26,6 +27,7 @@ comptime { _ = model_typing_tests; _ = binary_payload_tests; _ = schema_bounds_parsing_tests; + _ = generated_header_tests; _ = media_type; _ = generator; _ = cli; diff --git a/src/tests/generated_header_tests.zig b/src/tests/generated_header_tests.zig new file mode 100644 index 0000000..25dec77 --- /dev/null +++ b/src/tests/generated_header_tests.zig @@ -0,0 +1,15 @@ +const std = @import("std"); +const generated_header = @import("../generators/generated_header.zig"); + +test "render emits expected header" { + const allocator = std.testing.allocator; + const header = try generated_header.render(allocator, "0.6.0 (a1b2c3d)", "2026-07-12 21:49:56 UTC"); + defer allocator.free(header); + + try std.testing.expectEqualStrings( + \\// this code was generated by openapi2zig 0.6.0 (a1b2c3d) on 2026-07-12 21:49:56 UTC + \\// changes to this file may cause incorrect behavior and will be lost if the code is regenerated + \\ + \\ + , header); +} From 3cfed81ba3078be9d317b4f8ee470458e2f16739 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Mon, 13 Jul 2026 00:14:50 +0200 Subject: [PATCH 2/9] Wire generated header into CLI and library outputs --- README.md | 4 ++-- examples/example_usage.zig | 2 +- src/generator.zig | 11 ++++++++++- src/generators/generated_header.zig | 10 +++++----- src/lib.zig | 25 ++++++++++++------------- src/tests/generated_header_tests.zig | 25 +++++++++++++++++++++++++ 6 files changed, 55 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index cb6972c..75f79bf 100644 --- a/README.md +++ b/README.md @@ -342,7 +342,7 @@ pub fn main(init: std.process.Init) !void { .base_url = "https://api.example.com", }; - const generated_code = try openapi2zig.generateCode(allocator, unified_doc, args); + const generated_code = try openapi2zig.generateCode(allocator, io, unified_doc, args); defer allocator.free(generated_code); // Write generated code to file @@ -372,7 +372,7 @@ pub fn main(init: std.process.Init) !void { #### Code Generation -- `generateCode(allocator, unified_doc, args)` - Generate complete Zig code (models + API) +- `generateCode(allocator, io, unified_doc, args)` - Generate complete Zig code (models + API) - `generateModels(allocator, unified_doc)` - Generate only model structs - `generateApi(allocator, unified_doc, args)` - Generate only API client functions diff --git a/examples/example_usage.zig b/examples/example_usage.zig index dbcaa50..4a2efdd 100644 --- a/examples/example_usage.zig +++ b/examples/example_usage.zig @@ -32,7 +32,7 @@ pub fn main(init: std.process.Init) !void { .base_url = "https://petstore.swagger.io/v2", }; - const generated_code = try openapi2zig.generateCode(allocator, unified_doc, args); + const generated_code = try openapi2zig.generateCode(allocator, io, unified_doc, args); defer allocator.free(generated_code); std.debug.print("\nGenerated {} bytes of Zig code\n", .{generated_code.len}); diff --git a/src/generator.zig b/src/generator.zig index e70d66d..9c7eb90 100644 --- a/src/generator.zig +++ b/src/generator.zig @@ -4,6 +4,8 @@ const detector = @import("detector.zig"); const models = @import("models.zig"); const input_loader = @import("input_loader.zig"); const yaml_loader = @import("yaml_loader.zig"); +const version_info = @import("build_info"); +const generated_header = @import("generators/generated_header.zig"); const OpenApiConverter = @import("generators/converters/openapi_converter.zig").OpenApiConverter; const OpenApi31Converter = @import("generators/converters/openapi31_converter.zig").OpenApi31Converter; const OpenApi32Converter = @import("generators/converters/openapi32_converter.zig").OpenApi32Converter; @@ -123,6 +125,13 @@ fn generateCodeFromUnifiedDocument(allocator: std.mem.Allocator, io: std.Io, uni }; defer if (!args.models_only) allocator.free(generated_code); + const version = try std.fmt.allocPrint(allocator, "{s} ({s})", .{ version_info.VERSION, version_info.GIT_COMMIT }); + defer allocator.free(version); + const header = try generated_header.renderNow(allocator, io, version); + defer allocator.free(header); + const output_code = try std.mem.concat(allocator, u8, &.{ header, generated_code }); + defer allocator.free(output_code); + const output_path = args.output_path orelse default_output_file; const cwd = std.Io.Dir.cwd(); if (std.fs.path.dirname(output_path)) |dir_path| { @@ -130,7 +139,7 @@ fn generateCodeFromUnifiedDocument(allocator: std.mem.Allocator, io: std.Io, uni } const output_file = try cwd.createFile(io, output_path, .{}); defer output_file.close(io); - try output_file.writeStreamingAll(io, generated_code); + try output_file.writeStreamingAll(io, output_code); std.log.info("Code generated successfully and written to '{s}'.", .{output_path}); } diff --git a/src/generators/generated_header.zig b/src/generators/generated_header.zig index 2612f1d..492fcf1 100644 --- a/src/generators/generated_header.zig +++ b/src/generators/generated_header.zig @@ -9,15 +9,15 @@ pub fn render(allocator: std.mem.Allocator, version: []const u8, timestamp: []co , .{ version, timestamp }); } -pub fn renderNow(allocator: std.mem.Allocator, version: []const u8) ![]const u8 { - const timestamp = try formatUtcTimestamp(allocator); +pub fn renderNow(allocator: std.mem.Allocator, io: std.Io, version: []const u8) ![]const u8 { + const timestamp = try formatUtcTimestamp(allocator, io); defer allocator.free(timestamp); return try render(allocator, version, timestamp); } -fn formatUtcTimestamp(allocator: std.mem.Allocator) ![]const u8 { - const timestamp = std.time.timestamp(); - const epoch_seconds = std.time.epoch.EpochSeconds{ .secs = @as(u64, @intCast(timestamp)) }; +fn formatUtcTimestamp(allocator: std.mem.Allocator, io: std.Io) ![]const u8 { + const now = std.Io.Clock.real.now(io); + const epoch_seconds = std.time.epoch.EpochSeconds{ .secs = @as(u64, @intCast(now.toSeconds())) }; const epoch_day = epoch_seconds.getEpochDay(); const day_seconds = epoch_seconds.getDaySeconds(); const year_day = epoch_day.calculateYearDay(); diff --git a/src/lib.zig b/src/lib.zig index 2bb9d9f..62ede0c 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -30,6 +30,7 @@ const std = @import("std"); const yaml_loader = @import("yaml_loader.zig"); +const generated_header = @import("generators/generated_header.zig"); // Core version detection pub const ApiVersion = @import("detector.zig").OpenApiVersion; @@ -213,34 +214,32 @@ pub fn generateApi(allocator: std.mem.Allocator, unified_doc: UnifiedDocument, a /// /// Parameters: /// - allocator: Memory allocator to use for code generation +/// - io: Standard I/O context for reading the generation timestamp /// - unified_doc: The unified document containing schema and operation definitions /// - args: CLI arguments for customizing code generation /// /// Returns: /// - String containing complete generated Zig code -pub fn generateCode(allocator: std.mem.Allocator, unified_doc: UnifiedDocument, args: CliArgs) ![]const u8 { +pub fn generateCode(allocator: std.mem.Allocator, io: std.Io, unified_doc: UnifiedDocument, args: CliArgs) ![]const u8 { const models_code = try generateModels(allocator, unified_doc); errdefer allocator.free(models_code); + const version = try std.fmt.allocPrint(allocator, "{s} ({s})", .{ version_info.VERSION, version_info.GIT_COMMIT }); + defer allocator.free(version); + const header = try generated_header.renderNow(allocator, io, version); + defer allocator.free(header); + if (args.models_only) { - return models_code; + const output = try std.mem.concat(allocator, u8, &.{ header, models_code }); + allocator.free(models_code); + return output; } defer allocator.free(models_code); const api_code = try generateApi(allocator, unified_doc, args); defer allocator.free(api_code); - const header = - \\/////////////////////////////////////////// - \\// Generated Zig code from OpenAPI - \\/////////////////////////////////////////// - \\ - \\const std = @import("std"); - \\ - \\ - ; - - return try std.fmt.allocPrint(allocator, "{s}{s}\n{s}", .{ header, models_code, api_code }); + return try std.mem.concat(allocator, u8, &.{ header, models_code, "\n", api_code }); } fn convertDocument(allocator: std.mem.Allocator, doc: anytype, comptime Converter: type) !UnifiedDocument { diff --git a/src/tests/generated_header_tests.zig b/src/tests/generated_header_tests.zig index 25dec77..076e2ec 100644 --- a/src/tests/generated_header_tests.zig +++ b/src/tests/generated_header_tests.zig @@ -1,5 +1,6 @@ const std = @import("std"); const generated_header = @import("../generators/generated_header.zig"); +const openapi2zig = @import("../lib.zig"); test "render emits expected header" { const allocator = std.testing.allocator; @@ -13,3 +14,27 @@ test "render emits expected header" { \\ , header); } + +test "generateCode output includes generated header" { + const allocator = std.testing.allocator; + const json = + \\{ + \\ "openapi": "3.0.0", + \\ "info": { "title": "fixture", "version": "1.0.0" }, + \\ "paths": {} + \\} + ; + + var unified = try openapi2zig.parseToUnified(allocator, json); + defer unified.deinit(allocator); + + const code = try openapi2zig.generateCode(allocator, std.testing.io, unified, .{ + .input_path = "fixture.json", + .models_only = true, + }); + defer allocator.free(code); + + try std.testing.expect(std.mem.startsWith(u8, code, "// this code was generated by openapi2zig")); + try std.testing.expect(std.mem.indexOf(u8, code, "changes to this file may cause incorrect behavior") != null); + try std.testing.expect(std.mem.indexOf(u8, code, "will be lost if the code is regenerated") != null); +} From 5c6dc2517ac6820e200426043bf4aaaa9ebca596 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Mon, 13 Jul 2026 00:15:23 +0200 Subject: [PATCH 3/9] Regenerate sample outputs with generated header --- generated/generated_v2.zig | 3 +++ generated/generated_v2_yaml.zig | 3 +++ generated/generated_v3.zig | 3 +++ generated/generated_v31.zig | 3 +++ generated/generated_v31_yaml.zig | 3 +++ generated/generated_v32.zig | 3 +++ generated/generated_v3_yaml.zig | 3 +++ generated/lmstudio.zig | 3 +++ 8 files changed, 24 insertions(+) diff --git a/generated/generated_v2.zig b/generated/generated_v2.zig index 6f0a219..7f161b7 100644 --- a/generated/generated_v2.zig +++ b/generated/generated_v2.zig @@ -1,3 +1,6 @@ +// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// changes to this file may cause incorrect behavior and will be lost if the code is regenerated + const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v2_yaml.zig b/generated/generated_v2_yaml.zig index eb967e0..ad064c7 100644 --- a/generated/generated_v2_yaml.zig +++ b/generated/generated_v2_yaml.zig @@ -1,3 +1,6 @@ +// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// changes to this file may cause incorrect behavior and will be lost if the code is regenerated + const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v3.zig b/generated/generated_v3.zig index 5e3ddf1..9e2cc8f 100644 --- a/generated/generated_v3.zig +++ b/generated/generated_v3.zig @@ -1,3 +1,6 @@ +// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// changes to this file may cause incorrect behavior and will be lost if the code is regenerated + const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v31.zig b/generated/generated_v31.zig index fd1bf69..1d6dae2 100644 --- a/generated/generated_v31.zig +++ b/generated/generated_v31.zig @@ -1,3 +1,6 @@ +// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// changes to this file may cause incorrect behavior and will be lost if the code is regenerated + const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v31_yaml.zig b/generated/generated_v31_yaml.zig index fd1bf69..1d6dae2 100644 --- a/generated/generated_v31_yaml.zig +++ b/generated/generated_v31_yaml.zig @@ -1,3 +1,6 @@ +// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// changes to this file may cause incorrect behavior and will be lost if the code is regenerated + const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v32.zig b/generated/generated_v32.zig index 518a3cd..35a8cde 100644 --- a/generated/generated_v32.zig +++ b/generated/generated_v32.zig @@ -1,3 +1,6 @@ +// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// changes to this file may cause incorrect behavior and will be lost if the code is regenerated + const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v3_yaml.zig b/generated/generated_v3_yaml.zig index 5e3ddf1..9e2cc8f 100644 --- a/generated/generated_v3_yaml.zig +++ b/generated/generated_v3_yaml.zig @@ -1,3 +1,6 @@ +// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// changes to this file may cause incorrect behavior and will be lost if the code is regenerated + const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/lmstudio.zig b/generated/lmstudio.zig index b90b3aa..789dbb1 100644 --- a/generated/lmstudio.zig +++ b/generated/lmstudio.zig @@ -1,3 +1,6 @@ +// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// changes to this file may cause incorrect behavior and will be lost if the code is regenerated + const std = @import("std"); /////////////////////////////////////////// From 0a36359115295a886cad706148663336079a63e6 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Mon, 13 Jul 2026 10:43:29 +0200 Subject: [PATCH 4/9] Add lines to header --- generated/generated_v2.zig | 5 +++-- generated/generated_v2_yaml.zig | 5 +++-- generated/generated_v3.zig | 5 +++-- generated/generated_v31.zig | 5 +++-- generated/generated_v31_yaml.zig | 5 +++-- generated/generated_v32.zig | 5 +++-- generated/generated_v3_yaml.zig | 5 +++-- generated/lmstudio.zig | 5 +++-- src/generators/generated_header.zig | 3 ++- src/tests/generated_header_tests.zig | 5 +++-- 10 files changed, 29 insertions(+), 19 deletions(-) diff --git a/generated/generated_v2.zig b/generated/generated_v2.zig index 7f161b7..926cadb 100644 --- a/generated/generated_v2.zig +++ b/generated/generated_v2.zig @@ -1,6 +1,7 @@ -// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// +// this code was generated by openapi2zig 0.3.0 (cf00759) on 2026-07-13 08:44:15 UTC // changes to this file may cause incorrect behavior and will be lost if the code is regenerated - +// const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v2_yaml.zig b/generated/generated_v2_yaml.zig index ad064c7..da92dca 100644 --- a/generated/generated_v2_yaml.zig +++ b/generated/generated_v2_yaml.zig @@ -1,6 +1,7 @@ -// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// +// this code was generated by openapi2zig 0.3.0 (cf00759) on 2026-07-13 08:44:16 UTC // changes to this file may cause incorrect behavior and will be lost if the code is regenerated - +// const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v3.zig b/generated/generated_v3.zig index 9e2cc8f..f9b962d 100644 --- a/generated/generated_v3.zig +++ b/generated/generated_v3.zig @@ -1,6 +1,7 @@ -// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// +// this code was generated by openapi2zig 0.3.0 (cf00759) on 2026-07-13 08:44:16 UTC // changes to this file may cause incorrect behavior and will be lost if the code is regenerated - +// const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v31.zig b/generated/generated_v31.zig index 1d6dae2..0e15e4d 100644 --- a/generated/generated_v31.zig +++ b/generated/generated_v31.zig @@ -1,6 +1,7 @@ -// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// +// this code was generated by openapi2zig 0.3.0 (cf00759) on 2026-07-13 08:44:15 UTC // changes to this file may cause incorrect behavior and will be lost if the code is regenerated - +// const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v31_yaml.zig b/generated/generated_v31_yaml.zig index 1d6dae2..8f92f93 100644 --- a/generated/generated_v31_yaml.zig +++ b/generated/generated_v31_yaml.zig @@ -1,6 +1,7 @@ -// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// +// this code was generated by openapi2zig 0.3.0 (cf00759) on 2026-07-13 08:44:16 UTC // changes to this file may cause incorrect behavior and will be lost if the code is regenerated - +// const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v32.zig b/generated/generated_v32.zig index 35a8cde..11b0a2d 100644 --- a/generated/generated_v32.zig +++ b/generated/generated_v32.zig @@ -1,6 +1,7 @@ -// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// +// this code was generated by openapi2zig 0.3.0 (cf00759) on 2026-07-13 08:44:16 UTC // changes to this file may cause incorrect behavior and will be lost if the code is regenerated - +// const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/generated_v3_yaml.zig b/generated/generated_v3_yaml.zig index 9e2cc8f..e211537 100644 --- a/generated/generated_v3_yaml.zig +++ b/generated/generated_v3_yaml.zig @@ -1,6 +1,7 @@ -// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// +// this code was generated by openapi2zig 0.3.0 (cf00759) on 2026-07-13 08:44:15 UTC // changes to this file may cause incorrect behavior and will be lost if the code is regenerated - +// const std = @import("std"); /////////////////////////////////////////// diff --git a/generated/lmstudio.zig b/generated/lmstudio.zig index 789dbb1..8013fdc 100644 --- a/generated/lmstudio.zig +++ b/generated/lmstudio.zig @@ -1,6 +1,7 @@ -// this code was generated by openapi2zig 0.3.0 (3cfed81) on 2026-07-12 22:15:11 UTC +// +// this code was generated by openapi2zig 0.3.0 (cf00759) on 2026-07-13 08:44:15 UTC // changes to this file may cause incorrect behavior and will be lost if the code is regenerated - +// const std = @import("std"); /////////////////////////////////////////// diff --git a/src/generators/generated_header.zig b/src/generators/generated_header.zig index 492fcf1..0261871 100644 --- a/src/generators/generated_header.zig +++ b/src/generators/generated_header.zig @@ -2,9 +2,10 @@ const std = @import("std"); pub fn render(allocator: std.mem.Allocator, version: []const u8, timestamp: []const u8) ![]const u8 { return try std.fmt.allocPrint(allocator, + \\// \\// this code was generated by openapi2zig {s} on {s} \\// changes to this file may cause incorrect behavior and will be lost if the code is regenerated - \\ + \\// \\ , .{ version, timestamp }); } diff --git a/src/tests/generated_header_tests.zig b/src/tests/generated_header_tests.zig index 076e2ec..f6d167c 100644 --- a/src/tests/generated_header_tests.zig +++ b/src/tests/generated_header_tests.zig @@ -8,9 +8,10 @@ test "render emits expected header" { defer allocator.free(header); try std.testing.expectEqualStrings( + \\// \\// this code was generated by openapi2zig 0.6.0 (a1b2c3d) on 2026-07-12 21:49:56 UTC \\// changes to this file may cause incorrect behavior and will be lost if the code is regenerated - \\ + \\// \\ , header); } @@ -34,7 +35,7 @@ test "generateCode output includes generated header" { }); defer allocator.free(code); - try std.testing.expect(std.mem.startsWith(u8, code, "// this code was generated by openapi2zig")); + try std.testing.expect(std.mem.indexOf(u8, code, "this code was generated by openapi2zig") != null); try std.testing.expect(std.mem.indexOf(u8, code, "changes to this file may cause incorrect behavior") != null); try std.testing.expect(std.mem.indexOf(u8, code, "will be lost if the code is regenerated") != null); } From 996389b23f67ff26df475bcec41cb58701b87bb6 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Tue, 14 Jul 2026 00:12:02 +0200 Subject: [PATCH 5/9] Add renderNowFromBuildInfo helper to generated_header --- src/generators/generated_header.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/generators/generated_header.zig b/src/generators/generated_header.zig index 0261871..185c31f 100644 --- a/src/generators/generated_header.zig +++ b/src/generators/generated_header.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const version_info = @import("build_info"); pub fn render(allocator: std.mem.Allocator, version: []const u8, timestamp: []const u8) ![]const u8 { return try std.fmt.allocPrint(allocator, @@ -16,6 +17,12 @@ pub fn renderNow(allocator: std.mem.Allocator, io: std.Io, version: []const u8) return try render(allocator, version, timestamp); } +pub fn renderNowFromBuildInfo(allocator: std.mem.Allocator, io: std.Io) ![]const u8 { + const version = try std.fmt.allocPrint(allocator, "{s} ({s})", .{ version_info.VERSION, version_info.GIT_COMMIT }); + defer allocator.free(version); + return try renderNow(allocator, io, version); +} + fn formatUtcTimestamp(allocator: std.mem.Allocator, io: std.Io) ![]const u8 { const now = std.Io.Clock.real.now(io); const epoch_seconds = std.time.epoch.EpochSeconds{ .secs = @as(u64, @intCast(now.toSeconds())) }; From c5a375332bc7b650eec0c7a18525bd0a6e30ade8 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Tue, 14 Jul 2026 00:12:06 +0200 Subject: [PATCH 6/9] Use renderNowFromBuildInfo in CLI generator --- src/generator.zig | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/generator.zig b/src/generator.zig index 9c7eb90..a4bc3d6 100644 --- a/src/generator.zig +++ b/src/generator.zig @@ -4,7 +4,6 @@ const detector = @import("detector.zig"); const models = @import("models.zig"); const input_loader = @import("input_loader.zig"); const yaml_loader = @import("yaml_loader.zig"); -const version_info = @import("build_info"); const generated_header = @import("generators/generated_header.zig"); const OpenApiConverter = @import("generators/converters/openapi_converter.zig").OpenApiConverter; const OpenApi31Converter = @import("generators/converters/openapi31_converter.zig").OpenApi31Converter; @@ -125,9 +124,7 @@ fn generateCodeFromUnifiedDocument(allocator: std.mem.Allocator, io: std.Io, uni }; defer if (!args.models_only) allocator.free(generated_code); - const version = try std.fmt.allocPrint(allocator, "{s} ({s})", .{ version_info.VERSION, version_info.GIT_COMMIT }); - defer allocator.free(version); - const header = try generated_header.renderNow(allocator, io, version); + const header = try generated_header.renderNowFromBuildInfo(allocator, io); defer allocator.free(header); const output_code = try std.mem.concat(allocator, u8, &.{ header, generated_code }); defer allocator.free(output_code); From 0b0a87e5c79a4324db178dab5267a21a343f47a3 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Tue, 14 Jul 2026 00:12:11 +0200 Subject: [PATCH 7/9] Use renderNowFromBuildInfo and simplify models_code cleanup in lib.zig --- src/lib.zig | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/lib.zig b/src/lib.zig index 62ede0c..08cda83 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -222,19 +222,14 @@ pub fn generateApi(allocator: std.mem.Allocator, unified_doc: UnifiedDocument, a /// - String containing complete generated Zig code pub fn generateCode(allocator: std.mem.Allocator, io: std.Io, unified_doc: UnifiedDocument, args: CliArgs) ![]const u8 { const models_code = try generateModels(allocator, unified_doc); - errdefer allocator.free(models_code); + defer allocator.free(models_code); - const version = try std.fmt.allocPrint(allocator, "{s} ({s})", .{ version_info.VERSION, version_info.GIT_COMMIT }); - defer allocator.free(version); - const header = try generated_header.renderNow(allocator, io, version); + const header = try generated_header.renderNowFromBuildInfo(allocator, io); defer allocator.free(header); if (args.models_only) { - const output = try std.mem.concat(allocator, u8, &.{ header, models_code }); - allocator.free(models_code); - return output; + return try std.mem.concat(allocator, u8, &.{ header, models_code }); } - defer allocator.free(models_code); const api_code = try generateApi(allocator, unified_doc, args); defer allocator.free(api_code); From 40e9f47da846b717d7073580516bdfdb46442adb Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Tue, 14 Jul 2026 00:12:16 +0200 Subject: [PATCH 8/9] Use test_utils allocator in generated header tests --- src/tests/generated_header_tests.zig | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tests/generated_header_tests.zig b/src/tests/generated_header_tests.zig index f6d167c..f7075df 100644 --- a/src/tests/generated_header_tests.zig +++ b/src/tests/generated_header_tests.zig @@ -1,9 +1,13 @@ const std = @import("std"); const generated_header = @import("../generators/generated_header.zig"); const openapi2zig = @import("../lib.zig"); +const test_utils = @import("test_utils.zig"); test "render emits expected header" { - const allocator = std.testing.allocator; + var gpa = test_utils.createTestAllocator(); + const allocator = gpa.allocator(); + defer std.debug.assert(gpa.deinit() == .ok); + const header = try generated_header.render(allocator, "0.6.0 (a1b2c3d)", "2026-07-12 21:49:56 UTC"); defer allocator.free(header); @@ -17,7 +21,10 @@ test "render emits expected header" { } test "generateCode output includes generated header" { - const allocator = std.testing.allocator; + var gpa = test_utils.createTestAllocator(); + const allocator = gpa.allocator(); + defer std.debug.assert(gpa.deinit() == .ok); + const json = \\{ \\ "openapi": "3.0.0", From dd2385028e01b93c09b1ce5cb2344edc8e9a03c1 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Tue, 14 Jul 2026 00:12:20 +0200 Subject: [PATCH 9/9] Document auto-generated header in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 75f79bf..0f62973 100644 --- a/README.md +++ b/README.md @@ -372,7 +372,7 @@ pub fn main(init: std.process.Init) !void { #### Code Generation -- `generateCode(allocator, io, unified_doc, args)` - Generate complete Zig code (models + API) +- `generateCode(allocator, io, unified_doc, args)` - Generate complete Zig code (models + API). The output begins with a versioned `` header (generator version, timestamp, and a regeneration warning); any manual edits will be overwritten when the code is regenerated. - `generateModels(allocator, unified_doc)` - Generate only model structs - `generateApi(allocator, unified_doc, args)` - Generate only API client functions