diff --git a/build.zig b/build.zig index fe8f07c..00a9992 100644 --- a/build.zig +++ b/build.zig @@ -73,6 +73,20 @@ pub fn build(b: *std.Build) void { const run_generate_v3_step = b.step("run-generate-v3", "Run the app with generate command"); run_generate_v3_step.dependOn(&run_generate_v3_cmd.step); + const run_generate_v3_multi_cmd = b.addRunArtifact(exe); + run_generate_v3_multi_cmd.addArgs(&.{ + "generate", + "-i", + "openapi/v3.0/petstore.json", + "-o", + "generated/multi", + "--multiple-files", + "--base-url", + "https://petstore3.swagger.io/api/v3", + }); + const run_generate_v3_multi_step = b.step("run-generate-v3-multi", "Generate multiple output files (models, runtime, client)"); + run_generate_v3_multi_step.dependOn(&run_generate_v3_multi_cmd.step); + const run_generate_v2_cmd = b.addRunArtifact(exe); run_generate_v2_cmd.addArgs(&.{ "generate", @@ -188,6 +202,7 @@ pub fn build(b: *std.Build) void { const run_generate = b.step("run-generate", "Run the app with generate commands"); run_generate.dependOn(&run_generate_v3_cmd.step); + run_generate.dependOn(&run_generate_v3_multi_cmd.step); run_generate.dependOn(&run_generate_v3_yaml_cmd.step); run_generate.dependOn(&run_generate_v2_cmd.step); run_generate.dependOn(&run_generate_v2_yaml_cmd.step); @@ -233,6 +248,16 @@ pub fn build(b: *std.Build) void { const run_generated_tests = b.addRunArtifact(generated_tests); test_step.dependOn(&run_generated_tests.step); + const multi_generated_tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path("generated/compile_multi_generated.zig"), + .target = target, + .optimize = optimize, + }), + }); + const run_multi_generated_tests = b.addRunArtifact(multi_generated_tests); + test_step.dependOn(&run_multi_generated_tests.step); + const test_package_cmd = b.addSystemCommand(&.{ b.graph.zig_exe, "build" }); test_package_cmd.step.dependOn(package_snapshot_step); test_package_cmd.setCwd(b.path(".zig-cache/package-snapshot/examples/package_consumer")); diff --git a/generated/anthropic.zig b/generated/anthropic.zig index c1d4dc5..ab5239c 100644 --- a/generated/anthropic.zig +++ b/generated/anthropic.zig @@ -1,6 +1,6 @@ // -// this code was generated by openapi2zig 0.4.0 (2446685) on 2026-07-22 13:15:10 UTC -// changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:20 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated // const std = @import("std"); @@ -11646,6 +11646,118 @@ pub fn getJsonResult(comptime T: type, client: *Client, path: []const u8) !ApiRe pub fn postJsonResult(comptime T: type, client: *Client, path: []const u8, payload: anytype) !ApiResult(T) { return parseRawResponse(T, try postJsonRaw(client, path, payload)); } +fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); + + var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + + if (parsed.value == .object) { + try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); + } + + var out: std.Io.Writer.Allocating = .init(allocator); + errdefer out.deinit(); + try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); + return try out.toOwnedSlice(); +} + +fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; + try streamJson(client, path, requestBody, &typed_callback, cancellation_token); +} + +fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const allocator = client.allocator; + const payload = try stringifyStreamRequest(allocator, requestBody); + defer allocator.free(payload); + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); + defer if (auth_header) |value| allocator.free(value); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + try checkCancellation(cancellation_token); + + const start = std.Io.Clock.awake.now(client.io); + var req = client.http.request(.POST, uri, .{ + .redirect_behavior = .unhandled, + .headers = .{ .accept_encoding = .{ .override = "identity" } }, + .extra_headers = headers.items, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + defer req.deinit(); + + req.transfer_encoding = .{ .content_length = payload.len }; + var request_body = try req.sendBodyUnflushed(&.{}); + try request_body.writer.writeAll(payload); + try request_body.end(); + try req.connection.?.flush(); + try checkCancellation(cancellation_token); + + var response = req.receiveHead(&.{}) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + if (response.head.status.class() != .success) { + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + return error.ResponseError; + } + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + + var transfer_buffer: [8 * 1024]u8 = undefined; + const reader = response.reader(&transfer_buffer); + parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { + error.ReadFailed => return response.bodyErr() orelse err, + else => return err, + }; +} + +fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { + if (content_type) |ct| { + try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); + } + try headers.append(allocator, .{ .name = "Accept", .value = accept }); + + var auth_header: ?[]u8 = null; + if (client.api_key.len > 0) { + auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); + try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); + } + if (client.organization) |organization| { + try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); + } + if (client.project) |project| { + try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); + } + for (client.default_headers) |header| { + try headers.append(allocator, header); + } + return auth_header; +} pub const CancellationToken = struct { cancelled: std.atomic.Value(bool), @@ -11762,120 +11874,6 @@ pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reade var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; try parseSseReader(allocator, reader, &typed_callback, cancellation_token); } - -fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { - var buf: std.Io.Writer.Allocating = .init(allocator); - defer buf.deinit(); - try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); - - var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); - defer parsed.deinit(); - - if (parsed.value == .object) { - try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); - } - - var out: std.Io.Writer.Allocating = .init(allocator); - errdefer out.deinit(); - try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); - return try out.toOwnedSlice(); -} - -fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const Callback = @TypeOf(callback.*); - var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; - try streamJson(client, path, requestBody, &typed_callback, cancellation_token); -} - -fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const allocator = client.allocator; - const payload = try stringifyStreamRequest(allocator, requestBody); - defer allocator.free(payload); - - var headers = std.ArrayList(std.http.Header).empty; - defer headers.deinit(allocator); - const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); - defer if (auth_header) |value| allocator.free(value); - - const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); - defer allocator.free(url); - - if (client.http_observer) |obs| { - if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); - } - - const uri = try std.Uri.parse(url); - try checkCancellation(cancellation_token); - - const start = std.Io.Clock.awake.now(client.io); - var req = client.http.request(.POST, uri, .{ - .redirect_behavior = .unhandled, - .headers = .{ .accept_encoding = .{ .override = "identity" } }, - .extra_headers = headers.items, - }) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - defer req.deinit(); - - req.transfer_encoding = .{ .content_length = payload.len }; - var request_body = try req.sendBodyUnflushed(&.{}); - try request_body.writer.writeAll(payload); - try request_body.end(); - try req.connection.?.flush(); - try checkCancellation(cancellation_token); - - var response = req.receiveHead(&.{}) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); - if (response.head.status.class() != .success) { - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - return error.ResponseError; - } - - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - - var transfer_buffer: [8 * 1024]u8 = undefined; - const reader = response.reader(&transfer_buffer); - parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { - error.ReadFailed => return response.bodyErr() orelse err, - else => return err, - }; -} - -fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { - if (content_type) |ct| { - try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); - } - try headers.append(allocator, .{ .name = "Accept", .value = accept }); - - var auth_header: ?[]u8 = null; - if (client.api_key.len > 0) { - auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); - try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); - } - if (client.organization) |organization| { - try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); - } - if (client.project) |project| { - try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); - } - for (client.default_headers) |header| { - try headers.append(allocator, header); - } - return auth_header; -} - const max_sse_line_size = 8192; const max_sse_event_size = 65536; diff --git a/generated/compile_multi_generated.zig b/generated/compile_multi_generated.zig new file mode 100644 index 0000000..e00d640 --- /dev/null +++ b/generated/compile_multi_generated.zig @@ -0,0 +1,7 @@ +const std = @import("std"); + +const multi_v3 = @import("multi/client.zig"); + +test "multi-file generated clients compile" { + std.testing.refAllDecls(multi_v3); +} diff --git a/generated/generated_v2.zig b/generated/generated_v2.zig index 935d393..aaf35ca 100644 --- a/generated/generated_v2.zig +++ b/generated/generated_v2.zig @@ -1,6 +1,6 @@ // -// this code was generated by openapi2zig 0.4.0 (2446685) on 2026-07-22 13:15:10 UTC -// changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:20 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated // const std = @import("std"); @@ -264,6 +264,118 @@ pub fn getJsonResult(comptime T: type, client: *Client, path: []const u8) !ApiRe pub fn postJsonResult(comptime T: type, client: *Client, path: []const u8, payload: anytype) !ApiResult(T) { return parseRawResponse(T, try postJsonRaw(client, path, payload)); } +fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); + + var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + + if (parsed.value == .object) { + try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); + } + + var out: std.Io.Writer.Allocating = .init(allocator); + errdefer out.deinit(); + try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); + return try out.toOwnedSlice(); +} + +fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; + try streamJson(client, path, requestBody, &typed_callback, cancellation_token); +} + +fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const allocator = client.allocator; + const payload = try stringifyStreamRequest(allocator, requestBody); + defer allocator.free(payload); + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); + defer if (auth_header) |value| allocator.free(value); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + try checkCancellation(cancellation_token); + + const start = std.Io.Clock.awake.now(client.io); + var req = client.http.request(.POST, uri, .{ + .redirect_behavior = .unhandled, + .headers = .{ .accept_encoding = .{ .override = "identity" } }, + .extra_headers = headers.items, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + defer req.deinit(); + + req.transfer_encoding = .{ .content_length = payload.len }; + var request_body = try req.sendBodyUnflushed(&.{}); + try request_body.writer.writeAll(payload); + try request_body.end(); + try req.connection.?.flush(); + try checkCancellation(cancellation_token); + + var response = req.receiveHead(&.{}) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + if (response.head.status.class() != .success) { + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + return error.ResponseError; + } + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + + var transfer_buffer: [8 * 1024]u8 = undefined; + const reader = response.reader(&transfer_buffer); + parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { + error.ReadFailed => return response.bodyErr() orelse err, + else => return err, + }; +} + +fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { + if (content_type) |ct| { + try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); + } + try headers.append(allocator, .{ .name = "Accept", .value = accept }); + + var auth_header: ?[]u8 = null; + if (client.api_key.len > 0) { + auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); + try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); + } + if (client.organization) |organization| { + try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); + } + if (client.project) |project| { + try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); + } + for (client.default_headers) |header| { + try headers.append(allocator, header); + } + return auth_header; +} pub const CancellationToken = struct { cancelled: std.atomic.Value(bool), @@ -380,120 +492,6 @@ pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reade var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; try parseSseReader(allocator, reader, &typed_callback, cancellation_token); } - -fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { - var buf: std.Io.Writer.Allocating = .init(allocator); - defer buf.deinit(); - try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); - - var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); - defer parsed.deinit(); - - if (parsed.value == .object) { - try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); - } - - var out: std.Io.Writer.Allocating = .init(allocator); - errdefer out.deinit(); - try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); - return try out.toOwnedSlice(); -} - -fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const Callback = @TypeOf(callback.*); - var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; - try streamJson(client, path, requestBody, &typed_callback, cancellation_token); -} - -fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const allocator = client.allocator; - const payload = try stringifyStreamRequest(allocator, requestBody); - defer allocator.free(payload); - - var headers = std.ArrayList(std.http.Header).empty; - defer headers.deinit(allocator); - const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); - defer if (auth_header) |value| allocator.free(value); - - const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); - defer allocator.free(url); - - if (client.http_observer) |obs| { - if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); - } - - const uri = try std.Uri.parse(url); - try checkCancellation(cancellation_token); - - const start = std.Io.Clock.awake.now(client.io); - var req = client.http.request(.POST, uri, .{ - .redirect_behavior = .unhandled, - .headers = .{ .accept_encoding = .{ .override = "identity" } }, - .extra_headers = headers.items, - }) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - defer req.deinit(); - - req.transfer_encoding = .{ .content_length = payload.len }; - var request_body = try req.sendBodyUnflushed(&.{}); - try request_body.writer.writeAll(payload); - try request_body.end(); - try req.connection.?.flush(); - try checkCancellation(cancellation_token); - - var response = req.receiveHead(&.{}) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); - if (response.head.status.class() != .success) { - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - return error.ResponseError; - } - - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - - var transfer_buffer: [8 * 1024]u8 = undefined; - const reader = response.reader(&transfer_buffer); - parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { - error.ReadFailed => return response.bodyErr() orelse err, - else => return err, - }; -} - -fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { - if (content_type) |ct| { - try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); - } - try headers.append(allocator, .{ .name = "Accept", .value = accept }); - - var auth_header: ?[]u8 = null; - if (client.api_key.len > 0) { - auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); - try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); - } - if (client.organization) |organization| { - try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); - } - if (client.project) |project| { - try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); - } - for (client.default_headers) |header| { - try headers.append(allocator, header); - } - return auth_header; -} - const max_sse_line_size = 8192; const max_sse_event_size = 65536; diff --git a/generated/generated_v2_yaml.zig b/generated/generated_v2_yaml.zig index a356e30..72e1579 100644 --- a/generated/generated_v2_yaml.zig +++ b/generated/generated_v2_yaml.zig @@ -1,6 +1,6 @@ // -// this code was generated by openapi2zig 0.4.0 (2446685) on 2026-07-22 13:15:11 UTC -// changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:19 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated // const std = @import("std"); @@ -264,6 +264,118 @@ pub fn getJsonResult(comptime T: type, client: *Client, path: []const u8) !ApiRe pub fn postJsonResult(comptime T: type, client: *Client, path: []const u8, payload: anytype) !ApiResult(T) { return parseRawResponse(T, try postJsonRaw(client, path, payload)); } +fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); + + var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + + if (parsed.value == .object) { + try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); + } + + var out: std.Io.Writer.Allocating = .init(allocator); + errdefer out.deinit(); + try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); + return try out.toOwnedSlice(); +} + +fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; + try streamJson(client, path, requestBody, &typed_callback, cancellation_token); +} + +fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const allocator = client.allocator; + const payload = try stringifyStreamRequest(allocator, requestBody); + defer allocator.free(payload); + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); + defer if (auth_header) |value| allocator.free(value); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + try checkCancellation(cancellation_token); + + const start = std.Io.Clock.awake.now(client.io); + var req = client.http.request(.POST, uri, .{ + .redirect_behavior = .unhandled, + .headers = .{ .accept_encoding = .{ .override = "identity" } }, + .extra_headers = headers.items, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + defer req.deinit(); + + req.transfer_encoding = .{ .content_length = payload.len }; + var request_body = try req.sendBodyUnflushed(&.{}); + try request_body.writer.writeAll(payload); + try request_body.end(); + try req.connection.?.flush(); + try checkCancellation(cancellation_token); + + var response = req.receiveHead(&.{}) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + if (response.head.status.class() != .success) { + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + return error.ResponseError; + } + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + + var transfer_buffer: [8 * 1024]u8 = undefined; + const reader = response.reader(&transfer_buffer); + parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { + error.ReadFailed => return response.bodyErr() orelse err, + else => return err, + }; +} + +fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { + if (content_type) |ct| { + try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); + } + try headers.append(allocator, .{ .name = "Accept", .value = accept }); + + var auth_header: ?[]u8 = null; + if (client.api_key.len > 0) { + auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); + try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); + } + if (client.organization) |organization| { + try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); + } + if (client.project) |project| { + try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); + } + for (client.default_headers) |header| { + try headers.append(allocator, header); + } + return auth_header; +} pub const CancellationToken = struct { cancelled: std.atomic.Value(bool), @@ -380,120 +492,6 @@ pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reade var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; try parseSseReader(allocator, reader, &typed_callback, cancellation_token); } - -fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { - var buf: std.Io.Writer.Allocating = .init(allocator); - defer buf.deinit(); - try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); - - var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); - defer parsed.deinit(); - - if (parsed.value == .object) { - try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); - } - - var out: std.Io.Writer.Allocating = .init(allocator); - errdefer out.deinit(); - try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); - return try out.toOwnedSlice(); -} - -fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const Callback = @TypeOf(callback.*); - var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; - try streamJson(client, path, requestBody, &typed_callback, cancellation_token); -} - -fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const allocator = client.allocator; - const payload = try stringifyStreamRequest(allocator, requestBody); - defer allocator.free(payload); - - var headers = std.ArrayList(std.http.Header).empty; - defer headers.deinit(allocator); - const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); - defer if (auth_header) |value| allocator.free(value); - - const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); - defer allocator.free(url); - - if (client.http_observer) |obs| { - if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); - } - - const uri = try std.Uri.parse(url); - try checkCancellation(cancellation_token); - - const start = std.Io.Clock.awake.now(client.io); - var req = client.http.request(.POST, uri, .{ - .redirect_behavior = .unhandled, - .headers = .{ .accept_encoding = .{ .override = "identity" } }, - .extra_headers = headers.items, - }) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - defer req.deinit(); - - req.transfer_encoding = .{ .content_length = payload.len }; - var request_body = try req.sendBodyUnflushed(&.{}); - try request_body.writer.writeAll(payload); - try request_body.end(); - try req.connection.?.flush(); - try checkCancellation(cancellation_token); - - var response = req.receiveHead(&.{}) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); - if (response.head.status.class() != .success) { - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - return error.ResponseError; - } - - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - - var transfer_buffer: [8 * 1024]u8 = undefined; - const reader = response.reader(&transfer_buffer); - parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { - error.ReadFailed => return response.bodyErr() orelse err, - else => return err, - }; -} - -fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { - if (content_type) |ct| { - try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); - } - try headers.append(allocator, .{ .name = "Accept", .value = accept }); - - var auth_header: ?[]u8 = null; - if (client.api_key.len > 0) { - auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); - try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); - } - if (client.organization) |organization| { - try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); - } - if (client.project) |project| { - try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); - } - for (client.default_headers) |header| { - try headers.append(allocator, header); - } - return auth_header; -} - const max_sse_line_size = 8192; const max_sse_event_size = 65536; diff --git a/generated/generated_v3.zig b/generated/generated_v3.zig index 68417e6..9648ca7 100644 --- a/generated/generated_v3.zig +++ b/generated/generated_v3.zig @@ -1,6 +1,6 @@ // -// this code was generated by openapi2zig 0.4.0 (2446685) on 2026-07-22 13:15:11 UTC -// changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:21 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated // const std = @import("std"); @@ -277,6 +277,118 @@ pub fn getJsonResult(comptime T: type, client: *Client, path: []const u8) !ApiRe pub fn postJsonResult(comptime T: type, client: *Client, path: []const u8, payload: anytype) !ApiResult(T) { return parseRawResponse(T, try postJsonRaw(client, path, payload)); } +fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); + + var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + + if (parsed.value == .object) { + try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); + } + + var out: std.Io.Writer.Allocating = .init(allocator); + errdefer out.deinit(); + try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); + return try out.toOwnedSlice(); +} + +fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; + try streamJson(client, path, requestBody, &typed_callback, cancellation_token); +} + +fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const allocator = client.allocator; + const payload = try stringifyStreamRequest(allocator, requestBody); + defer allocator.free(payload); + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); + defer if (auth_header) |value| allocator.free(value); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + try checkCancellation(cancellation_token); + + const start = std.Io.Clock.awake.now(client.io); + var req = client.http.request(.POST, uri, .{ + .redirect_behavior = .unhandled, + .headers = .{ .accept_encoding = .{ .override = "identity" } }, + .extra_headers = headers.items, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + defer req.deinit(); + + req.transfer_encoding = .{ .content_length = payload.len }; + var request_body = try req.sendBodyUnflushed(&.{}); + try request_body.writer.writeAll(payload); + try request_body.end(); + try req.connection.?.flush(); + try checkCancellation(cancellation_token); + + var response = req.receiveHead(&.{}) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + if (response.head.status.class() != .success) { + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + return error.ResponseError; + } + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + + var transfer_buffer: [8 * 1024]u8 = undefined; + const reader = response.reader(&transfer_buffer); + parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { + error.ReadFailed => return response.bodyErr() orelse err, + else => return err, + }; +} + +fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { + if (content_type) |ct| { + try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); + } + try headers.append(allocator, .{ .name = "Accept", .value = accept }); + + var auth_header: ?[]u8 = null; + if (client.api_key.len > 0) { + auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); + try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); + } + if (client.organization) |organization| { + try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); + } + if (client.project) |project| { + try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); + } + for (client.default_headers) |header| { + try headers.append(allocator, header); + } + return auth_header; +} pub const CancellationToken = struct { cancelled: std.atomic.Value(bool), @@ -393,120 +505,6 @@ pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reade var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; try parseSseReader(allocator, reader, &typed_callback, cancellation_token); } - -fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { - var buf: std.Io.Writer.Allocating = .init(allocator); - defer buf.deinit(); - try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); - - var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); - defer parsed.deinit(); - - if (parsed.value == .object) { - try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); - } - - var out: std.Io.Writer.Allocating = .init(allocator); - errdefer out.deinit(); - try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); - return try out.toOwnedSlice(); -} - -fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const Callback = @TypeOf(callback.*); - var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; - try streamJson(client, path, requestBody, &typed_callback, cancellation_token); -} - -fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const allocator = client.allocator; - const payload = try stringifyStreamRequest(allocator, requestBody); - defer allocator.free(payload); - - var headers = std.ArrayList(std.http.Header).empty; - defer headers.deinit(allocator); - const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); - defer if (auth_header) |value| allocator.free(value); - - const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); - defer allocator.free(url); - - if (client.http_observer) |obs| { - if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); - } - - const uri = try std.Uri.parse(url); - try checkCancellation(cancellation_token); - - const start = std.Io.Clock.awake.now(client.io); - var req = client.http.request(.POST, uri, .{ - .redirect_behavior = .unhandled, - .headers = .{ .accept_encoding = .{ .override = "identity" } }, - .extra_headers = headers.items, - }) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - defer req.deinit(); - - req.transfer_encoding = .{ .content_length = payload.len }; - var request_body = try req.sendBodyUnflushed(&.{}); - try request_body.writer.writeAll(payload); - try request_body.end(); - try req.connection.?.flush(); - try checkCancellation(cancellation_token); - - var response = req.receiveHead(&.{}) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); - if (response.head.status.class() != .success) { - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - return error.ResponseError; - } - - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - - var transfer_buffer: [8 * 1024]u8 = undefined; - const reader = response.reader(&transfer_buffer); - parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { - error.ReadFailed => return response.bodyErr() orelse err, - else => return err, - }; -} - -fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { - if (content_type) |ct| { - try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); - } - try headers.append(allocator, .{ .name = "Accept", .value = accept }); - - var auth_header: ?[]u8 = null; - if (client.api_key.len > 0) { - auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); - try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); - } - if (client.organization) |organization| { - try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); - } - if (client.project) |project| { - try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); - } - for (client.default_headers) |header| { - try headers.append(allocator, header); - } - return auth_header; -} - const max_sse_line_size = 8192; const max_sse_event_size = 65536; diff --git a/generated/generated_v31.zig b/generated/generated_v31.zig index 298c79c..3967803 100644 --- a/generated/generated_v31.zig +++ b/generated/generated_v31.zig @@ -1,6 +1,6 @@ // -// this code was generated by openapi2zig 0.4.0 (2446685) on 2026-07-22 13:15:12 UTC -// changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:21 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated // const std = @import("std"); @@ -225,6 +225,118 @@ pub fn getJsonResult(comptime T: type, client: *Client, path: []const u8) !ApiRe pub fn postJsonResult(comptime T: type, client: *Client, path: []const u8, payload: anytype) !ApiResult(T) { return parseRawResponse(T, try postJsonRaw(client, path, payload)); } +fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); + + var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + + if (parsed.value == .object) { + try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); + } + + var out: std.Io.Writer.Allocating = .init(allocator); + errdefer out.deinit(); + try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); + return try out.toOwnedSlice(); +} + +fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; + try streamJson(client, path, requestBody, &typed_callback, cancellation_token); +} + +fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const allocator = client.allocator; + const payload = try stringifyStreamRequest(allocator, requestBody); + defer allocator.free(payload); + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); + defer if (auth_header) |value| allocator.free(value); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + try checkCancellation(cancellation_token); + + const start = std.Io.Clock.awake.now(client.io); + var req = client.http.request(.POST, uri, .{ + .redirect_behavior = .unhandled, + .headers = .{ .accept_encoding = .{ .override = "identity" } }, + .extra_headers = headers.items, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + defer req.deinit(); + + req.transfer_encoding = .{ .content_length = payload.len }; + var request_body = try req.sendBodyUnflushed(&.{}); + try request_body.writer.writeAll(payload); + try request_body.end(); + try req.connection.?.flush(); + try checkCancellation(cancellation_token); + + var response = req.receiveHead(&.{}) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + if (response.head.status.class() != .success) { + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + return error.ResponseError; + } + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + + var transfer_buffer: [8 * 1024]u8 = undefined; + const reader = response.reader(&transfer_buffer); + parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { + error.ReadFailed => return response.bodyErr() orelse err, + else => return err, + }; +} + +fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { + if (content_type) |ct| { + try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); + } + try headers.append(allocator, .{ .name = "Accept", .value = accept }); + + var auth_header: ?[]u8 = null; + if (client.api_key.len > 0) { + auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); + try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); + } + if (client.organization) |organization| { + try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); + } + if (client.project) |project| { + try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); + } + for (client.default_headers) |header| { + try headers.append(allocator, header); + } + return auth_header; +} pub const CancellationToken = struct { cancelled: std.atomic.Value(bool), @@ -341,120 +453,6 @@ pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reade var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; try parseSseReader(allocator, reader, &typed_callback, cancellation_token); } - -fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { - var buf: std.Io.Writer.Allocating = .init(allocator); - defer buf.deinit(); - try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); - - var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); - defer parsed.deinit(); - - if (parsed.value == .object) { - try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); - } - - var out: std.Io.Writer.Allocating = .init(allocator); - errdefer out.deinit(); - try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); - return try out.toOwnedSlice(); -} - -fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const Callback = @TypeOf(callback.*); - var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; - try streamJson(client, path, requestBody, &typed_callback, cancellation_token); -} - -fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const allocator = client.allocator; - const payload = try stringifyStreamRequest(allocator, requestBody); - defer allocator.free(payload); - - var headers = std.ArrayList(std.http.Header).empty; - defer headers.deinit(allocator); - const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); - defer if (auth_header) |value| allocator.free(value); - - const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); - defer allocator.free(url); - - if (client.http_observer) |obs| { - if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); - } - - const uri = try std.Uri.parse(url); - try checkCancellation(cancellation_token); - - const start = std.Io.Clock.awake.now(client.io); - var req = client.http.request(.POST, uri, .{ - .redirect_behavior = .unhandled, - .headers = .{ .accept_encoding = .{ .override = "identity" } }, - .extra_headers = headers.items, - }) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - defer req.deinit(); - - req.transfer_encoding = .{ .content_length = payload.len }; - var request_body = try req.sendBodyUnflushed(&.{}); - try request_body.writer.writeAll(payload); - try request_body.end(); - try req.connection.?.flush(); - try checkCancellation(cancellation_token); - - var response = req.receiveHead(&.{}) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); - if (response.head.status.class() != .success) { - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - return error.ResponseError; - } - - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - - var transfer_buffer: [8 * 1024]u8 = undefined; - const reader = response.reader(&transfer_buffer); - parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { - error.ReadFailed => return response.bodyErr() orelse err, - else => return err, - }; -} - -fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { - if (content_type) |ct| { - try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); - } - try headers.append(allocator, .{ .name = "Accept", .value = accept }); - - var auth_header: ?[]u8 = null; - if (client.api_key.len > 0) { - auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); - try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); - } - if (client.organization) |organization| { - try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); - } - if (client.project) |project| { - try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); - } - for (client.default_headers) |header| { - try headers.append(allocator, header); - } - return auth_header; -} - const max_sse_line_size = 8192; const max_sse_event_size = 65536; diff --git a/generated/generated_v31_yaml.zig b/generated/generated_v31_yaml.zig index 298c79c..3967803 100644 --- a/generated/generated_v31_yaml.zig +++ b/generated/generated_v31_yaml.zig @@ -1,6 +1,6 @@ // -// this code was generated by openapi2zig 0.4.0 (2446685) on 2026-07-22 13:15:12 UTC -// changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:21 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated // const std = @import("std"); @@ -225,6 +225,118 @@ pub fn getJsonResult(comptime T: type, client: *Client, path: []const u8) !ApiRe pub fn postJsonResult(comptime T: type, client: *Client, path: []const u8, payload: anytype) !ApiResult(T) { return parseRawResponse(T, try postJsonRaw(client, path, payload)); } +fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); + + var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + + if (parsed.value == .object) { + try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); + } + + var out: std.Io.Writer.Allocating = .init(allocator); + errdefer out.deinit(); + try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); + return try out.toOwnedSlice(); +} + +fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; + try streamJson(client, path, requestBody, &typed_callback, cancellation_token); +} + +fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const allocator = client.allocator; + const payload = try stringifyStreamRequest(allocator, requestBody); + defer allocator.free(payload); + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); + defer if (auth_header) |value| allocator.free(value); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + try checkCancellation(cancellation_token); + + const start = std.Io.Clock.awake.now(client.io); + var req = client.http.request(.POST, uri, .{ + .redirect_behavior = .unhandled, + .headers = .{ .accept_encoding = .{ .override = "identity" } }, + .extra_headers = headers.items, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + defer req.deinit(); + + req.transfer_encoding = .{ .content_length = payload.len }; + var request_body = try req.sendBodyUnflushed(&.{}); + try request_body.writer.writeAll(payload); + try request_body.end(); + try req.connection.?.flush(); + try checkCancellation(cancellation_token); + + var response = req.receiveHead(&.{}) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + if (response.head.status.class() != .success) { + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + return error.ResponseError; + } + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + + var transfer_buffer: [8 * 1024]u8 = undefined; + const reader = response.reader(&transfer_buffer); + parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { + error.ReadFailed => return response.bodyErr() orelse err, + else => return err, + }; +} + +fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { + if (content_type) |ct| { + try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); + } + try headers.append(allocator, .{ .name = "Accept", .value = accept }); + + var auth_header: ?[]u8 = null; + if (client.api_key.len > 0) { + auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); + try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); + } + if (client.organization) |organization| { + try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); + } + if (client.project) |project| { + try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); + } + for (client.default_headers) |header| { + try headers.append(allocator, header); + } + return auth_header; +} pub const CancellationToken = struct { cancelled: std.atomic.Value(bool), @@ -341,120 +453,6 @@ pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reade var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; try parseSseReader(allocator, reader, &typed_callback, cancellation_token); } - -fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { - var buf: std.Io.Writer.Allocating = .init(allocator); - defer buf.deinit(); - try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); - - var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); - defer parsed.deinit(); - - if (parsed.value == .object) { - try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); - } - - var out: std.Io.Writer.Allocating = .init(allocator); - errdefer out.deinit(); - try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); - return try out.toOwnedSlice(); -} - -fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const Callback = @TypeOf(callback.*); - var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; - try streamJson(client, path, requestBody, &typed_callback, cancellation_token); -} - -fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const allocator = client.allocator; - const payload = try stringifyStreamRequest(allocator, requestBody); - defer allocator.free(payload); - - var headers = std.ArrayList(std.http.Header).empty; - defer headers.deinit(allocator); - const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); - defer if (auth_header) |value| allocator.free(value); - - const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); - defer allocator.free(url); - - if (client.http_observer) |obs| { - if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); - } - - const uri = try std.Uri.parse(url); - try checkCancellation(cancellation_token); - - const start = std.Io.Clock.awake.now(client.io); - var req = client.http.request(.POST, uri, .{ - .redirect_behavior = .unhandled, - .headers = .{ .accept_encoding = .{ .override = "identity" } }, - .extra_headers = headers.items, - }) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - defer req.deinit(); - - req.transfer_encoding = .{ .content_length = payload.len }; - var request_body = try req.sendBodyUnflushed(&.{}); - try request_body.writer.writeAll(payload); - try request_body.end(); - try req.connection.?.flush(); - try checkCancellation(cancellation_token); - - var response = req.receiveHead(&.{}) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); - if (response.head.status.class() != .success) { - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - return error.ResponseError; - } - - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - - var transfer_buffer: [8 * 1024]u8 = undefined; - const reader = response.reader(&transfer_buffer); - parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { - error.ReadFailed => return response.bodyErr() orelse err, - else => return err, - }; -} - -fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { - if (content_type) |ct| { - try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); - } - try headers.append(allocator, .{ .name = "Accept", .value = accept }); - - var auth_header: ?[]u8 = null; - if (client.api_key.len > 0) { - auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); - try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); - } - if (client.organization) |organization| { - try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); - } - if (client.project) |project| { - try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); - } - for (client.default_headers) |header| { - try headers.append(allocator, header); - } - return auth_header; -} - const max_sse_line_size = 8192; const max_sse_event_size = 65536; diff --git a/generated/generated_v32.zig b/generated/generated_v32.zig index 2c22fd6..d106be7 100644 --- a/generated/generated_v32.zig +++ b/generated/generated_v32.zig @@ -1,6 +1,6 @@ // -// this code was generated by openapi2zig 0.4.0 (2446685) on 2026-07-22 13:15:10 UTC -// changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:18 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated // const std = @import("std"); @@ -264,6 +264,118 @@ pub fn getJsonResult(comptime T: type, client: *Client, path: []const u8) !ApiRe pub fn postJsonResult(comptime T: type, client: *Client, path: []const u8, payload: anytype) !ApiResult(T) { return parseRawResponse(T, try postJsonRaw(client, path, payload)); } +fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); + + var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + + if (parsed.value == .object) { + try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); + } + + var out: std.Io.Writer.Allocating = .init(allocator); + errdefer out.deinit(); + try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); + return try out.toOwnedSlice(); +} + +fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; + try streamJson(client, path, requestBody, &typed_callback, cancellation_token); +} + +fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const allocator = client.allocator; + const payload = try stringifyStreamRequest(allocator, requestBody); + defer allocator.free(payload); + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); + defer if (auth_header) |value| allocator.free(value); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + try checkCancellation(cancellation_token); + + const start = std.Io.Clock.awake.now(client.io); + var req = client.http.request(.POST, uri, .{ + .redirect_behavior = .unhandled, + .headers = .{ .accept_encoding = .{ .override = "identity" } }, + .extra_headers = headers.items, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + defer req.deinit(); + + req.transfer_encoding = .{ .content_length = payload.len }; + var request_body = try req.sendBodyUnflushed(&.{}); + try request_body.writer.writeAll(payload); + try request_body.end(); + try req.connection.?.flush(); + try checkCancellation(cancellation_token); + + var response = req.receiveHead(&.{}) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + if (response.head.status.class() != .success) { + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + return error.ResponseError; + } + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + + var transfer_buffer: [8 * 1024]u8 = undefined; + const reader = response.reader(&transfer_buffer); + parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { + error.ReadFailed => return response.bodyErr() orelse err, + else => return err, + }; +} + +fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { + if (content_type) |ct| { + try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); + } + try headers.append(allocator, .{ .name = "Accept", .value = accept }); + + var auth_header: ?[]u8 = null; + if (client.api_key.len > 0) { + auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); + try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); + } + if (client.organization) |organization| { + try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); + } + if (client.project) |project| { + try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); + } + for (client.default_headers) |header| { + try headers.append(allocator, header); + } + return auth_header; +} pub const CancellationToken = struct { cancelled: std.atomic.Value(bool), @@ -380,120 +492,6 @@ pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reade var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; try parseSseReader(allocator, reader, &typed_callback, cancellation_token); } - -fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { - var buf: std.Io.Writer.Allocating = .init(allocator); - defer buf.deinit(); - try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); - - var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); - defer parsed.deinit(); - - if (parsed.value == .object) { - try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); - } - - var out: std.Io.Writer.Allocating = .init(allocator); - errdefer out.deinit(); - try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); - return try out.toOwnedSlice(); -} - -fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const Callback = @TypeOf(callback.*); - var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; - try streamJson(client, path, requestBody, &typed_callback, cancellation_token); -} - -fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const allocator = client.allocator; - const payload = try stringifyStreamRequest(allocator, requestBody); - defer allocator.free(payload); - - var headers = std.ArrayList(std.http.Header).empty; - defer headers.deinit(allocator); - const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); - defer if (auth_header) |value| allocator.free(value); - - const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); - defer allocator.free(url); - - if (client.http_observer) |obs| { - if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); - } - - const uri = try std.Uri.parse(url); - try checkCancellation(cancellation_token); - - const start = std.Io.Clock.awake.now(client.io); - var req = client.http.request(.POST, uri, .{ - .redirect_behavior = .unhandled, - .headers = .{ .accept_encoding = .{ .override = "identity" } }, - .extra_headers = headers.items, - }) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - defer req.deinit(); - - req.transfer_encoding = .{ .content_length = payload.len }; - var request_body = try req.sendBodyUnflushed(&.{}); - try request_body.writer.writeAll(payload); - try request_body.end(); - try req.connection.?.flush(); - try checkCancellation(cancellation_token); - - var response = req.receiveHead(&.{}) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); - if (response.head.status.class() != .success) { - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - return error.ResponseError; - } - - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - - var transfer_buffer: [8 * 1024]u8 = undefined; - const reader = response.reader(&transfer_buffer); - parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { - error.ReadFailed => return response.bodyErr() orelse err, - else => return err, - }; -} - -fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { - if (content_type) |ct| { - try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); - } - try headers.append(allocator, .{ .name = "Accept", .value = accept }); - - var auth_header: ?[]u8 = null; - if (client.api_key.len > 0) { - auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); - try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); - } - if (client.organization) |organization| { - try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); - } - if (client.project) |project| { - try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); - } - for (client.default_headers) |header| { - try headers.append(allocator, header); - } - return auth_header; -} - const max_sse_line_size = 8192; const max_sse_event_size = 65536; diff --git a/generated/generated_v3_yaml.zig b/generated/generated_v3_yaml.zig index 31a39be..9648ca7 100644 --- a/generated/generated_v3_yaml.zig +++ b/generated/generated_v3_yaml.zig @@ -1,6 +1,6 @@ // -// this code was generated by openapi2zig 0.4.0 (2446685) on 2026-07-22 13:15:10 UTC -// changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:21 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated // const std = @import("std"); @@ -277,6 +277,118 @@ pub fn getJsonResult(comptime T: type, client: *Client, path: []const u8) !ApiRe pub fn postJsonResult(comptime T: type, client: *Client, path: []const u8, payload: anytype) !ApiResult(T) { return parseRawResponse(T, try postJsonRaw(client, path, payload)); } +fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); + + var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + + if (parsed.value == .object) { + try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); + } + + var out: std.Io.Writer.Allocating = .init(allocator); + errdefer out.deinit(); + try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); + return try out.toOwnedSlice(); +} + +fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; + try streamJson(client, path, requestBody, &typed_callback, cancellation_token); +} + +fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const allocator = client.allocator; + const payload = try stringifyStreamRequest(allocator, requestBody); + defer allocator.free(payload); + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); + defer if (auth_header) |value| allocator.free(value); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + try checkCancellation(cancellation_token); + + const start = std.Io.Clock.awake.now(client.io); + var req = client.http.request(.POST, uri, .{ + .redirect_behavior = .unhandled, + .headers = .{ .accept_encoding = .{ .override = "identity" } }, + .extra_headers = headers.items, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + defer req.deinit(); + + req.transfer_encoding = .{ .content_length = payload.len }; + var request_body = try req.sendBodyUnflushed(&.{}); + try request_body.writer.writeAll(payload); + try request_body.end(); + try req.connection.?.flush(); + try checkCancellation(cancellation_token); + + var response = req.receiveHead(&.{}) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + if (response.head.status.class() != .success) { + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + return error.ResponseError; + } + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + + var transfer_buffer: [8 * 1024]u8 = undefined; + const reader = response.reader(&transfer_buffer); + parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { + error.ReadFailed => return response.bodyErr() orelse err, + else => return err, + }; +} + +fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { + if (content_type) |ct| { + try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); + } + try headers.append(allocator, .{ .name = "Accept", .value = accept }); + + var auth_header: ?[]u8 = null; + if (client.api_key.len > 0) { + auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); + try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); + } + if (client.organization) |organization| { + try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); + } + if (client.project) |project| { + try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); + } + for (client.default_headers) |header| { + try headers.append(allocator, header); + } + return auth_header; +} pub const CancellationToken = struct { cancelled: std.atomic.Value(bool), @@ -393,120 +505,6 @@ pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reade var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; try parseSseReader(allocator, reader, &typed_callback, cancellation_token); } - -fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { - var buf: std.Io.Writer.Allocating = .init(allocator); - defer buf.deinit(); - try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); - - var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); - defer parsed.deinit(); - - if (parsed.value == .object) { - try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); - } - - var out: std.Io.Writer.Allocating = .init(allocator); - errdefer out.deinit(); - try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); - return try out.toOwnedSlice(); -} - -fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const Callback = @TypeOf(callback.*); - var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; - try streamJson(client, path, requestBody, &typed_callback, cancellation_token); -} - -fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const allocator = client.allocator; - const payload = try stringifyStreamRequest(allocator, requestBody); - defer allocator.free(payload); - - var headers = std.ArrayList(std.http.Header).empty; - defer headers.deinit(allocator); - const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); - defer if (auth_header) |value| allocator.free(value); - - const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); - defer allocator.free(url); - - if (client.http_observer) |obs| { - if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); - } - - const uri = try std.Uri.parse(url); - try checkCancellation(cancellation_token); - - const start = std.Io.Clock.awake.now(client.io); - var req = client.http.request(.POST, uri, .{ - .redirect_behavior = .unhandled, - .headers = .{ .accept_encoding = .{ .override = "identity" } }, - .extra_headers = headers.items, - }) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - defer req.deinit(); - - req.transfer_encoding = .{ .content_length = payload.len }; - var request_body = try req.sendBodyUnflushed(&.{}); - try request_body.writer.writeAll(payload); - try request_body.end(); - try req.connection.?.flush(); - try checkCancellation(cancellation_token); - - var response = req.receiveHead(&.{}) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); - if (response.head.status.class() != .success) { - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - return error.ResponseError; - } - - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - - var transfer_buffer: [8 * 1024]u8 = undefined; - const reader = response.reader(&transfer_buffer); - parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { - error.ReadFailed => return response.bodyErr() orelse err, - else => return err, - }; -} - -fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { - if (content_type) |ct| { - try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); - } - try headers.append(allocator, .{ .name = "Accept", .value = accept }); - - var auth_header: ?[]u8 = null; - if (client.api_key.len > 0) { - auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); - try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); - } - if (client.organization) |organization| { - try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); - } - if (client.project) |project| { - try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); - } - for (client.default_headers) |header| { - try headers.append(allocator, header); - } - return auth_header; -} - const max_sse_line_size = 8192; const max_sse_event_size = 65536; diff --git a/generated/lmstudio.zig b/generated/lmstudio.zig index 176b240..a77c951 100644 --- a/generated/lmstudio.zig +++ b/generated/lmstudio.zig @@ -1,6 +1,6 @@ // -// this code was generated by openapi2zig 0.4.0 (2446685) on 2026-07-22 13:15:10 UTC -// changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:18 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated // const std = @import("std"); @@ -592,6 +592,118 @@ pub fn getJsonResult(comptime T: type, client: *Client, path: []const u8) !ApiRe pub fn postJsonResult(comptime T: type, client: *Client, path: []const u8, payload: anytype) !ApiResult(T) { return parseRawResponse(T, try postJsonRaw(client, path, payload)); } +fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); + + var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + + if (parsed.value == .object) { + try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); + } + + var out: std.Io.Writer.Allocating = .init(allocator); + errdefer out.deinit(); + try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); + return try out.toOwnedSlice(); +} + +fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; + try streamJson(client, path, requestBody, &typed_callback, cancellation_token); +} + +fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const allocator = client.allocator; + const payload = try stringifyStreamRequest(allocator, requestBody); + defer allocator.free(payload); + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); + defer if (auth_header) |value| allocator.free(value); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + try checkCancellation(cancellation_token); + + const start = std.Io.Clock.awake.now(client.io); + var req = client.http.request(.POST, uri, .{ + .redirect_behavior = .unhandled, + .headers = .{ .accept_encoding = .{ .override = "identity" } }, + .extra_headers = headers.items, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + defer req.deinit(); + + req.transfer_encoding = .{ .content_length = payload.len }; + var request_body = try req.sendBodyUnflushed(&.{}); + try request_body.writer.writeAll(payload); + try request_body.end(); + try req.connection.?.flush(); + try checkCancellation(cancellation_token); + + var response = req.receiveHead(&.{}) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + if (response.head.status.class() != .success) { + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + return error.ResponseError; + } + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + + var transfer_buffer: [8 * 1024]u8 = undefined; + const reader = response.reader(&transfer_buffer); + parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { + error.ReadFailed => return response.bodyErr() orelse err, + else => return err, + }; +} + +fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { + if (content_type) |ct| { + try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); + } + try headers.append(allocator, .{ .name = "Accept", .value = accept }); + + var auth_header: ?[]u8 = null; + if (client.api_key.len > 0) { + auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); + try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); + } + if (client.organization) |organization| { + try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); + } + if (client.project) |project| { + try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); + } + for (client.default_headers) |header| { + try headers.append(allocator, header); + } + return auth_header; +} pub const CancellationToken = struct { cancelled: std.atomic.Value(bool), @@ -708,120 +820,6 @@ pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reade var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; try parseSseReader(allocator, reader, &typed_callback, cancellation_token); } - -fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { - var buf: std.Io.Writer.Allocating = .init(allocator); - defer buf.deinit(); - try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); - - var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); - defer parsed.deinit(); - - if (parsed.value == .object) { - try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); - } - - var out: std.Io.Writer.Allocating = .init(allocator); - errdefer out.deinit(); - try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); - return try out.toOwnedSlice(); -} - -fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const Callback = @TypeOf(callback.*); - var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; - try streamJson(client, path, requestBody, &typed_callback, cancellation_token); -} - -fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const allocator = client.allocator; - const payload = try stringifyStreamRequest(allocator, requestBody); - defer allocator.free(payload); - - var headers = std.ArrayList(std.http.Header).empty; - defer headers.deinit(allocator); - const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); - defer if (auth_header) |value| allocator.free(value); - - const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); - defer allocator.free(url); - - if (client.http_observer) |obs| { - if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); - } - - const uri = try std.Uri.parse(url); - try checkCancellation(cancellation_token); - - const start = std.Io.Clock.awake.now(client.io); - var req = client.http.request(.POST, uri, .{ - .redirect_behavior = .unhandled, - .headers = .{ .accept_encoding = .{ .override = "identity" } }, - .extra_headers = headers.items, - }) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - defer req.deinit(); - - req.transfer_encoding = .{ .content_length = payload.len }; - var request_body = try req.sendBodyUnflushed(&.{}); - try request_body.writer.writeAll(payload); - try request_body.end(); - try req.connection.?.flush(); - try checkCancellation(cancellation_token); - - var response = req.receiveHead(&.{}) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); - if (response.head.status.class() != .success) { - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - return error.ResponseError; - } - - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - - var transfer_buffer: [8 * 1024]u8 = undefined; - const reader = response.reader(&transfer_buffer); - parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { - error.ReadFailed => return response.bodyErr() orelse err, - else => return err, - }; -} - -fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { - if (content_type) |ct| { - try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); - } - try headers.append(allocator, .{ .name = "Accept", .value = accept }); - - var auth_header: ?[]u8 = null; - if (client.api_key.len > 0) { - auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); - try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); - } - if (client.organization) |organization| { - try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); - } - if (client.project) |project| { - try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); - } - for (client.default_headers) |header| { - try headers.append(allocator, header); - } - return auth_header; -} - const max_sse_line_size = 8192; const max_sse_event_size = 65536; diff --git a/generated/main.zig b/generated/main.zig index 7254169..2964d24 100644 --- a/generated/main.zig +++ b/generated/main.zig @@ -7,6 +7,7 @@ const v31_yaml = @import("generated_v31_yaml.zig"); const lmstudio = @import("lmstudio.zig"); const openai = @import("openai.zig"); const anthropic = @import("anthropic.zig"); +const multi_v3 = @import("multi/client.zig"); fn logRequest(ctx: ?*anyopaque, method: std.http.Method, url: []const u8, headers: []const std.http.Header, body: ?[]const u8) void { _ = ctx; @@ -66,6 +67,14 @@ pub fn main(init: std.process.Init) !void { defer v2_yaml_client.deinit(); var v31_yaml_client = v31_yaml.Client.init(allocator, io, ""); defer v31_yaml_client.deinit(); + var multi_v3_client = multi_v3.Client.init(allocator, io, ""); + multi_v3_client.http_observer = .{ + .ctx = null, + .onRequest = &logRequest, + .onResponse = &logResponse, + .onError = &logError, + }; + defer multi_v3_client.deinit(); _ = &v3_yaml_client; _ = &v2_yaml_client; _ = &v31_yaml_client; @@ -87,4 +96,11 @@ pub fn main(init: std.process.Init) !void { }; defer pet2.deinit(); std.debug.print("Found Pet v2 with ID:{any}\n\n", .{pet2.value().id}); + + var multi_pet = multi_v3.getPetById(&multi_v3_client, 1) catch |err| { + std.debug.print("Failed to get Pet (multi-file): {any}\n", .{err}); + return; + }; + defer multi_pet.deinit(); + std.debug.print("Found Pet (multi-file) with ID:{any}\n\n", .{multi_pet.value().id}); } diff --git a/generated/multi/client.zig b/generated/multi/client.zig new file mode 100644 index 0000000..10e47ea --- /dev/null +++ b/generated/multi/client.zig @@ -0,0 +1,1091 @@ +// +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:21 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// + +/////////////////////////////////////////// +// Generated Zig API client from OpenAPI +/////////////////////////////////////////// + +const std = @import("std"); +const models = @import("models.zig"); +const runtime = @import("runtime.zig"); +const Owned = runtime.Owned; +const HttpObserver = runtime.HttpObserver; +const RawResponse = runtime.RawResponse; +const ParseErrorResponse = runtime.ParseErrorResponse; +const ApiResult = runtime.ApiResult; +const CancellationToken = runtime.CancellationToken; +const checkCancellation = runtime.checkCancellation; +const parseSseReader = runtime.parseSseReader; +const parseSseBytes = runtime.parseSseBytes; +const parseSseBytesTyped = runtime.parseSseBytesTyped; +const parseSseReaderTyped = runtime.parseSseReaderTyped; +const TypedSseCallback = runtime.TypedSseCallback; + +pub const Client = struct { + allocator: std.mem.Allocator, + io: std.Io, + http: std.http.Client, + api_key: []const u8, + base_url: []const u8 = "https://petstore3.swagger.io/api/v3", + organization: ?[]const u8 = null, + project: ?[]const u8 = null, + default_headers: []const std.http.Header = &.{}, + http_observer: ?HttpObserver = null, + + pub fn init(allocator: std.mem.Allocator, io: std.Io, api_key: []const u8) Client { + return .{ + .allocator = allocator, + .io = io, + .http = .{ .allocator = allocator, .io = io }, + .api_key = api_key, + .http_observer = null, + }; + } + + pub fn deinit(self: *Client) void { + self.http.deinit(); + } + + pub fn withBaseUrl(self: *Client, base_url: []const u8) void { + self.base_url = base_url; + } +}; + +fn isQueryChar(c: u8) bool { + return std.ascii.isAlphanumeric(c) or switch (c) { + '-', '.', '_', '~' => true, + else => false, + }; +} + +fn writeQueryComponent(writer: *std.Io.Writer, value: []const u8) !void { + try std.Uri.Component.percentEncode(writer, value, isQueryChar); +} + +fn writeQueryValue(writer: *std.Io.Writer, value: anytype) !void { + const T = @TypeOf(value); + switch (@typeInfo(T)) { + .pointer => |ptr| { + if (ptr.size == .slice and ptr.child == u8) { + try writeQueryComponent(writer, value); + } else { + try std.json.Stringify.value(value, .{}, writer); + } + }, + .int, .comptime_int, .float, .comptime_float, .bool => try writer.print("{}", .{value}), + .@"enum" => try writeQueryComponent(writer, @tagName(value)), + else => try std.json.Stringify.value(value, .{}, writer), + } +} + +fn appendQueryParam(writer: *std.Io.Writer, first_query: *bool, name: []const u8, value: anytype) !void { + if (first_query.*) { + try writer.writeByte('?'); + first_query.* = false; + } else { + try writer.writeByte('&'); + } + try writeQueryComponent(writer, name); + try writer.writeByte('='); + try writeQueryValue(writer, value); +} + +pub fn requestRaw(client: *Client, method: std.http.Method, url: []const u8, payload: ?[]const u8) !RawResponse { + return requestRawWithContentType(client, method, url, payload, "application/json"); +} + +pub fn requestRawWithContentType(client: *Client, method: std.http.Method, url: []const u8, payload: ?[]const u8, content_type_value: []const u8) !RawResponse { + const allocator = client.allocator; + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const content_type: ?[]const u8 = if (payload != null) content_type_value else null; + const auth_header = try appendClientHeaders(allocator, &headers, client, content_type, "application/json"); + defer if (auth_header) |value| allocator.free(value); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, method, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + var response_body: std.Io.Writer.Allocating = .init(allocator); + defer response_body.deinit(); + + const start = std.Io.Clock.awake.now(client.io); + const result = client.http.fetch(.{ + .location = .{ .uri = uri }, + .method = method, + .extra_headers = headers.items, + .payload = payload, + .response_writer = &response_body.writer, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, method, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + + const body = try response_body.toOwnedSlice(); + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, method, url, result.status, &.{}, body, elapsed_ns); + } + + return .{ + .allocator = allocator, + .status = result.status, + .body = body, + }; +} + +pub fn getRaw(client: *Client, path: []const u8) !RawResponse { + const url = try std.fmt.allocPrint(client.allocator, "{s}{s}", .{ client.base_url, path }); + defer client.allocator.free(url); + return requestRaw(client, .GET, url, null); +} + +pub fn postJsonRaw(client: *Client, path: []const u8, payload: anytype) !RawResponse { + const allocator = client.allocator; + var str: std.Io.Writer.Allocating = .init(allocator); + defer str.deinit(); + try std.json.Stringify.value(payload, .{ .emit_null_optional_fields = false }, &str.writer); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + return requestRaw(client, .POST, url, str.written()); +} + +pub fn parseRawResponse(comptime T: type, raw: RawResponse) !ApiResult(T) { + if (raw.status.class() != .success) return .{ .api_error = raw }; + const parsed = std.json.parseFromSlice(T, raw.allocator, raw.body, .{ .ignore_unknown_fields = true }) catch |err| { + return .{ .parse_error = .{ .raw = raw, .error_name = @errorName(err) } }; + }; + return .{ .ok = .{ .allocator = raw.allocator, .body = raw.body, .parsed = parsed } }; +} + +pub fn getJsonResult(comptime T: type, client: *Client, path: []const u8) !ApiResult(T) { + return parseRawResponse(T, try getRaw(client, path)); +} + +pub fn postJsonResult(comptime T: type, client: *Client, path: []const u8, payload: anytype) !ApiResult(T) { + return parseRawResponse(T, try postJsonRaw(client, path, payload)); +} +fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); + + var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + + if (parsed.value == .object) { + try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); + } + + var out: std.Io.Writer.Allocating = .init(allocator); + errdefer out.deinit(); + try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); + return try out.toOwnedSlice(); +} + +fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; + try streamJson(client, path, requestBody, &typed_callback, cancellation_token); +} + +fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const allocator = client.allocator; + const payload = try stringifyStreamRequest(allocator, requestBody); + defer allocator.free(payload); + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); + defer if (auth_header) |value| allocator.free(value); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + try checkCancellation(cancellation_token); + + const start = std.Io.Clock.awake.now(client.io); + var req = client.http.request(.POST, uri, .{ + .redirect_behavior = .unhandled, + .headers = .{ .accept_encoding = .{ .override = "identity" } }, + .extra_headers = headers.items, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + defer req.deinit(); + + req.transfer_encoding = .{ .content_length = payload.len }; + var request_body = try req.sendBodyUnflushed(&.{}); + try request_body.writer.writeAll(payload); + try request_body.end(); + try req.connection.?.flush(); + try checkCancellation(cancellation_token); + + var response = req.receiveHead(&.{}) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + if (response.head.status.class() != .success) { + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + return error.ResponseError; + } + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + + var transfer_buffer: [8 * 1024]u8 = undefined; + const reader = response.reader(&transfer_buffer); + parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { + error.ReadFailed => return response.bodyErr() orelse err, + else => return err, + }; +} + +fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { + if (content_type) |ct| { + try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); + } + try headers.append(allocator, .{ .name = "Accept", .value = accept }); + + var auth_header: ?[]u8 = null; + if (client.api_key.len > 0) { + auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); + try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); + } + if (client.organization) |organization| { + try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); + } + if (client.project) |project| { + try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); + } + for (client.default_headers) |header| { + try headers.append(allocator, header); + } + return auth_header; +} + +///////////////// +// Summary: +// Place an order for a pet +// +// Description: +// Place a new order in the store +// +pub fn placeOrder(client: *Client, requestBody: models.Order) !Owned(models.Order) { + var result = try placeOrderResult(client, requestBody); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn placeOrderRaw(client: *Client, requestBody: models.Order) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/store/order", .{client.base_url}); + + var str: std.Io.Writer.Allocating = .init(allocator); + defer str.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &str.writer); + const payload: ?[]const u8 = str.written(); + + return requestRaw(client, std.http.Method.POST, uri_buf.written(), payload); +} + +pub fn placeOrderResult(client: *Client, requestBody: models.Order) !ApiResult(models.Order) { + return parseRawResponse(models.Order, try placeOrderRaw(client, requestBody)); +} + +///////////////// +// Summary: +// uploads an image +// +// Description: +// +// +pub fn uploadFile(client: *Client, petId: i64, additionalMetadata: ?[]const u8, requestBody: []const u8) !Owned(models.ApiResponse) { + var result = try uploadFileResult(client, petId, additionalMetadata, requestBody); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn uploadFileRaw(client: *Client, petId: i64, additionalMetadata: ?[]const u8, requestBody: []const u8) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/pet/{d}/uploadImage", .{client.base_url, petId}); + var first_query = true; + if (additionalMetadata) |value| { + try appendQueryParam(&uri_buf.writer, &first_query, "additionalMetadata", value); + } + const payload: ?[]const u8 = requestBody; + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/octet-stream", "application/json"); + defer if (auth_header) |value| allocator.free(value); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, std.http.Method.POST, uri_buf.written(), headers.items, payload); + } + + const uri = try std.Uri.parse(uri_buf.written()); + var response_body: std.Io.Writer.Allocating = .init(allocator); + defer response_body.deinit(); + const start = std.Io.Clock.awake.now(client.io); + const result = client.http.fetch(.{ + .location = .{ .uri = uri }, + .method = std.http.Method.POST, + .extra_headers = headers.items, + .payload = payload, + .response_writer = &response_body.writer, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, std.http.Method.POST, uri_buf.written(), @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + + const body = try response_body.toOwnedSlice(); + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, std.http.Method.POST, uri_buf.written(), result.status, &.{}, body, elapsed_ns); + } + + return .{ + .allocator = allocator, + .status = result.status, + .body = body, + }; +} + +pub fn uploadFileResult(client: *Client, petId: i64, additionalMetadata: ?[]const u8, requestBody: []const u8) !ApiResult(models.ApiResponse) { + return parseRawResponse(models.ApiResponse, try uploadFileRaw(client, petId, additionalMetadata, requestBody)); +} + +///////////////// +// Summary: +// Find pet by ID +// +// Description: +// Returns a single pet +// +pub fn getPetById(client: *Client, petId: i64) !Owned(models.Pet) { + var result = try getPetByIdResult(client, petId); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn getPetByIdRaw(client: *Client, petId: i64) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/pet/{d}", .{client.base_url, petId}); + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.GET, uri_buf.written(), payload); +} + +pub fn getPetByIdResult(client: *Client, petId: i64) !ApiResult(models.Pet) { + return parseRawResponse(models.Pet, try getPetByIdRaw(client, petId)); +} + +///////////////// +// Summary: +// Updates a pet in the store with form data +// +// Description: +// +// +pub fn updatePetWithForm(client: *Client, petId: i64, name: ?[]const u8, status: ?[]const u8) !void { + var raw = try updatePetWithFormRaw(client, petId, name, status); + defer raw.deinit(); + if (raw.status.class() != .success) return error.ResponseError; +} + +pub fn updatePetWithFormRaw(client: *Client, petId: i64, name: ?[]const u8, status: ?[]const u8) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/pet/{d}", .{client.base_url, petId}); + var first_query = true; + if (name) |value| { + try appendQueryParam(&uri_buf.writer, &first_query, "name", value); + } + if (status) |value| { + try appendQueryParam(&uri_buf.writer, &first_query, "status", value); + } + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.POST, uri_buf.written(), payload); +} + +///////////////// +// Summary: +// Deletes a pet +// +// Description: +// +// +pub fn deletePet(client: *Client, api_key: []const u8, petId: i64) !void { + var raw = try deletePetRaw(client, api_key, petId); + defer raw.deinit(); + if (raw.status.class() != .success) return error.ResponseError; +} + +pub fn deletePetRaw(client: *Client, api_key: []const u8, petId: i64) !RawResponse { + const allocator = client.allocator; + _ = api_key; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/pet/{d}", .{client.base_url, petId}); + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.DELETE, uri_buf.written(), payload); +} + +///////////////// +// Summary: +// Finds Pets by tags +// +// Description: +// Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. +// +pub fn findPetsByTags(client: *Client, tags: ?[]const u8) !Owned([]const std.json.Value) { + var result = try findPetsByTagsResult(client, tags); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn findPetsByTagsRaw(client: *Client, tags: ?[]const u8) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/pet/findByTags", .{client.base_url}); + var first_query = true; + if (tags) |value| { + try appendQueryParam(&uri_buf.writer, &first_query, "tags", value); + } + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.GET, uri_buf.written(), payload); +} + +pub fn findPetsByTagsResult(client: *Client, tags: ?[]const u8) !ApiResult([]const std.json.Value) { + return parseRawResponse([]const std.json.Value, try findPetsByTagsRaw(client, tags)); +} + +///////////////// +// Summary: +// Logs user into the system +// +// Description: +// +// +pub fn loginUser(client: *Client, username: ?[]const u8, password: ?[]const u8) !Owned([]const u8) { + var result = try loginUserResult(client, username, password); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn loginUserRaw(client: *Client, username: ?[]const u8, password: ?[]const u8) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/user/login", .{client.base_url}); + var first_query = true; + if (username) |value| { + try appendQueryParam(&uri_buf.writer, &first_query, "username", value); + } + if (password) |value| { + try appendQueryParam(&uri_buf.writer, &first_query, "password", value); + } + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.GET, uri_buf.written(), payload); +} + +pub fn loginUserResult(client: *Client, username: ?[]const u8, password: ?[]const u8) !ApiResult([]const u8) { + return parseRawResponse([]const u8, try loginUserRaw(client, username, password)); +} + +///////////////// +// Summary: +// Finds Pets by status +// +// Description: +// Multiple status values can be provided with comma separated strings +// +pub fn findPetsByStatus(client: *Client, status: ?[]const u8) !Owned([]const std.json.Value) { + var result = try findPetsByStatusResult(client, status); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn findPetsByStatusRaw(client: *Client, status: ?[]const u8) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/pet/findByStatus", .{client.base_url}); + var first_query = true; + if (status) |value| { + try appendQueryParam(&uri_buf.writer, &first_query, "status", value); + } + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.GET, uri_buf.written(), payload); +} + +pub fn findPetsByStatusResult(client: *Client, status: ?[]const u8) !ApiResult([]const std.json.Value) { + return parseRawResponse([]const std.json.Value, try findPetsByStatusRaw(client, status)); +} + +///////////////// +// Summary: +// Returns pet inventories by status +// +// Description: +// Returns a map of status codes to quantities +// +pub fn getInventory(client: *Client) !Owned(std.json.Value) { + var result = try getInventoryResult(client); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn getInventoryRaw(client: *Client) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/store/inventory", .{client.base_url}); + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.GET, uri_buf.written(), payload); +} + +pub fn getInventoryResult(client: *Client) !ApiResult(std.json.Value) { + return parseRawResponse(std.json.Value, try getInventoryRaw(client)); +} + +///////////////// +// Summary: +// Get user by user name +// +// Description: +// +// +pub fn getUserByName(client: *Client, username: []const u8) !Owned(models.User) { + var result = try getUserByNameResult(client, username); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn getUserByNameRaw(client: *Client, username: []const u8) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/user/{s}", .{client.base_url, username}); + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.GET, uri_buf.written(), payload); +} + +pub fn getUserByNameResult(client: *Client, username: []const u8) !ApiResult(models.User) { + return parseRawResponse(models.User, try getUserByNameRaw(client, username)); +} + +///////////////// +// Summary: +// Update user +// +// Description: +// This can only be done by the logged in user. +// +pub fn updateUser(client: *Client, username: []const u8, requestBody: models.User) !void { + var raw = try updateUserRaw(client, username, requestBody); + defer raw.deinit(); + if (raw.status.class() != .success) return error.ResponseError; +} + +pub fn updateUserRaw(client: *Client, username: []const u8, requestBody: models.User) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/user/{s}", .{client.base_url, username}); + + var str: std.Io.Writer.Allocating = .init(allocator); + defer str.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &str.writer); + const payload: ?[]const u8 = str.written(); + + return requestRaw(client, std.http.Method.PUT, uri_buf.written(), payload); +} + +///////////////// +// Summary: +// Delete user +// +// Description: +// This can only be done by the logged in user. +// +pub fn deleteUser(client: *Client, username: []const u8) !void { + var raw = try deleteUserRaw(client, username); + defer raw.deinit(); + if (raw.status.class() != .success) return error.ResponseError; +} + +pub fn deleteUserRaw(client: *Client, username: []const u8) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/user/{s}", .{client.base_url, username}); + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.DELETE, uri_buf.written(), payload); +} + +///////////////// +// Summary: +// Create user +// +// Description: +// This can only be done by the logged in user. +// +pub fn createUser(client: *Client, requestBody: models.User) !void { + var raw = try createUserRaw(client, requestBody); + defer raw.deinit(); + if (raw.status.class() != .success) return error.ResponseError; +} + +pub fn createUserRaw(client: *Client, requestBody: models.User) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/user", .{client.base_url}); + + var str: std.Io.Writer.Allocating = .init(allocator); + defer str.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &str.writer); + const payload: ?[]const u8 = str.written(); + + return requestRaw(client, std.http.Method.POST, uri_buf.written(), payload); +} + +///////////////// +// Summary: +// Creates list of users with given input array +// +// Description: +// Creates list of users with given input array +// +pub fn createUsersWithListInput(client: *Client, requestBody: []const std.json.Value) !Owned(models.User) { + var result = try createUsersWithListInputResult(client, requestBody); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn createUsersWithListInputRaw(client: *Client, requestBody: []const std.json.Value) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/user/createWithList", .{client.base_url}); + + var str: std.Io.Writer.Allocating = .init(allocator); + defer str.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &str.writer); + const payload: ?[]const u8 = str.written(); + + return requestRaw(client, std.http.Method.POST, uri_buf.written(), payload); +} + +pub fn createUsersWithListInputResult(client: *Client, requestBody: []const std.json.Value) !ApiResult(models.User) { + return parseRawResponse(models.User, try createUsersWithListInputRaw(client, requestBody)); +} + +///////////////// +// Summary: +// Add a new pet to the store +// +// Description: +// Add a new pet to the store +// +pub fn addPet(client: *Client, requestBody: models.Pet) !Owned(models.Pet) { + var result = try addPetResult(client, requestBody); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn addPetRaw(client: *Client, requestBody: models.Pet) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/pet", .{client.base_url}); + + var str: std.Io.Writer.Allocating = .init(allocator); + defer str.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &str.writer); + const payload: ?[]const u8 = str.written(); + + return requestRaw(client, std.http.Method.POST, uri_buf.written(), payload); +} + +pub fn addPetResult(client: *Client, requestBody: models.Pet) !ApiResult(models.Pet) { + return parseRawResponse(models.Pet, try addPetRaw(client, requestBody)); +} + +///////////////// +// Summary: +// Update an existing pet +// +// Description: +// Update an existing pet by Id +// +pub fn updatePet(client: *Client, requestBody: models.Pet) !Owned(models.Pet) { + var result = try updatePetResult(client, requestBody); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn updatePetRaw(client: *Client, requestBody: models.Pet) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/pet", .{client.base_url}); + + var str: std.Io.Writer.Allocating = .init(allocator); + defer str.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &str.writer); + const payload: ?[]const u8 = str.written(); + + return requestRaw(client, std.http.Method.PUT, uri_buf.written(), payload); +} + +pub fn updatePetResult(client: *Client, requestBody: models.Pet) !ApiResult(models.Pet) { + return parseRawResponse(models.Pet, try updatePetRaw(client, requestBody)); +} + +///////////////// +// Summary: +// Find purchase order by ID +// +// Description: +// For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions +// +pub fn getOrderById(client: *Client, orderId: i64) !Owned(models.Order) { + var result = try getOrderByIdResult(client, orderId); + switch (result) { + .ok => |ok| return ok, + .api_error => |*err| { + err.deinit(); + return error.ResponseError; + }, + .parse_error => |*err| { + err.raw.deinit(); + return error.ResponseParseError; + }, + } +} + +pub fn getOrderByIdRaw(client: *Client, orderId: i64) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/store/order/{d}", .{client.base_url, orderId}); + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.GET, uri_buf.written(), payload); +} + +pub fn getOrderByIdResult(client: *Client, orderId: i64) !ApiResult(models.Order) { + return parseRawResponse(models.Order, try getOrderByIdRaw(client, orderId)); +} + +///////////////// +// Summary: +// Delete purchase order by ID +// +// Description: +// For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors +// +pub fn deleteOrder(client: *Client, orderId: i64) !void { + var raw = try deleteOrderRaw(client, orderId); + defer raw.deinit(); + if (raw.status.class() != .success) return error.ResponseError; +} + +pub fn deleteOrderRaw(client: *Client, orderId: i64) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/store/order/{d}", .{client.base_url, orderId}); + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.DELETE, uri_buf.written(), payload); +} + +///////////////// +// Summary: +// Logs out current logged in user session +// +// Description: +// +// +pub fn logoutUser(client: *Client) !void { + var raw = try logoutUserRaw(client); + defer raw.deinit(); + if (raw.status.class() != .success) return error.ResponseError; +} + +pub fn logoutUserRaw(client: *Client) !RawResponse { + const allocator = client.allocator; + var uri_buf: std.Io.Writer.Allocating = .init(allocator); + defer uri_buf.deinit(); + try uri_buf.writer.print("{s}/user/logout", .{client.base_url}); + const payload: ?[]const u8 = null; + + return requestRaw(client, std.http.Method.GET, uri_buf.written(), payload); +} + + +pub const resources = struct { + pub const pet = struct { + pub fn addpet(client: *Client, requestBody: models.Pet) !Owned(models.Pet) { + return addPet(client, requestBody); + } + pub fn addpetResult(client: *Client, requestBody: models.Pet) !ApiResult(models.Pet) { + return addPetResult(client, requestBody); + } + pub fn delete(client: *Client, api_key: []const u8, petId: i64) !void { + return deletePet(client, api_key, petId); + } + pub fn get(client: *Client, petId: i64) !Owned(models.Pet) { + return getPetById(client, petId); + } + pub fn getResult(client: *Client, petId: i64) !ApiResult(models.Pet) { + return getPetByIdResult(client, petId); + } + pub fn updatepet_(client: *Client, requestBody: models.Pet) !Owned(models.Pet) { + return updatePet(client, requestBody); + } + pub fn updatepet_Result(client: *Client, requestBody: models.Pet) !ApiResult(models.Pet) { + return updatePetResult(client, requestBody); + } + pub fn updatepetwithform_(client: *Client, petId: i64, name: ?[]const u8, status: ?[]const u8) !void { + return updatePetWithForm(client, petId, name, status); + } + pub const findbystatus = struct { + pub fn findpetsbystatus(client: *Client, status: ?[]const u8) !Owned([]const std.json.Value) { + return findPetsByStatus(client, status); + } + pub fn findpetsbystatusResult(client: *Client, status: ?[]const u8) !ApiResult([]const std.json.Value) { + return findPetsByStatusResult(client, status); + } + }; + pub const findbytags = struct { + pub fn findpetsbytags(client: *Client, tags: ?[]const u8) !Owned([]const std.json.Value) { + return findPetsByTags(client, tags); + } + pub fn findpetsbytagsResult(client: *Client, tags: ?[]const u8) !ApiResult([]const std.json.Value) { + return findPetsByTagsResult(client, tags); + } + }; + pub const uploadimage = struct { + pub fn uploadfile(client: *Client, petId: i64, additionalMetadata: ?[]const u8, requestBody: []const u8) !Owned(models.ApiResponse) { + return uploadFile(client, petId, additionalMetadata, requestBody); + } + pub fn uploadfileResult(client: *Client, petId: i64, additionalMetadata: ?[]const u8, requestBody: []const u8) !ApiResult(models.ApiResponse) { + return uploadFileResult(client, petId, additionalMetadata, requestBody); + } + }; + }; + pub const store = struct { + pub const inventory = struct { + pub fn get(client: *Client) !Owned(std.json.Value) { + return getInventory(client); + } + pub fn getResult(client: *Client) !ApiResult(std.json.Value) { + return getInventoryResult(client); + } + }; + pub const order = struct { + pub fn delete(client: *Client, orderId: i64) !void { + return deleteOrder(client, orderId); + } + pub fn get(client: *Client, orderId: i64) !Owned(models.Order) { + return getOrderById(client, orderId); + } + pub fn getResult(client: *Client, orderId: i64) !ApiResult(models.Order) { + return getOrderByIdResult(client, orderId); + } + pub fn placeorder(client: *Client, requestBody: models.Order) !Owned(models.Order) { + return placeOrder(client, requestBody); + } + pub fn placeorderResult(client: *Client, requestBody: models.Order) !ApiResult(models.Order) { + return placeOrderResult(client, requestBody); + } + }; + }; + pub const user = struct { + pub fn create(client: *Client, requestBody: models.User) !void { + return createUser(client, requestBody); + } + pub fn delete(client: *Client, username: []const u8) !void { + return deleteUser(client, username); + } + pub fn get(client: *Client, username: []const u8) !Owned(models.User) { + return getUserByName(client, username); + } + pub fn getResult(client: *Client, username: []const u8) !ApiResult(models.User) { + return getUserByNameResult(client, username); + } + pub fn update(client: *Client, username: []const u8, requestBody: models.User) !void { + return updateUser(client, username, requestBody); + } + pub const createwithlist = struct { + pub fn createuserswithlistinput_(client: *Client, requestBody: []const std.json.Value) !Owned(models.User) { + return createUsersWithListInput(client, requestBody); + } + pub fn createuserswithlistinput_Result(client: *Client, requestBody: []const std.json.Value) !ApiResult(models.User) { + return createUsersWithListInputResult(client, requestBody); + } + }; + pub const login = struct { + pub fn loginuser(client: *Client, username: ?[]const u8, password: ?[]const u8) !Owned([]const u8) { + return loginUser(client, username, password); + } + pub fn loginuserResult(client: *Client, username: ?[]const u8, password: ?[]const u8) !ApiResult([]const u8) { + return loginUserResult(client, username, password); + } + }; + pub const logout = struct { + pub fn logoutuser(client: *Client) !void { + return logoutUser(client); + } + }; + }; +}; + +pub const pet = resources.pet; +pub const store = resources.store; +pub const user = resources.user; + diff --git a/generated/multi/models.zig b/generated/multi/models.zig new file mode 100644 index 0000000..a0346c4 --- /dev/null +++ b/generated/multi/models.zig @@ -0,0 +1,69 @@ +// +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:21 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// + +const std = @import("std"); + +/////////////////////////////////////////// +// Generated Zig structures from OpenAPI +/////////////////////////////////////////// + +pub const Category = struct { + id: ?i64 = null, + name: ?[]const u8 = null, +}; + +pub const Address = struct { + city: ?[]const u8 = null, + zip: ?[]const u8 = null, + state: ?[]const u8 = null, + street: ?[]const u8 = null, +}; + +pub const Tag = struct { + id: ?i64 = null, + name: ?[]const u8 = null, +}; + +pub const Order = struct { + status: ?[]const u8 = null, + petId: ?i64 = null, + complete: ?bool = null, + id: ?i64 = null, + quantity: ?i64 = null, + shipDate: ?[]const u8 = null, +}; + +pub const Customer = struct { + address: ?[]const Address = null, + id: ?i64 = null, + username: ?[]const u8 = null, +}; + +pub const Pet = struct { + status: ?[]const u8 = null, + tags: ?[]const Tag = null, + category: ?Category = null, + id: ?i64 = null, + name: []const u8, + photoUrls: []const []const u8, +}; + +pub const User = struct { + password: ?[]const u8 = null, + userStatus: ?i64 = null, + username: ?[]const u8 = null, + email: ?[]const u8 = null, + firstName: ?[]const u8 = null, + id: ?i64 = null, + lastName: ?[]const u8 = null, + phone: ?[]const u8 = null, +}; + +pub const ApiResponse = struct { + type: ?[]const u8 = null, + message: ?[]const u8 = null, + code: ?i64 = null, +}; + diff --git a/generated/multi/runtime.zig b/generated/multi/runtime.zig new file mode 100644 index 0000000..b4ebc95 --- /dev/null +++ b/generated/multi/runtime.zig @@ -0,0 +1,178 @@ +// +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:21 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// + +const std = @import("std"); +pub fn Owned(comptime T: type) type { + return struct { + allocator: std.mem.Allocator, + body: []u8, + parsed: std.json.Parsed(T), + + pub fn deinit(self: *@This()) void { + self.parsed.deinit(); + self.allocator.free(self.body); + } + + pub fn value(self: *@This()) *T { + return &self.parsed.value; + } + }; +} + +pub const RawResponse = struct { + allocator: std.mem.Allocator, + status: std.http.Status, + body: []u8, + + pub fn deinit(self: *@This()) void { + self.allocator.free(self.body); + } +}; + +pub const ParseErrorResponse = struct { + raw: RawResponse, + error_name: []const u8, +}; + +pub fn ApiResult(comptime T: type) type { + return union(enum) { + ok: Owned(T), + api_error: RawResponse, + parse_error: ParseErrorResponse, + + pub fn deinit(self: *@This()) void { + switch (self.*) { + .ok => |*value| value.deinit(), + .api_error => |*value| value.deinit(), + .parse_error => |*value| value.raw.deinit(), + } + } + }; +} +pub const HttpObserver = struct { + ctx: ?*anyopaque, + onRequest: ?*const fn (ctx: ?*anyopaque, method: std.http.Method, url: []const u8, headers: []const std.http.Header, body: ?[]const u8) void, + onResponse: ?*const fn (ctx: ?*anyopaque, method: std.http.Method, url: []const u8, status: std.http.Status, headers: []const std.http.Header, body: []const u8, duration_ns: u64) void, + onError: ?*const fn (ctx: ?*anyopaque, method: std.http.Method, url: []const u8, err_name: []const u8) void, +}; + +const max_sse_line_size = 8192; +const max_sse_event_size = 65536; + +pub const CancellationToken = struct { + cancelled: std.atomic.Value(bool), + + pub fn init() CancellationToken { + return .{ .cancelled = std.atomic.Value(bool).init(false) }; + } + + pub fn cancel(self: *CancellationToken) void { + self.cancelled.store(true, .seq_cst); + } + + pub fn isCancelled(self: *CancellationToken) bool { + return self.cancelled.load(.seq_cst); + } +}; + +pub fn checkCancellation(token: ?*CancellationToken) !void { + if (token) |t| { + if (t.isCancelled()) return error.Cancelled; + } +} + +pub fn parseSseBytes(allocator: std.mem.Allocator, bytes: []const u8, callback: anytype, cancellation_token: ?*CancellationToken) !void { + var reader: std.Io.Reader = .fixed(bytes); + try parseSseReader(allocator, &reader, callback, cancellation_token); +} + +pub fn parseSseReader(allocator: std.mem.Allocator, reader: *std.Io.Reader, callback: anytype, cancellation_token: ?*CancellationToken) !void { + var line_buf: std.Io.Writer.Allocating = .init(allocator); + defer line_buf.deinit(); + + var event_data: std.Io.Writer.Allocating = .init(allocator); + defer event_data.deinit(); + + while (true) { + try checkCancellation(cancellation_token); + line_buf.clearRetainingCapacity(); + + _ = reader.streamDelimiterLimit(&line_buf.writer, '\n', .limited(max_sse_line_size)) catch |err| switch (err) { + error.StreamTooLong => return error.SseLineTooLong, + error.ReadFailed => return err, + error.WriteFailed => return err, + }; + + const ended_with_delimiter = blk: { + const byte = reader.peekByte() catch |err| switch (err) { + error.EndOfStream => break :blk false, + error.ReadFailed => return err, + }; + if (byte == '\n') { + _ = try reader.takeByte(); + break :blk true; + } + break :blk false; + }; + + if (try processSseLine(&event_data, line_buf.written(), callback)) return; + if (!ended_with_delimiter) break; + } + + _ = try dispatchSseEvent(&event_data, callback); +} + +fn processSseLine(event_data: *std.Io.Writer.Allocating, raw_line: []const u8, callback: anytype) !bool { + const line = std.mem.trimEnd(u8, raw_line, "\r"); + if (line.len == 0) return try dispatchSseEvent(event_data, callback); + if (line[0] == ':') return false; + + const colon = std.mem.indexOfScalar(u8, line, ':') orelse return false; + const field = line[0..colon]; + if (!std.mem.eql(u8, field, "data")) return false; + + var value = line[colon + 1 ..]; + if (value.len > 0 and value[0] == ' ') value = value[1..]; + const separator_len: usize = if (event_data.written().len == 0) 0 else 1; + if (event_data.written().len + separator_len + value.len > max_sse_event_size) return error.SseEventTooLong; + if (separator_len != 0) try event_data.writer.writeByte('\n'); + try event_data.writer.writeAll(value); + return false; +} + +fn dispatchSseEvent(event_data: *std.Io.Writer.Allocating, callback: anytype) !bool { + const data = event_data.written(); + if (data.len == 0) return false; + defer event_data.clearRetainingCapacity(); + + if (std.mem.eql(u8, data, "[DONE]")) return true; + try callback.event(data); + return false; +} + +pub fn TypedSseCallback(comptime T: type, comptime Callback: type) type { + return struct { + allocator: std.mem.Allocator, + callback: *Callback, + + pub fn event(self: *@This(), data: []const u8) !void { + var parsed = try std.json.parseFromSlice(T, self.allocator, data, .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + try self.callback.event(&parsed.value); + } + }; +} + +pub fn parseSseBytesTyped(comptime T: type, allocator: std.mem.Allocator, bytes: []const u8, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; + try parseSseBytes(allocator, bytes, &typed_callback, cancellation_token); +} + +pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reader: *std.Io.Reader, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; + try parseSseReader(allocator, reader, &typed_callback, cancellation_token); +} diff --git a/generated/openai.zig b/generated/openai.zig index b2deb68..ecbbf5e 100644 --- a/generated/openai.zig +++ b/generated/openai.zig @@ -1,6 +1,6 @@ // -// this code was generated by openapi2zig 0.4.0 (2446685) on 2026-07-22 13:15:12 UTC -// changes to this file may cause incorrect behavior and will be lost if the code is regenerated +// This code was generated by openapi2zig 0.4.0 (f1b1b79) on 2026-07-29 10:38:19 UTC +// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated // const std = @import("std"); @@ -16558,6 +16558,118 @@ pub fn getJsonResult(comptime T: type, client: *Client, path: []const u8) !ApiRe pub fn postJsonResult(comptime T: type, client: *Client, path: []const u8, payload: anytype) !ApiResult(T) { return parseRawResponse(T, try postJsonRaw(client, path, payload)); } +fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); + + var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); + defer parsed.deinit(); + + if (parsed.value == .object) { + try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); + } + + var out: std.Io.Writer.Allocating = .init(allocator); + errdefer out.deinit(); + try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); + return try out.toOwnedSlice(); +} + +fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const Callback = @TypeOf(callback.*); + var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; + try streamJson(client, path, requestBody, &typed_callback, cancellation_token); +} + +fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { + const allocator = client.allocator; + const payload = try stringifyStreamRequest(allocator, requestBody); + defer allocator.free(payload); + + var headers = std.ArrayList(std.http.Header).empty; + defer headers.deinit(allocator); + const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); + defer if (auth_header) |value| allocator.free(value); + + const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); + defer allocator.free(url); + + if (client.http_observer) |obs| { + if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); + } + + const uri = try std.Uri.parse(url); + try checkCancellation(cancellation_token); + + const start = std.Io.Clock.awake.now(client.io); + var req = client.http.request(.POST, uri, .{ + .redirect_behavior = .unhandled, + .headers = .{ .accept_encoding = .{ .override = "identity" } }, + .extra_headers = headers.items, + }) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + defer req.deinit(); + + req.transfer_encoding = .{ .content_length = payload.len }; + var request_body = try req.sendBodyUnflushed(&.{}); + try request_body.writer.writeAll(payload); + try request_body.end(); + try req.connection.?.flush(); + try checkCancellation(cancellation_token); + + var response = req.receiveHead(&.{}) catch |err| { + if (client.http_observer) |obs| { + if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); + } + return err; + }; + const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); + if (response.head.status.class() != .success) { + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + return error.ResponseError; + } + + if (client.http_observer) |obs| { + if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); + } + + var transfer_buffer: [8 * 1024]u8 = undefined; + const reader = response.reader(&transfer_buffer); + parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { + error.ReadFailed => return response.bodyErr() orelse err, + else => return err, + }; +} + +fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { + if (content_type) |ct| { + try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); + } + try headers.append(allocator, .{ .name = "Accept", .value = accept }); + + var auth_header: ?[]u8 = null; + if (client.api_key.len > 0) { + auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); + try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); + } + if (client.organization) |organization| { + try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); + } + if (client.project) |project| { + try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); + } + for (client.default_headers) |header| { + try headers.append(allocator, header); + } + return auth_header; +} pub const CancellationToken = struct { cancelled: std.atomic.Value(bool), @@ -16674,120 +16786,6 @@ pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reade var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; try parseSseReader(allocator, reader, &typed_callback, cancellation_token); } - -fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { - var buf: std.Io.Writer.Allocating = .init(allocator); - defer buf.deinit(); - try std.json.Stringify.value(requestBody, .{ .emit_null_optional_fields = false }, &buf.writer); - - var parsed = try std.json.parseFromSlice(std.json.Value, allocator, buf.written(), .{ .ignore_unknown_fields = true }); - defer parsed.deinit(); - - if (parsed.value == .object) { - try parsed.value.object.put(parsed.arena.allocator(), "stream", .{ .bool = true }); - } - - var out: std.Io.Writer.Allocating = .init(allocator); - errdefer out.deinit(); - try std.json.Stringify.value(parsed.value, .{ .emit_null_optional_fields = false }, &out.writer); - return try out.toOwnedSlice(); -} - -fn streamJsonTyped(comptime T: type, client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const Callback = @TypeOf(callback.*); - var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = client.allocator, .callback = callback }; - try streamJson(client, path, requestBody, &typed_callback, cancellation_token); -} - -fn streamJson(client: *Client, path: []const u8, requestBody: anytype, callback: anytype, cancellation_token: ?*CancellationToken) !void { - const allocator = client.allocator; - const payload = try stringifyStreamRequest(allocator, requestBody); - defer allocator.free(payload); - - var headers = std.ArrayList(std.http.Header).empty; - defer headers.deinit(allocator); - const auth_header = try appendClientHeaders(allocator, &headers, client, "application/json", "text/event-stream"); - defer if (auth_header) |value| allocator.free(value); - - const url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ client.base_url, path }); - defer allocator.free(url); - - if (client.http_observer) |obs| { - if (obs.onRequest) |cb| cb(obs.ctx, .POST, url, headers.items, payload); - } - - const uri = try std.Uri.parse(url); - try checkCancellation(cancellation_token); - - const start = std.Io.Clock.awake.now(client.io); - var req = client.http.request(.POST, uri, .{ - .redirect_behavior = .unhandled, - .headers = .{ .accept_encoding = .{ .override = "identity" } }, - .extra_headers = headers.items, - }) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - defer req.deinit(); - - req.transfer_encoding = .{ .content_length = payload.len }; - var request_body = try req.sendBodyUnflushed(&.{}); - try request_body.writer.writeAll(payload); - try request_body.end(); - try req.connection.?.flush(); - try checkCancellation(cancellation_token); - - var response = req.receiveHead(&.{}) catch |err| { - if (client.http_observer) |obs| { - if (obs.onError) |cb| cb(obs.ctx, .POST, url, @errorName(err)); - } - return err; - }; - const elapsed_ns = @as(u64, @intCast(start.untilNow(client.io, .awake).nanoseconds)); - if (response.head.status.class() != .success) { - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - return error.ResponseError; - } - - if (client.http_observer) |obs| { - if (obs.onResponse) |cb| cb(obs.ctx, .POST, url, response.head.status, &.{}, "", elapsed_ns); - } - - var transfer_buffer: [8 * 1024]u8 = undefined; - const reader = response.reader(&transfer_buffer); - parseSseReader(allocator, reader, callback, cancellation_token) catch |err| switch (err) { - error.ReadFailed => return response.bodyErr() orelse err, - else => return err, - }; -} - -fn appendClientHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), client: *Client, content_type: ?[]const u8, accept: []const u8) !?[]u8 { - if (content_type) |ct| { - try headers.append(allocator, .{ .name = "Content-Type", .value = ct }); - } - try headers.append(allocator, .{ .name = "Accept", .value = accept }); - - var auth_header: ?[]u8 = null; - if (client.api_key.len > 0) { - auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{client.api_key}); - try headers.append(allocator, .{ .name = "Authorization", .value = auth_header.? }); - } - if (client.organization) |organization| { - try headers.append(allocator, .{ .name = "OpenAI-Organization", .value = organization }); - } - if (client.project) |project| { - try headers.append(allocator, .{ .name = "OpenAI-Project", .value = project }); - } - for (client.default_headers) |header| { - try headers.append(allocator, header); - } - return auth_header; -} - const max_sse_line_size = 8192; const max_sse_event_size = 65536; diff --git a/src/cli.zig b/src/cli.zig index 266ab3c..ea6832d 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -33,6 +33,7 @@ pub const CliArgs = struct { base_url: ?[]const u8 = null, resource_wrappers: ResourceWrapperMode = .paths, models_only: bool = false, + multiple_files: bool = false, sse_buffer: SseBufferMode = .small, }; @@ -63,6 +64,7 @@ pub fn parse(args: []const [:0]const u8) !ParsedArgs { var base_url: ?[]const u8 = null; var resource_wrappers: ResourceWrapperMode = .paths; var models_only = false; + var multiple_files = false; var sse_buffer: SseBufferMode = .small; var i: usize = 2; @@ -107,6 +109,8 @@ pub fn parse(args: []const [:0]const u8) !ParsedArgs { }; } else if (std.mem.eql(u8, arg, "--models-only")) { models_only = true; + } else if (std.mem.eql(u8, arg, "--multiple-files")) { + multiple_files = true; } else if (std.mem.eql(u8, arg, "--sse-buffer")) { i += 1; if (i >= args.len) { @@ -135,6 +139,7 @@ pub fn parse(args: []const [:0]const u8) !ParsedArgs { .base_url = base_url, .resource_wrappers = resource_wrappers, .models_only = models_only, + .multiple_files = multiple_files, .sse_buffer = sse_buffer, }, }; @@ -165,13 +170,17 @@ fn printUsage() void { \\ \\ Options: \\ -i, --input OpenAPI/Swagger spec (file path or http/https URL) - \\ -o, --output Path to the output file path for the generated Zig code + \\ -o, --output Output file path for the generated Zig code. \\ (default: generated.zig) + \\ When --multiple-files is used, this specifies the + \\ output directory (default: generated/) \\ --base-url Base URL for the API client. \\ (default: server URL from OpenAPI Specification) \\ --resource-wrappers Generate resource wrappers: none, tags, paths, hybrid. \\ (default: paths) \\ --models-only Generate only Zig models, skipping the API client. + \\ --multiple-files Generate separate output files for models, runtime, and API client + \\ into the output directory specified by -o. \\ --sse-buffer SSE parse buffer size: small (8KB line / 64KB event) \\ or large (256KB line / 1MB event). (default: small) \\ @@ -221,3 +230,18 @@ test "parse upgrade" { try std.testing.expect(parsed.upgrade); } + +test "parse generate supports multiple-files flag" { + const argv = [_][:0]const u8{ + "openapi2zig", + "generate", + "-i", + "openapi.json", + "--multiple-files", + }; + + const parsed = try parse(&argv); + + try std.testing.expect(parsed.args.multiple_files); + try std.testing.expectEqualStrings("openapi.json", parsed.args.input_path); +} diff --git a/src/generator.zig b/src/generator.zig index a4bc3d6..6d2bd8e 100644 --- a/src/generator.zig +++ b/src/generator.zig @@ -11,8 +11,10 @@ const OpenApi32Converter = @import("generators/converters/openapi32_converter.zi const SwaggerConverter = @import("generators/converters/swagger_converter.zig").SwaggerConverter; const UnifiedModelGenerator = @import("generators/unified/model_generator.zig").UnifiedModelGenerator; const UnifiedApiGenerator = @import("generators/unified/api_generator.zig").UnifiedApiGenerator; +const RuntimeGenerator = @import("generators/unified/runtime_generator.zig").RuntimeGenerator; const default_output_file: []const u8 = "generated.zig"; +const default_output_dir: []const u8 = "generated"; const Extension = enum { YAML, @@ -106,6 +108,13 @@ fn generateCodeFromJsonContents(allocator: std.mem.Allocator, io: std.Io, json_c } fn generateCodeFromUnifiedDocument(allocator: std.mem.Allocator, io: std.Io, unified_doc: @import("models/common/document.zig").UnifiedDocument, args: cli.CliArgs) !void { + const cwd = std.Io.Dir.cwd(); + + if (args.multiple_files) { + try generateMultipleFiles(allocator, io, cwd, unified_doc, args); + return; + } + var model_generator = UnifiedModelGenerator.init(allocator); defer model_generator.deinit(); const generated_models = try model_generator.generate(unified_doc); @@ -130,7 +139,6 @@ fn generateCodeFromUnifiedDocument(allocator: std.mem.Allocator, io: std.Io, uni 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| { try cwd.createDirPath(io, dir_path); } @@ -140,6 +148,54 @@ fn generateCodeFromUnifiedDocument(allocator: std.mem.Allocator, io: std.Io, uni std.log.info("Code generated successfully and written to '{s}'.", .{output_path}); } +fn writeFile(allocator: std.mem.Allocator, io: std.Io, cwd: std.Io.Dir, dir_path: []const u8, file_name: []const u8, content: []const u8) !void { + const full_path = try std.fs.path.join(allocator, &.{ dir_path, file_name }); + defer allocator.free(full_path); + const output_file = try cwd.createFile(io, full_path, .{}); + defer output_file.close(io); + try output_file.writeStreamingAll(io, content); + std.log.info("Wrote '{s}'", .{full_path}); +} + +fn generateMultipleFiles(allocator: std.mem.Allocator, io: std.Io, cwd: std.Io.Dir, unified_doc: @import("models/common/document.zig").UnifiedDocument, args: cli.CliArgs) !void { + const dir_path = args.output_path orelse default_output_dir; + try cwd.createDirPath(io, dir_path); + + var model_generator = UnifiedModelGenerator.init(allocator); + defer model_generator.deinit(); + const generated_models = try model_generator.generate(unified_doc); + defer allocator.free(generated_models); + + const header = try generated_header.renderNowFromBuildInfo(allocator, io); + defer allocator.free(header); + + const models_content = try std.mem.concat(allocator, u8, &.{ header, generated_models }); + defer allocator.free(models_content); + try writeFile(allocator, io, cwd, dir_path, "models.zig", models_content); + + if (args.models_only) return; + + var runtime_gen = RuntimeGenerator.init(allocator, args.sse_buffer); + defer runtime_gen.deinit(); + const generated_runtime = try runtime_gen.generate(); + defer allocator.free(generated_runtime); + + const runtime_content = try std.mem.concat(allocator, u8, &.{ header, generated_runtime }); + defer allocator.free(runtime_content); + try writeFile(allocator, io, cwd, dir_path, "runtime.zig", runtime_content); + + var api_generator = UnifiedApiGenerator.init(allocator, args); + api_generator.model_prefix = "models."; + api_generator.emit_imports = true; + defer api_generator.deinit(); + const generated_api = try api_generator.generateClientOnly(unified_doc); + defer allocator.free(generated_api); + + const client_content = try std.mem.concat(allocator, u8, &.{ header, generated_api }); + defer allocator.free(client_content); + try writeFile(allocator, io, cwd, dir_path, "client.zig", client_content); +} + fn generateCodeFromDocument(allocator: std.mem.Allocator, io: std.Io, doc: anytype, args: cli.CliArgs, comptime Converter: type) !void { var converter = Converter.init(allocator); var unified_doc = try converter.convert(doc); diff --git a/src/generators/unified/api_generator.zig b/src/generators/unified/api_generator.zig index bb8a5f9..db96c1d 100644 --- a/src/generators/unified/api_generator.zig +++ b/src/generators/unified/api_generator.zig @@ -123,6 +123,8 @@ pub const UnifiedApiGenerator = struct { allocator: std.mem.Allocator, buffer: std.ArrayList(u8), args: cli.CliArgs, + model_prefix: []const u8 = "", + emit_imports: bool = true, pub fn init(allocator: std.mem.Allocator, args: cli.CliArgs) UnifiedApiGenerator { return UnifiedApiGenerator{ @@ -146,6 +148,16 @@ pub const UnifiedApiGenerator = struct { return try self.allocator.dupe(u8, self.buffer.items); } + pub fn generateClientOnly(self: *UnifiedApiGenerator, document: UnifiedDocument) ![]const u8 { + self.buffer.clearRetainingCapacity(); + try self.generateHeaderMulti(); + try self.generateApiClient(document); + if (self.args.resource_wrappers != .none) { + try self.generateResourceWrappers(document); + } + return try self.allocator.dupe(u8, self.buffer.items); + } + fn appendIdentifier(self: *UnifiedApiGenerator, name: []const u8) !void { try ident.appendIdentifier(&self.buffer, self.allocator, name); } @@ -163,6 +175,29 @@ pub const UnifiedApiGenerator = struct { try self.buffer.appendSlice(self.allocator, "///////////////////////////////////////////\n"); try self.buffer.appendSlice(self.allocator, "// Generated Zig API client from OpenAPI\n"); try self.buffer.appendSlice(self.allocator, "///////////////////////////////////////////\n\n"); + try self.generateRuntimePreamble(); + try self.generateClientPreamble(); + try self.generateSsePreamble(); + try self.generateSseBufferConstants(); + } + + fn generateHeaderMulti(self: *UnifiedApiGenerator) !void { + try self.buffer.appendSlice(self.allocator, "///////////////////////////////////////////\n"); + try self.buffer.appendSlice(self.allocator, "// Generated Zig API client from OpenAPI\n"); + try self.buffer.appendSlice(self.allocator, "///////////////////////////////////////////\n\n"); + if (self.emit_imports) { + try self.buffer.appendSlice(self.allocator, + \\const std = @import("std"); + \\const models = @import("models.zig"); + \\const runtime = @import("runtime.zig"); + \\ + ); + try self.generateRuntimeReexports(); + } + try self.generateClientPreamble(); + } + + fn generateRuntimePreamble(self: *UnifiedApiGenerator) !void { try self.buffer.appendSlice(self.allocator, \\ \\pub fn Owned(comptime T: type) type { @@ -215,6 +250,27 @@ pub const UnifiedApiGenerator = struct { \\ ); try self.generateHttpObserverType(); + } + + fn generateRuntimeReexports(self: *UnifiedApiGenerator) !void { + try self.buffer.appendSlice(self.allocator, + \\const Owned = runtime.Owned; + \\const HttpObserver = runtime.HttpObserver; + \\const RawResponse = runtime.RawResponse; + \\const ParseErrorResponse = runtime.ParseErrorResponse; + \\const ApiResult = runtime.ApiResult; + \\const CancellationToken = runtime.CancellationToken; + \\const checkCancellation = runtime.checkCancellation; + \\const parseSseReader = runtime.parseSseReader; + \\const parseSseBytes = runtime.parseSseBytes; + \\const parseSseBytesTyped = runtime.parseSseBytesTyped; + \\const parseSseReaderTyped = runtime.parseSseReaderTyped; + \\const TypedSseCallback = runtime.TypedSseCallback; + \\ + ); + } + + fn generateClientPreamble(self: *UnifiedApiGenerator) !void { try self.buffer.appendSlice(self.allocator, \\ \\pub const Client = struct { @@ -371,6 +427,12 @@ pub const UnifiedApiGenerator = struct { \\ return parseRawResponse(T, try postJsonRaw(client, path, payload)); \\} \\ + ); + try self.generateStreamAuthCode(); + } + + fn generateSsePreamble(self: *UnifiedApiGenerator) !void { + try self.buffer.appendSlice(self.allocator, \\pub const CancellationToken = struct { \\ cancelled: std.atomic.Value(bool), \\ @@ -487,6 +549,11 @@ pub const UnifiedApiGenerator = struct { \\ try parseSseReader(allocator, reader, &typed_callback, cancellation_token); \\} \\ + ); + } + + fn generateStreamAuthCode(self: *UnifiedApiGenerator) !void { + try self.buffer.appendSlice(self.allocator, \\fn stringifyStreamRequest(allocator: std.mem.Allocator, requestBody: anytype) ![]u8 { \\ var buf: std.Io.Writer.Allocating = .init(allocator); \\ defer buf.deinit(); @@ -602,7 +669,6 @@ pub const UnifiedApiGenerator = struct { \\ \\ ); - try self.generateSseBufferConstants(); } fn generateSseBufferConstants(self: *UnifiedApiGenerator) !void { @@ -1848,6 +1914,7 @@ pub const UnifiedApiGenerator = struct { if (schema.ref) |ref| { if (std.mem.lastIndexOf(u8, ref, "/")) |last_slash| { + try self.buffer.appendSlice(self.allocator, self.model_prefix); try self.appendIdentifier(ref[last_slash + 1 ..]); return; } diff --git a/src/generators/unified/runtime_generator.zig b/src/generators/unified/runtime_generator.zig new file mode 100644 index 0000000..97c3ce0 --- /dev/null +++ b/src/generators/unified/runtime_generator.zig @@ -0,0 +1,231 @@ +const std = @import("std"); +const cli = @import("../../cli.zig"); + +pub const RuntimeGenerator = struct { + allocator: std.mem.Allocator, + buffer: std.ArrayList(u8), + sse_buffer: cli.SseBufferMode, + + pub fn init(allocator: std.mem.Allocator, sse_buffer: cli.SseBufferMode) RuntimeGenerator { + return RuntimeGenerator{ + .allocator = allocator, + .buffer = std.ArrayList(u8).empty, + .sse_buffer = sse_buffer, + }; + } + + pub fn deinit(self: *RuntimeGenerator) void { + self.buffer.deinit(self.allocator); + } + + pub fn generate(self: *RuntimeGenerator) ![]const u8 { + self.buffer.clearRetainingCapacity(); + try self.buffer.appendSlice(self.allocator, + \\const std = @import("std"); + \\ + ); + try self.generateRuntimeTypes(); + try self.generateSseBufferConstants(); + try self.generateSseFunctions(); + return try self.allocator.dupe(u8, self.buffer.items); + } + + fn generateRuntimeTypes(self: *RuntimeGenerator) !void { + try self.buffer.appendSlice(self.allocator, + \\pub fn Owned(comptime T: type) type { + \\ return struct { + \\ allocator: std.mem.Allocator, + \\ body: []u8, + \\ parsed: std.json.Parsed(T), + \\ + \\ pub fn deinit(self: *@This()) void { + \\ self.parsed.deinit(); + \\ self.allocator.free(self.body); + \\ } + \\ + \\ pub fn value(self: *@This()) *T { + \\ return &self.parsed.value; + \\ } + \\ }; + \\} + \\ + \\pub const RawResponse = struct { + \\ allocator: std.mem.Allocator, + \\ status: std.http.Status, + \\ body: []u8, + \\ + \\ pub fn deinit(self: *@This()) void { + \\ self.allocator.free(self.body); + \\ } + \\}; + \\ + \\pub const ParseErrorResponse = struct { + \\ raw: RawResponse, + \\ error_name: []const u8, + \\}; + \\ + \\pub fn ApiResult(comptime T: type) type { + \\ return union(enum) { + \\ ok: Owned(T), + \\ api_error: RawResponse, + \\ parse_error: ParseErrorResponse, + \\ + \\ pub fn deinit(self: *@This()) void { + \\ switch (self.*) { + \\ .ok => |*value| value.deinit(), + \\ .api_error => |*value| value.deinit(), + \\ .parse_error => |*value| value.raw.deinit(), + \\ } + \\ } + \\ }; + \\} + \\ + ); + try self.generateHttpObserverType(); + } + + fn generateHttpObserverType(self: *RuntimeGenerator) !void { + try self.buffer.appendSlice(self.allocator, + \\pub const HttpObserver = struct { + \\ ctx: ?*anyopaque, + \\ onRequest: ?*const fn (ctx: ?*anyopaque, method: std.http.Method, url: []const u8, headers: []const std.http.Header, body: ?[]const u8) void, + \\ onResponse: ?*const fn (ctx: ?*anyopaque, method: std.http.Method, url: []const u8, status: std.http.Status, headers: []const std.http.Header, body: []const u8, duration_ns: u64) void, + \\ onError: ?*const fn (ctx: ?*anyopaque, method: std.http.Method, url: []const u8, err_name: []const u8) void, + \\}; + \\ + ); + } + + fn generateSseBufferConstants(self: *RuntimeGenerator) !void { + try self.buffer.appendSlice(self.allocator, "\nconst max_sse_line_size = "); + const line_size = try std.fmt.allocPrint(self.allocator, "{d}", .{self.sse_buffer.maxLineSize()}); + defer self.allocator.free(line_size); + try self.buffer.appendSlice(self.allocator, line_size); + try self.buffer.appendSlice(self.allocator, ";\nconst max_sse_event_size = "); + const event_size = try std.fmt.allocPrint(self.allocator, "{d}", .{self.sse_buffer.maxEventSize()}); + defer self.allocator.free(event_size); + try self.buffer.appendSlice(self.allocator, event_size); + try self.buffer.appendSlice(self.allocator, ";\n\n"); + } + + fn generateSseFunctions(self: *RuntimeGenerator) !void { + try self.buffer.appendSlice(self.allocator, + \\pub const CancellationToken = struct { + \\ cancelled: std.atomic.Value(bool), + \\ + \\ pub fn init() CancellationToken { + \\ return .{ .cancelled = std.atomic.Value(bool).init(false) }; + \\ } + \\ + \\ pub fn cancel(self: *CancellationToken) void { + \\ self.cancelled.store(true, .seq_cst); + \\ } + \\ + \\ pub fn isCancelled(self: *CancellationToken) bool { + \\ return self.cancelled.load(.seq_cst); + \\ } + \\}; + \\ + \\pub fn checkCancellation(token: ?*CancellationToken) !void { + \\ if (token) |t| { + \\ if (t.isCancelled()) return error.Cancelled; + \\ } + \\} + \\ + \\pub fn parseSseBytes(allocator: std.mem.Allocator, bytes: []const u8, callback: anytype, cancellation_token: ?*CancellationToken) !void { + \\ var reader: std.Io.Reader = .fixed(bytes); + \\ try parseSseReader(allocator, &reader, callback, cancellation_token); + \\} + \\ + \\pub fn parseSseReader(allocator: std.mem.Allocator, reader: *std.Io.Reader, callback: anytype, cancellation_token: ?*CancellationToken) !void { + \\ var line_buf: std.Io.Writer.Allocating = .init(allocator); + \\ defer line_buf.deinit(); + \\ + \\ var event_data: std.Io.Writer.Allocating = .init(allocator); + \\ defer event_data.deinit(); + \\ + \\ while (true) { + \\ try checkCancellation(cancellation_token); + \\ line_buf.clearRetainingCapacity(); + \\ + \\ _ = reader.streamDelimiterLimit(&line_buf.writer, '\n', .limited(max_sse_line_size)) catch |err| switch (err) { + \\ error.StreamTooLong => return error.SseLineTooLong, + \\ error.ReadFailed => return err, + \\ error.WriteFailed => return err, + \\ }; + \\ + \\ const ended_with_delimiter = blk: { + \\ const byte = reader.peekByte() catch |err| switch (err) { + \\ error.EndOfStream => break :blk false, + \\ error.ReadFailed => return err, + \\ }; + \\ if (byte == '\n') { + \\ _ = try reader.takeByte(); + \\ break :blk true; + \\ } + \\ break :blk false; + \\ }; + \\ + \\ if (try processSseLine(&event_data, line_buf.written(), callback)) return; + \\ if (!ended_with_delimiter) break; + \\ } + \\ + \\ _ = try dispatchSseEvent(&event_data, callback); + \\} + \\ + \\fn processSseLine(event_data: *std.Io.Writer.Allocating, raw_line: []const u8, callback: anytype) !bool { + \\ const line = std.mem.trimEnd(u8, raw_line, "\r"); + \\ if (line.len == 0) return try dispatchSseEvent(event_data, callback); + \\ if (line[0] == ':') return false; + \\ + \\ const colon = std.mem.indexOfScalar(u8, line, ':') orelse return false; + \\ const field = line[0..colon]; + \\ if (!std.mem.eql(u8, field, "data")) return false; + \\ + \\ var value = line[colon + 1 ..]; + \\ if (value.len > 0 and value[0] == ' ') value = value[1..]; + \\ const separator_len: usize = if (event_data.written().len == 0) 0 else 1; + \\ if (event_data.written().len + separator_len + value.len > max_sse_event_size) return error.SseEventTooLong; + \\ if (separator_len != 0) try event_data.writer.writeByte('\n'); + \\ try event_data.writer.writeAll(value); + \\ return false; + \\} + \\ + \\fn dispatchSseEvent(event_data: *std.Io.Writer.Allocating, callback: anytype) !bool { + \\ const data = event_data.written(); + \\ if (data.len == 0) return false; + \\ defer event_data.clearRetainingCapacity(); + \\ + \\ if (std.mem.eql(u8, data, "[DONE]")) return true; + \\ try callback.event(data); + \\ return false; + \\} + \\ + \\pub fn TypedSseCallback(comptime T: type, comptime Callback: type) type { + \\ return struct { + \\ allocator: std.mem.Allocator, + \\ callback: *Callback, + \\ + \\ pub fn event(self: *@This(), data: []const u8) !void { + \\ var parsed = try std.json.parseFromSlice(T, self.allocator, data, .{ .ignore_unknown_fields = true }); + \\ defer parsed.deinit(); + \\ try self.callback.event(&parsed.value); + \\ } + \\ }; + \\} + \\ + \\pub fn parseSseBytesTyped(comptime T: type, allocator: std.mem.Allocator, bytes: []const u8, callback: anytype, cancellation_token: ?*CancellationToken) !void { + \\ const Callback = @TypeOf(callback.*); + \\ var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; + \\ try parseSseBytes(allocator, bytes, &typed_callback, cancellation_token); + \\} + \\ + \\pub fn parseSseReaderTyped(comptime T: type, allocator: std.mem.Allocator, reader: *std.Io.Reader, callback: anytype, cancellation_token: ?*CancellationToken) !void { + \\ const Callback = @TypeOf(callback.*); + \\ var typed_callback: TypedSseCallback(T, Callback) = .{ .allocator = allocator, .callback = callback }; + \\ try parseSseReader(allocator, reader, &typed_callback, cancellation_token); + \\} + \\ + ); + } +}; diff --git a/src/lib.zig b/src/lib.zig index 08cda83..7a6a226 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -69,6 +69,7 @@ pub const OpenApi32Converter = @import("generators/converters/openapi32_converte // Code generators pub const UnifiedModelGenerator = @import("generators/unified/model_generator.zig").UnifiedModelGenerator; pub const UnifiedApiGenerator = @import("generators/unified/api_generator.zig").UnifiedApiGenerator; +pub const RuntimeGenerator = @import("generators/unified/runtime_generator.zig").RuntimeGenerator; // CLI argument types for code generation pub const CliArgs = @import("cli.zig").CliArgs; @@ -237,6 +238,61 @@ pub fn generateCode(allocator: std.mem.Allocator, io: std.Io, unified_doc: Unifi return try std.mem.concat(allocator, u8, &.{ header, models_code, "\n", api_code }); } +/// Result of generating code in multiple-files mode. +pub const GeneratedFiles = struct { + models: []const u8, + runtime: ?[]const u8 = null, + client: ?[]const u8 = null, + + pub fn deinit(self: *GeneratedFiles, allocator: std.mem.Allocator) void { + allocator.free(self.models); + if (self.runtime) |r| allocator.free(r); + if (self.client) |c| allocator.free(c); + } +}; + +/// Generate separate Zig source files (models, runtime, client) from a unified document. +/// Only the models field is always present; runtime and client are null when +/// args.models_only is true. +pub fn generateCodeMultiple(allocator: std.mem.Allocator, io: std.Io, unified_doc: UnifiedDocument, args: CliArgs) !GeneratedFiles { + const models_code = try generateModels(allocator, unified_doc); + defer allocator.free(models_code); + + const header = try generated_header.renderNowFromBuildInfo(allocator, io); + defer allocator.free(header); + + const models_with_header = try std.mem.concat(allocator, u8, &.{ header, models_code }); + errdefer allocator.free(models_with_header); + + if (args.models_only) { + return .{ .models = models_with_header }; + } + + var runtime_gen = RuntimeGenerator.init(allocator, args.sse_buffer); + defer runtime_gen.deinit(); + const runtime_code = try runtime_gen.generate(); + defer allocator.free(runtime_code); + + const runtime_with_header = try std.mem.concat(allocator, u8, &.{ header, runtime_code }); + errdefer allocator.free(runtime_with_header); + + var api_gen = UnifiedApiGenerator.init(allocator, args); + api_gen.model_prefix = "models."; + api_gen.emit_imports = true; + defer api_gen.deinit(); + const api_code = try api_gen.generateClientOnly(unified_doc); + defer allocator.free(api_code); + + const client_with_header = try std.mem.concat(allocator, u8, &.{ header, api_code }); + errdefer allocator.free(client_with_header); + + return .{ + .models = models_with_header, + .runtime = runtime_with_header, + .client = client_with_header, + }; +} + fn convertDocument(allocator: std.mem.Allocator, doc: anytype, comptime Converter: type) !UnifiedDocument { var converter = Converter.init(allocator); return try converter.convert(doc);