const std = @import("std"); /// Minimal JSON emitter for StyleProfile output. /// We only need to emit, not parse (baselines are compiled in). pub const JsonWriter = struct { buf: []u8, pos: usize, pub fn init(buf: []u8) JsonWriter { return .{ .buf = buf, .pos = 0 }; } pub fn written(self: *const JsonWriter) []const u8 { return self.buf[0..self.pos]; } pub fn put(self: *JsonWriter, bytes: []const u8) void { const end = @min(self.pos + bytes.len, self.buf.len); const n = end - self.pos; @memcpy(self.buf[self.pos..end], bytes[0..n]); self.pos = end; } pub fn putByte(self: *JsonWriter, b: u8) void { if (self.pos < self.buf.len) { self.buf[self.pos] = b; self.pos += 1; } } pub fn objectBegin(self: *JsonWriter) void { self.putByte('{'); } pub fn objectEnd(self: *JsonWriter) void { // remove trailing comma if present if (self.pos > 0 and self.buf[self.pos - 1] == ',') self.pos -= 1; self.putByte('}'); } pub fn arrayBegin(self: *JsonWriter) void { self.putByte('['); } pub fn arrayEnd(self: *JsonWriter) void { if (self.pos > 0 and self.buf[self.pos - 1] == ',') self.pos -= 1; self.putByte(']'); } pub fn key(self: *JsonWriter, k: []const u8) void { self.putByte('"'); self.put(k); self.putByte('"'); self.putByte(':'); } pub fn stringValue(self: *JsonWriter, s: []const u8) void { self.putByte('"'); // Escape special chars for (s) |c| { switch (c) { '"' => { self.putByte('\\'); self.putByte('"'); }, '\\' => { self.putByte('\\'); self.putByte('\\'); }, '\n' => { self.putByte('\\'); self.putByte('n'); }, '\r' => { self.putByte('\\'); self.putByte('r'); }, '\t' => { self.putByte('\\'); self.putByte('t'); }, else => self.putByte(c), } } self.putByte('"'); self.putByte(','); } pub fn floatValue(self: *JsonWriter, v: f32) void { var tmp: [32]u8 = undefined; const s = std.fmt.bufPrint(&tmp, "{d:.4}", .{v}) catch "0"; self.put(s); self.putByte(','); } pub fn boolValue(self: *JsonWriter, v: bool) void { if (v) self.put("true") else self.put("false"); self.putByte(','); } pub fn intValue(self: *JsonWriter, v: u32) void { var tmp: [16]u8 = undefined; const s = std.fmt.bufPrint(&tmp, "{d}", .{v}) catch "0"; self.put(s); self.putByte(','); } }; test "json writer basic" { var buf: [256]u8 = undefined; var jw = JsonWriter.init(&buf); jw.objectBegin(); jw.key("name"); jw.stringValue("test"); jw.key("value"); jw.floatValue(3.14); jw.key("ok"); jw.boolValue(true); jw.objectEnd(); const out = jw.written(); try std.testing.expectEqualStrings("{\"name\":\"test\",\"value\":3.1400,\"ok\":true}", out); }