lib/pretty/core/src/json/format.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const Style = enum {
4 minified,
5 indent_2,
6 };
7
8 pub fn stringifyOptions(style: Style) std.json.Stringify.Options {
9 return switch (style) {
10 .minified => .{},
11 .indent_2 => .{ .whitespace = .indent_2 },
12 };
13 }
14
15 pub fn writeMinified(writer: anytype, value: anytype) !void {
16 try std.json.Stringify.value(value, stringifyOptions(.minified), writer);
17 }
18
19 pub fn writeMinifiedLine(writer: anytype, value: anytype) !void {
20 try writeMinified(writer, value);
21 try writer.writeByte('\n');
22 }
23
24 pub fn renderMinifiedAlloc(
25 allocator: std.mem.Allocator,
26 value: anytype,
27 ) std.mem.Allocator.Error![]u8 {
28 var out: std.Io.Writer.Allocating = .init(allocator);
29 errdefer out.deinit();
30 writeMinified(&out.writer, value) catch return error.OutOfMemory;
31 return try out.toOwnedSlice();
32 }
33
34 pub fn renderMinifiedLineAlloc(
35 allocator: std.mem.Allocator,
36 value: anytype,
37 ) std.mem.Allocator.Error![]u8 {
38 var out: std.Io.Writer.Allocating = .init(allocator);
39 errdefer out.deinit();
40 writeMinifiedLine(&out.writer, value) catch return error.OutOfMemory;
41 return try out.toOwnedSlice();
42 }
43
44 pub fn writeIndented(writer: anytype, value: anytype) !void {
45 try std.json.Stringify.value(value, stringifyOptions(.indent_2), writer);
46 }
47
48 pub fn renderIndentedAlloc(allocator: std.mem.Allocator, value: anytype) ![]u8 {
49 var out: std.Io.Writer.Allocating = .init(allocator);
50 errdefer out.deinit();
51 try writeIndented(&out.writer, value);
52 return try out.toOwnedSlice();
53 }
54
55 test "writeMinified serializes byte-stable machine JSON" {
56 const allocator = std.testing.allocator;
57 var parsed = try std.json.parseFromSlice(std.json.Value, allocator, "{\"name\":\"alpha\",\"items\":[1,true,null]}", .{});
58 defer parsed.deinit();
59
60 var out = std.Io.Writer.Allocating.init(allocator);
61 defer out.deinit();
62 try writeMinified(&out.writer, parsed.value);
63 try std.testing.expectEqualStrings("{\"name\":\"alpha\",\"items\":[1,true,null]}", out.written());
64
65 const rendered = try renderMinifiedAlloc(allocator, .{ .status = "ok", .count = @as(u8, 2) });
66 defer allocator.free(rendered);
67 try std.testing.expectEqualStrings("{\"status\":\"ok\",\"count\":2}", rendered);
68 }
69
70 test "writeMinifiedLine serializes independent escaped JSONL records" {
71 var out = std.Io.Writer.Allocating.init(std.testing.allocator);
72 defer out.deinit();
73
74 try writeMinifiedLine(&out.writer, .{ .event = "first\"\n", .count = @as(u8, 1) });
75 try writeMinifiedLine(&out.writer, .{ .event = "second", .count = @as(u8, 2) });
76
77 try std.testing.expectEqualStrings(
78 "{\"event\":\"first\\\"\\n\",\"count\":1}\n" ++
79 "{\"event\":\"second\",\"count\":2}\n",
80 out.written(),
81 );
82
83 const rendered = try renderMinifiedLineAlloc(
84 std.testing.allocator,
85 .{ .event = "owned", .count = @as(u8, 3) },
86 );
87 defer std.testing.allocator.free(rendered);
88 try std.testing.expectEqualStrings(
89 "{\"event\":\"owned\",\"count\":3}\n",
90 rendered,
91 );
92 }
93
94 test "allocated renders expose allocator failures" {
95 var failing = std.testing.FailingAllocator.init(
96 std.testing.allocator,
97 .{ .fail_index = 0 },
98 );
99 try std.testing.expectError(
100 error.OutOfMemory,
101 renderMinifiedAlloc(failing.allocator(), .{ .status = "ok" }),
102 );
103 try std.testing.expectError(
104 error.OutOfMemory,
105 renderMinifiedLineAlloc(failing.allocator(), .{ .status = "ok" }),
106 );
107 }
108
109 test "writeIndented serializes two-space machine JSON" {
110 const allocator = std.testing.allocator;
111 const rendered = try renderIndentedAlloc(allocator, .{ .status = "ok", .count = @as(u8, 2) });
112 defer allocator.free(rendered);
113 try std.testing.expectEqualStrings(
114 "{\n \"status\": \"ok\",\n \"count\": 2\n}",
115 rendered,
116 );
117 }