Something went wrong. Try again.
My version of https://github.com/vercel-labs/fx
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126const std = @import("std");const types = @import("../shared/types.zig");
const Allocator = std.mem.Allocator;
const EncodeError = Allocator.Error || std.Io.Writer.Error || error{ AnswerCountMismatch,};
/// Encodes ordered question entries and answers as an owned JSON result./// The caller owns the returned slice and must free it with `alloc`.pub fn encodeJson( alloc: Allocator, entries: []const types.QuestionBatchEntry, answers: []const []const u8,) EncodeError![]u8 { if (entries.len != answers.len) return error.AnswerCountMismatch;
var out: std.Io.Writer.Allocating = .init(alloc); defer out.deinit();
try out.writer.writeAll("["); for (entries, 0..) |entry, index| { if (index > 0) try out.writer.writeAll(","); try out.writer.writeAll("{\"question\":"); try std.json.Stringify.value(entry.question, .{}, &out.writer); try out.writer.writeAll(",\"answer\":"); try std.json.Stringify.value(answers[index], .{}, &out.writer); try out.writer.writeAll("}"); } try out.writer.writeAll("]");
return out.toOwnedSlice();}
/// Decodes a successful question result. Returned strings borrow from `output`/// or allocations in `arena`; both must outlive the returned answers.pub fn decodeJson( arena: Allocator, output: []const u8,) Allocator.Error!?[]const types.QuestionAnswer { const root = std.json.parseFromSliceLeaky( std.json.Value, arena, output, .{}, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => return null, };
if (root != .array) return null; const items = root.array.items; if (items.len < 1 or items.len > 4) return null;
const answers = try arena.alloc(types.QuestionAnswer, items.len); for (items, 0..) |item, index| { if (item != .object) return null; const question = item.object.get("question") orelse return null; const answer = item.object.get("answer") orelse return null; if (question != .string or answer != .string) return null; answers[index] = .{ .question = question.string, .answer = answer.string, }; }
return answers;}
test "question answers preserve order and JSON escaping" { const entries = [_]types.QuestionBatchEntry{ .{ .question = "Which depth?", .options = &.{} }, .{ .question = "Ship it?", .options = &.{} }, }; const answers = [_][]const u8{ "Thorough", "Yes\nnow" };
const encoded = try encodeJson(std.testing.allocator, &entries, &answers); defer std.testing.allocator.free(encoded);
try std.testing.expectEqualStrings( "[{\"question\":\"Which depth?\",\"answer\":\"Thorough\"},{\"question\":\"Ship it?\",\"answer\":\"Yes\\nnow\"}]", encoded, );
var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const decoded = (try decodeJson(arena_state.allocator(), encoded)).?; try std.testing.expectEqual(@as(usize, 2), decoded.len); try std.testing.expectEqualStrings("Which depth?", decoded[0].question); try std.testing.expectEqualStrings("Thorough", decoded[0].answer); try std.testing.expectEqualStrings("Ship it?", decoded[1].question); try std.testing.expectEqualStrings("Yes\nnow", decoded[1].answer);}
test "question answer decoding rejects non-result payloads" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const arena = arena_state.allocator();
try std.testing.expect((try decodeJson(arena, "(user cancelled the question)")) == null); try std.testing.expect((try decodeJson(arena, "[]")) == null); try std.testing.expect((try decodeJson(arena, "[{\"question\":\"Q\"}]")) == null); try std.testing.expect((try decodeJson(arena, "[{\"question\":\"Q\",\"answer\":1}]")) == null); try std.testing.expect((try decodeJson(arena, "[{\"question\":\"Q\",\"answer\":\"A\"}] trailing")) == null);}
test "question answer codec exposes structural and allocation failures" { const entries = [_]types.QuestionBatchEntry{ .{ .question = "Continue?", .options = &.{} }, }; try std.testing.expectError( error.AnswerCountMismatch, encodeJson(std.testing.allocator, &entries, &.{}), );
var failing = std.testing.FailingAllocator.init( std.testing.allocator, .{ .fail_index = 0 }, ); try std.testing.expectError( error.OutOfMemory, decodeJson(failing.allocator(), "[{\"question\":\"Q\",\"answer\":\"A\"}]"), );}