lib/closure/src/receipt/json.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const pretty_json = @import("pretty").json;
 3 
 4 pub const Error = error{ReceiptCapacityExceeded};
 5 
 6 const measurement_buffer_bytes: usize = 256;
 7 
 8 pub fn measure(
 9     comptime write: anytype,
10     args: anytype,
11 ) Error!usize {
12     var buffer: [measurement_buffer_bytes]u8 = undefined;
13     var discarding = std.Io.Writer.Discarding.init(&buffer);
14     @call(.auto, write, .{&discarding.writer} ++ args) catch
15         return error.ReceiptCapacityExceeded;
16     return std.math.cast(usize, discarding.fullCount()) orelse
17         error.ReceiptCapacityExceeded;
18 }
19 
20 pub fn render(
21     output: []u8,
22     expected: usize,
23     comptime write: anytype,
24     args: anytype,
25 ) Error![]const u8 {
26     if (expected > output.len) return error.ReceiptCapacityExceeded;
27     var writer = std.Io.Writer.fixed(output);
28     @call(.auto, write, .{&writer} ++ args) catch
29         return error.ReceiptCapacityExceeded;
30     if (writer.end != expected) return error.ReceiptCapacityExceeded;
31     return output[0..writer.end];
32 }
33 
34 fn writeFixture(writer: *std.Io.Writer, value: []const u8) std.Io.Writer.Error!void {
35     var json = pretty_json.Writer.init(writer, .minified);
36     const object = try json.object();
37     try object.byteString("value", value);
38     try object.endLine();
39 }
40 
41 test "bounded JSON measures and renders arbitrary byte strings exactly" {
42     const expected = "{\"value\":\"quote\\\"\\n\\u0080\\u00ff\"}\n";
43     const length = try measure(writeFixture, .{"quote\"\n\x80\xff"});
44     try std.testing.expectEqual(expected.len, length);
45     var output: [expected.len]u8 = undefined;
46     const rendered = try render(
47         &output,
48         length,
49         writeFixture,
50         .{"quote\"\n\x80\xff"},
51     );
52     try std.testing.expectEqualStrings(expected, rendered);
53 }
54 
55 test "bounded JSON rejects short output without mutation" {
56     const length = try measure(writeFixture, .{"value"});
57     var output: [1]u8 = .{0xa5};
58     try std.testing.expectError(
59         error.ReceiptCapacityExceeded,
60         render(&output, length, writeFixture, .{"value"}),
61     );
62     try std.testing.expectEqual(@as(u8, 0xa5), output[0]);
63 }