| ... | @@ -24,8 +24,10 @@ const ArrayList = std.ArrayList; | ... | @@ -24,8 +24,10 @@ const ArrayList = std.ArrayList; |
| 24 | const BitStack = std.BitStack; | 24 | const BitStack = std.BitStack; |
| 25 | const Stringify = @This(); | 25 | const Stringify = @This(); |
| 26 | | 26 | |
| 27 | const OBJECT_MODE = 0; | 27 | const IndentationMode = enum(u1) { |
| 28 | const ARRAY_MODE = 1; | 28 | object = 0, |
| | 29 | array = 1, |
| | 30 | }; |
| 29 | | 31 | |
| 30 | writer: *std.io.BufferedWriter, | 32 | writer: *std.io.BufferedWriter, |
| 31 | options: Options = .{}, | 33 | options: Options = .{}, |
| ... | @@ -78,22 +80,22 @@ else | ... | @@ -78,22 +80,22 @@ else |
| 78 | pub fn beginArray(self: *Stringify) anyerror!void { | 80 | pub fn beginArray(self: *Stringify) anyerror!void { |
| 79 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); | 81 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 80 | try self.valueStart(); | 82 | try self.valueStart(); |
| 81 | try self.stream.writeByte('['); | 83 | try self.writer.writeByte('['); |
| 82 | try self.pushIndentation(ARRAY_MODE); | 84 | try self.pushIndentation(.array); |
| 83 | self.next_punctuation = .none; | 85 | self.next_punctuation = .none; |
| 84 | } | 86 | } |
| 85 | | 87 | |
| 86 | pub fn beginObject(self: *Stringify) anyerror!void { | 88 | pub fn beginObject(self: *Stringify) anyerror!void { |
| 87 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); | 89 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 88 | try self.valueStart(); | 90 | try self.valueStart(); |
| 89 | try self.stream.writeByte('{'); | 91 | try self.writer.writeByte('{'); |
| 90 | try self.pushIndentation(OBJECT_MODE); | 92 | try self.pushIndentation(.object); |
| 91 | self.next_punctuation = .none; | 93 | self.next_punctuation = .none; |
| 92 | } | 94 | } |
| 93 | | 95 | |
| 94 | pub fn endArray(self: *Stringify) anyerror!void { | 96 | pub fn endArray(self: *Stringify) anyerror!void { |
| 95 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); | 97 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 96 | self.popIndentation(ARRAY_MODE); | 98 | self.popIndentation(.array); |
| 97 | switch (self.next_punctuation) { | 99 | switch (self.next_punctuation) { |
| 98 | .none => {}, | 100 | .none => {}, |
| 99 | .comma => { | 101 | .comma => { |
| ... | @@ -101,13 +103,13 @@ pub fn endArray(self: *Stringify) anyerror!void { | ... | @@ -101,13 +103,13 @@ pub fn endArray(self: *Stringify) anyerror!void { |
| 101 | }, | 103 | }, |
| 102 | .the_beginning, .colon => unreachable, | 104 | .the_beginning, .colon => unreachable, |
| 103 | } | 105 | } |
| 104 | try self.stream.writeByte(']'); | 106 | try self.writer.writeByte(']'); |
| 105 | self.valueDone(); | 107 | self.valueDone(); |
| 106 | } | 108 | } |
| 107 | | 109 | |
| 108 | pub fn endObject(self: *Stringify) anyerror!void { | 110 | pub fn endObject(self: *Stringify) anyerror!void { |
| 109 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); | 111 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 110 | self.popIndentation(OBJECT_MODE); | 112 | self.popIndentation(.object); |
| 111 | switch (self.next_punctuation) { | 113 | switch (self.next_punctuation) { |
| 112 | .none => {}, | 114 | .none => {}, |
| 113 | .comma => { | 115 | .comma => { |
| ... | @@ -115,24 +117,24 @@ pub fn endObject(self: *Stringify) anyerror!void { | ... | @@ -115,24 +117,24 @@ pub fn endObject(self: *Stringify) anyerror!void { |
| 115 | }, | 117 | }, |
| 116 | .the_beginning, .colon => unreachable, | 118 | .the_beginning, .colon => unreachable, |
| 117 | } | 119 | } |
| 118 | try self.stream.writeByte('}'); | 120 | try self.writer.writeByte('}'); |
| 119 | self.valueDone(); | 121 | self.valueDone(); |
| 120 | } | 122 | } |
| 121 | | 123 | |
| 122 | fn pushIndentation(self: *Stringify, mode: u1) !void { | 124 | fn pushIndentation(self: *Stringify, mode: IndentationMode) !void { |
| 123 | switch (safety_checks) { | 125 | switch (safety_checks) { |
| 124 | .checked_to_fixed_depth => { | 126 | .checked_to_fixed_depth => { |
| 125 | BitStack.pushWithStateAssumeCapacity(&self.nesting_stack, &self.indent_level, mode); | 127 | BitStack.pushWithStateAssumeCapacity(&self.nesting_stack, &self.indent_level, @intFromEnum(mode)); |
| 126 | }, | 128 | }, |
| 127 | .assumed_correct => { | 129 | .assumed_correct => { |
| 128 | self.indent_level += 1; | 130 | self.indent_level += 1; |
| 129 | }, | 131 | }, |
| 130 | } | 132 | } |
| 131 | } | 133 | } |
| 132 | fn popIndentation(self: *Stringify, assert_its_this_one: u1) void { | 134 | fn popIndentation(self: *Stringify, expected_mode: IndentationMode) void { |
| 133 | switch (safety_checks) { | 135 | switch (safety_checks) { |
| 134 | .checked_to_fixed_depth => { | 136 | .checked_to_fixed_depth => { |
| 135 | assert(BitStack.popWithState(&self.nesting_stack, &self.indent_level) == assert_its_this_one); | 137 | assert(BitStack.popWithState(&self.nesting_stack, &self.indent_level) == @intFromEnum(expected_mode)); |
| 136 | }, | 138 | }, |
| 137 | .assumed_correct => { | 139 | .assumed_correct => { |
| 138 | self.indent_level -= 1; | 140 | self.indent_level -= 1; |
| ... | @@ -154,8 +156,8 @@ fn indent(self: *Stringify) !void { | ... | @@ -154,8 +156,8 @@ fn indent(self: *Stringify) !void { |
| 154 | break :blk self.indent_level; | 156 | break :blk self.indent_level; |
| 155 | }, | 157 | }, |
| 156 | }; | 158 | }; |
| 157 | try self.stream.writeByte('\n'); | 159 | try self.writer.writeByte('\n'); |
| 158 | try self.stream.writeByteNTimes(char, n_chars); | 160 | try self.writer.splatByteAll(char, n_chars); |
| 159 | } | 161 | } |
| 160 | | 162 | |
| 161 | fn valueStart(self: *Stringify) !void { | 163 | fn valueStart(self: *Stringify) !void { |
| ... | @@ -178,13 +180,13 @@ fn valueStartAssumeTypeOk(self: *Stringify) !void { | ... | @@ -178,13 +180,13 @@ fn valueStartAssumeTypeOk(self: *Stringify) !void { |
| 178 | }, | 180 | }, |
| 179 | .comma => { | 181 | .comma => { |
| 180 | // Subsequent item in a container. | 182 | // Subsequent item in a container. |
| 181 | try self.stream.writeByte(','); | 183 | try self.writer.writeByte(','); |
| 182 | try self.indent(); | 184 | try self.indent(); |
| 183 | }, | 185 | }, |
| 184 | .colon => { | 186 | .colon => { |
| 185 | try self.stream.writeByte(':'); | 187 | try self.writer.writeByte(':'); |
| 186 | if (self.options.whitespace != .minified) { | 188 | if (self.options.whitespace != .minified) { |
| 187 | try self.stream.writeByte(' '); | 189 | try self.writer.writeByte(' '); |
| 188 | } | 190 | } |
| 189 | }, | 191 | }, |
| 190 | } | 192 | } |
| ... | @@ -197,7 +199,7 @@ fn valueDone(self: *Stringify) void { | ... | @@ -197,7 +199,7 @@ fn valueDone(self: *Stringify) void { |
| 197 | fn isObjectKeyExpected(self: *const Stringify) ?bool { | 199 | fn isObjectKeyExpected(self: *const Stringify) ?bool { |
| 198 | switch (safety_checks) { | 200 | switch (safety_checks) { |
| 199 | .checked_to_fixed_depth => return self.indent_level > 0 and | 201 | .checked_to_fixed_depth => return self.indent_level > 0 and |
| 200 | BitStack.peekWithState(&self.nesting_stack, self.indent_level) == OBJECT_MODE and | 202 | BitStack.peekWithState(&self.nesting_stack, self.indent_level) == @intFromEnum(IndentationMode.object) and |
| 201 | self.next_punctuation != .colon, | 203 | self.next_punctuation != .colon, |
| 202 | .assumed_correct => return null, | 204 | .assumed_correct => return null, |
| 203 | } | 205 | } |
| ... | @@ -214,7 +216,7 @@ fn isComplete(self: *const Stringify) bool { | ... | @@ -214,7 +216,7 @@ fn isComplete(self: *const Stringify) bool { |
| 214 | pub fn print(self: *Stringify, comptime fmt: []const u8, args: anytype) anyerror!void { | 216 | pub fn print(self: *Stringify, comptime fmt: []const u8, args: anytype) anyerror!void { |
| 215 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); | 217 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 216 | try self.valueStart(); | 218 | try self.valueStart(); |
| 217 | try self.stream.print(fmt, args); | 219 | try self.writer.print(fmt, args); |
| 218 | self.valueDone(); | 220 | self.valueDone(); |
| 219 | } | 221 | } |
| 220 | | 222 | |
| ... | @@ -224,7 +226,6 @@ test print { | ... | @@ -224,7 +226,6 @@ test print { |
| 224 | out.initFixed(&out_buf); | 226 | out.initFixed(&out_buf); |
| 225 | | 227 | |
| 226 | var w: Stringify = .{ .writer = &out, .options = .{ .whitespace = .indent_2 } }; | 228 | var w: Stringify = .{ .writer = &out, .options = .{ .whitespace = .indent_2 } }; |
| 227 | defer w.deinit(); | | |
| 228 | | 229 | |
| 229 | try w.beginObject(); | 230 | try w.beginObject(); |
| 230 | try w.objectField("a"); | 231 | try w.objectField("a"); |
| ... | @@ -248,8 +249,8 @@ test print { | ... | @@ -248,8 +249,8 @@ test print { |
| 248 | try std.testing.expectEqualStrings(expected, out.getWritten()); | 249 | try std.testing.expectEqualStrings(expected, out.getWritten()); |
| 249 | } | 250 | } |
| 250 | | 251 | |
| 251 | /// An alternative to calling `write` that allows you to write directly to the `.stream` field, e.g. with `.stream.writeAll()`. | 252 | /// An alternative to calling `write` that allows you to write directly to the `.writer` field, e.g. with `.writer.writeAll()`. |
| 252 | /// Call `beginWriteRaw()`, then write a complete value (including any quotes if necessary) directly to the `.stream` field, | 253 | /// Call `beginWriteRaw()`, then write a complete value (including any quotes if necessary) directly to the `.writer` field, |
| 253 | /// then call `endWriteRaw()`. | 254 | /// then call `endWriteRaw()`. |
| 254 | /// This can be useful for streaming very long strings into the output without needing it all buffered in memory. | 255 | /// This can be useful for streaming very long strings into the output without needing it all buffered in memory. |
| 255 | pub fn beginWriteRaw(self: *Stringify) !void { | 256 | pub fn beginWriteRaw(self: *Stringify) !void { |
| ... | @@ -276,7 +277,7 @@ pub fn endWriteRaw(self: *Stringify) void { | ... | @@ -276,7 +277,7 @@ pub fn endWriteRaw(self: *Stringify) void { |
| 276 | pub fn objectField(self: *Stringify, key: []const u8) anyerror!void { | 277 | pub fn objectField(self: *Stringify, key: []const u8) anyerror!void { |
| 277 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); | 278 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 278 | try self.objectFieldStart(); | 279 | try self.objectFieldStart(); |
| 279 | try encodeJsonString(key, self.options, self.stream); | 280 | try encodeJsonString(key, self.options, self.writer); |
| 280 | self.next_punctuation = .colon; | 281 | self.next_punctuation = .colon; |
| 281 | } | 282 | } |
| 282 | /// See `Stringify` for when to call this method. | 283 | /// See `Stringify` for when to call this method. |
| ... | @@ -287,12 +288,12 @@ pub fn objectFieldRaw(self: *Stringify, quoted_key: []const u8) anyerror!void { | ... | @@ -287,12 +288,12 @@ pub fn objectFieldRaw(self: *Stringify, quoted_key: []const u8) anyerror!void { |
| 287 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); | 288 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 288 | assert(quoted_key.len >= 2 and quoted_key[0] == '"' and quoted_key[quoted_key.len - 1] == '"'); // quoted_key should be "quoted". | 289 | assert(quoted_key.len >= 2 and quoted_key[0] == '"' and quoted_key[quoted_key.len - 1] == '"'); // quoted_key should be "quoted". |
| 289 | try self.objectFieldStart(); | 290 | try self.objectFieldStart(); |
| 290 | try self.stream.writeAll(quoted_key); | 291 | try self.writer.writeAll(quoted_key); |
| 291 | self.next_punctuation = .colon; | 292 | self.next_punctuation = .colon; |
| 292 | } | 293 | } |
| 293 | | 294 | |
| 294 | /// In the rare case that you need to write very long object field names, | 295 | /// In the rare case that you need to write very long object field names, |
| 295 | /// this is an alternative to `objectField` and `objectFieldRaw` that allows you to write directly to the `.stream` field | 296 | /// this is an alternative to `objectField` and `objectFieldRaw` that allows you to write directly to the `.writer` field |
| 296 | /// similar to `beginWriteRaw`. | 297 | /// similar to `beginWriteRaw`. |
| 297 | /// Call `endObjectFieldRaw()` when you're done. | 298 | /// Call `endObjectFieldRaw()` when you're done. |
| 298 | pub fn beginObjectFieldRaw(self: *Stringify) !void { | 299 | pub fn beginObjectFieldRaw(self: *Stringify) !void { |
| ... | @@ -351,9 +352,9 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void { | ... | @@ -351,9 +352,9 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void { |
| 351 | if (self.options.emit_nonportable_numbers_as_strings and | 352 | if (self.options.emit_nonportable_numbers_as_strings and |
| 352 | (v <= -(1 << 53) or v >= (1 << 53))) | 353 | (v <= -(1 << 53) or v >= (1 << 53))) |
| 353 | { | 354 | { |
| 354 | try self.stream.print("\"{}\"", .{v}); | 355 | try self.writer.print("\"{}\"", .{v}); |
| 355 | } else { | 356 | } else { |
| 356 | try self.stream.print("{}", .{v}); | 357 | try self.writer.print("{}", .{v}); |
| 357 | } | 358 | } |
| 358 | self.valueDone(); | 359 | self.valueDone(); |
| 359 | return; | 360 | return; |
| ... | @@ -364,25 +365,25 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void { | ... | @@ -364,25 +365,25 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void { |
| 364 | .float, .comptime_float => { | 365 | .float, .comptime_float => { |
| 365 | if (@as(f64, @floatCast(v)) == v) { | 366 | if (@as(f64, @floatCast(v)) == v) { |
| 366 | try self.valueStart(); | 367 | try self.valueStart(); |
| 367 | try self.stream.print("{}", .{@as(f64, @floatCast(v))}); | 368 | try self.writer.print("{}", .{@as(f64, @floatCast(v))}); |
| 368 | self.valueDone(); | 369 | self.valueDone(); |
| 369 | return; | 370 | return; |
| 370 | } | 371 | } |
| 371 | try self.valueStart(); | 372 | try self.valueStart(); |
| 372 | try self.stream.print("\"{}\"", .{v}); | 373 | try self.writer.print("\"{}\"", .{v}); |
| 373 | self.valueDone(); | 374 | self.valueDone(); |
| 374 | return; | 375 | return; |
| 375 | }, | 376 | }, |
| 376 | | 377 | |
| 377 | .bool => { | 378 | .bool => { |
| 378 | try self.valueStart(); | 379 | try self.valueStart(); |
| 379 | try self.stream.writeAll(if (v) "true" else "false"); | 380 | try self.writer.writeAll(if (v) "true" else "false"); |
| 380 | self.valueDone(); | 381 | self.valueDone(); |
| 381 | return; | 382 | return; |
| 382 | }, | 383 | }, |
| 383 | .null => { | 384 | .null => { |
| 384 | try self.valueStart(); | 385 | try self.valueStart(); |
| 385 | try self.stream.writeAll("null"); | 386 | try self.writer.writeAll("null"); |
| 386 | self.valueDone(); | 387 | self.valueDone(); |
| 387 | return; | 388 | return; |
| 388 | }, | 389 | }, |
| ... | @@ -529,7 +530,7 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void { | ... | @@ -529,7 +530,7 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void { |
| 529 | | 530 | |
| 530 | fn stringValue(self: *Stringify, s: []const u8) !void { | 531 | fn stringValue(self: *Stringify, s: []const u8) !void { |
| 531 | try self.valueStart(); | 532 | try self.valueStart(); |
| 532 | try encodeJsonString(s, self.options, self.stream); | 533 | try encodeJsonString(s, self.options, self.writer); |
| 533 | self.valueDone(); | 534 | self.valueDone(); |
| 534 | } | 535 | } |
| 535 | | 536 | |
| ... | @@ -564,7 +565,7 @@ pub const Options = struct { | ... | @@ -564,7 +565,7 @@ pub const Options = struct { |
| 564 | emit_nonportable_numbers_as_strings: bool = false, | 565 | emit_nonportable_numbers_as_strings: bool = false, |
| 565 | }; | 566 | }; |
| 566 | | 567 | |
| 567 | /// Writes the given value to the `std.io.Writer` stream. | 568 | /// Writes the given value to the `std.io.Writer` writer. |
| 568 | /// See `Stringify` for how the given value is serialized into JSON. | 569 | /// See `Stringify` for how the given value is serialized into JSON. |
| 569 | /// The maximum nesting depth of the output JSON document is 256. | 570 | /// The maximum nesting depth of the output JSON document is 256. |
| 570 | pub fn value(v: anytype, options: Options, writer: *std.io.BufferedWriter) anyerror!void { | 571 | pub fn value(v: anytype, options: Options, writer: *std.io.BufferedWriter) anyerror!void { |
| ... | @@ -616,7 +617,7 @@ pub fn valueAlloc(gpa: Allocator, v: anytype, options: Options) error{OutOfMemor | ... | @@ -616,7 +617,7 @@ pub fn valueAlloc(gpa: Allocator, v: anytype, options: Options) error{OutOfMemor |
| 616 | var aw: std.io.AllocatingWriter = undefined; | 617 | var aw: std.io.AllocatingWriter = undefined; |
| 617 | const writer = aw.init(gpa); | 618 | const writer = aw.init(gpa); |
| 618 | defer aw.deinit(); | 619 | defer aw.deinit(); |
| 619 | try value(v, options, writer); | 620 | value(v, options, writer) catch return error.OutOfMemory; // TODO: try @errorCast(...) |
| 620 | return aw.toOwnedSlice(); | 621 | return aw.toOwnedSlice(); |
| 621 | } | 622 | } |
| 622 | | 623 | |
| ... | @@ -631,27 +632,27 @@ test valueAlloc { | ... | @@ -631,27 +632,27 @@ test valueAlloc { |
| 631 | try std.testing.expectEqualStrings(expected, actual); | 632 | try std.testing.expectEqualStrings(expected, actual); |
| 632 | } | 633 | } |
| 633 | | 634 | |
| 634 | fn outputUnicodeEscape(codepoint: u21, out_stream: *std.io.BufferedWriter) !void { | 635 | fn outputUnicodeEscape(codepoint: u21, bw: *std.io.BufferedWriter) anyerror!void { |
| 635 | if (codepoint <= 0xFFFF) { | 636 | if (codepoint <= 0xFFFF) { |
| 636 | // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF), | 637 | // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF), |
| 637 | // then it may be represented as a six-character sequence: a reverse solidus, followed | 638 | // then it may be represented as a six-character sequence: a reverse solidus, followed |
| 638 | // by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point. | 639 | // by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point. |
| 639 | try out_stream.writeAll("\\u"); | 640 | try bw.writeAll("\\u"); |
| 640 | try std.fmt.formatIntValue(codepoint, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); | 641 | try bw.printInt("x", .{ .width = 4, .fill = '0' }, codepoint); |
| 641 | } else { | 642 | } else { |
| 642 | assert(codepoint <= 0x10FFFF); | 643 | assert(codepoint <= 0x10FFFF); |
| 643 | // To escape an extended character that is not in the Basic Multilingual Plane, | 644 | // To escape an extended character that is not in the Basic Multilingual Plane, |
| 644 | // the character is represented as a 12-character sequence, encoding the UTF-16 surrogate pair. | 645 | // the character is represented as a 12-character sequence, encoding the UTF-16 surrogate pair. |
| 645 | const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800; | 646 | const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800; |
| 646 | const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00; | 647 | const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00; |
| 647 | try out_stream.writeAll("\\u"); | 648 | try bw.writeAll("\\u"); |
| 648 | try std.fmt.formatIntValue(high, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); | 649 | try bw.printInt("x", .{ .width = 4, .fill = '0' }, high); |
| 649 | try out_stream.writeAll("\\u"); | 650 | try bw.writeAll("\\u"); |
| 650 | try std.fmt.formatIntValue(low, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); | 651 | try bw.printInt("x", .{ .width = 4, .fill = '0' }, low); |
| 651 | } | 652 | } |
| 652 | } | 653 | } |
| 653 | | 654 | |
| 654 | fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) !void { | 655 | fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) anyerror!void { |
| 655 | switch (c) { | 656 | switch (c) { |
| 656 | '\\' => try writer.writeAll("\\\\"), | 657 | '\\' => try writer.writeAll("\\\\"), |
| 657 | '\"' => try writer.writeAll("\\\""), | 658 | '\"' => try writer.writeAll("\\\""), |
| ... | @@ -665,14 +666,14 @@ fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) !void { | ... | @@ -665,14 +666,14 @@ fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) !void { |
| 665 | } | 666 | } |
| 666 | | 667 | |
| 667 | /// Write `string` to `writer` as a JSON encoded string. | 668 | /// Write `string` to `writer` as a JSON encoded string. |
| 668 | pub fn encodeJsonString(string: []const u8, options: Options, writer: *std.io.BufferedWriter) !void { | 669 | pub fn encodeJsonString(string: []const u8, options: Options, writer: *std.io.BufferedWriter) anyerror!void { |
| 669 | try writer.writeByte('\"'); | 670 | try writer.writeByte('\"'); |
| 670 | try encodeJsonStringChars(string, options, writer); | 671 | try encodeJsonStringChars(string, options, writer); |
| 671 | try writer.writeByte('\"'); | 672 | try writer.writeByte('\"'); |
| 672 | } | 673 | } |
| 673 | | 674 | |
| 674 | /// Write `chars` to `writer` as JSON encoded string characters. | 675 | /// Write `chars` to `writer` as JSON encoded string characters. |
| 675 | pub fn encodeJsonStringChars(chars: []const u8, options: Options, writer: *std.io.BufferedWriter) !void { | 676 | pub fn encodeJsonStringChars(chars: []const u8, options: Options, writer: *std.io.BufferedWriter) anyerror!void { |
| 676 | var write_cursor: usize = 0; | 677 | var write_cursor: usize = 0; |
| 677 | var i: usize = 0; | 678 | var i: usize = 0; |
| 678 | if (options.escape_unicode) { | 679 | if (options.escape_unicode) { |
| ... | @@ -721,8 +722,8 @@ test "json write stream" { | ... | @@ -721,8 +722,8 @@ test "json write stream" { |
| 721 | try testBasicWriteStream(&w); | 722 | try testBasicWriteStream(&w); |
| 722 | } | 723 | } |
| 723 | | 724 | |
| 724 | fn testBasicWriteStream(w: *Stringify, out: *std.io.BufferedWriter) !void { | 725 | fn testBasicWriteStream(w: *Stringify) anyerror!void { |
| 725 | out.reset(); | 726 | w.writer.reset(); |
| 726 | | 727 | |
| 727 | try w.beginObject(); | 728 | try w.beginObject(); |
| 728 | | 729 | |
| ... | @@ -765,7 +766,7 @@ fn testBasicWriteStream(w: *Stringify, out: *std.io.BufferedWriter) !void { | ... | @@ -765,7 +766,7 @@ fn testBasicWriteStream(w: *Stringify, out: *std.io.BufferedWriter) !void { |
| 765 | \\ "float": 3.5e0 | 766 | \\ "float": 3.5e0 |
| 766 | \\} | 767 | \\} |
| 767 | ; | 768 | ; |
| 768 | try std.testing.expectEqualStrings(expected, out.getWritten()); | 769 | try std.testing.expectEqualStrings(expected, w.writer.getWritten()); |
| 769 | } | 770 | } |
| 770 | | 771 | |
| 771 | fn getJsonObject(allocator: std.mem.Allocator) !std.json.Value { | 772 | fn getJsonObject(allocator: std.mem.Allocator) !std.json.Value { |
| ... | @@ -967,59 +968,11 @@ test "stringify struct with custom stringifier" { | ... | @@ -967,59 +968,11 @@ test "stringify struct with custom stringifier" { |
| 967 | } | 968 | } |
| 968 | | 969 | |
| 969 | fn testStringify(expected: []const u8, v: anytype, options: Options) !void { | 970 | fn testStringify(expected: []const u8, v: anytype, options: Options) !void { |
| 970 | const ValidationWriter = struct { | 971 | var buffer: [4096]u8 = undefined; |
| 971 | const Self = @This(); | 972 | var bw: std.io.BufferedWriter = undefined; |
| 972 | pub const Writer = std.io.Writer(*Self, Error, Self.write); | 973 | bw.initFixed(&buffer); |
| 973 | pub const Error = error{ | 974 | try value(v, options, &bw); |
| 974 | TooMuchData, | 975 | try std.testing.expectEqualStrings(expected, bw.getWritten()); |
| 975 | DifferentData, | | |
| 976 | }; | | |
| 977 | | | |
| 978 | expected_remaining: []const u8, | | |
| 979 | | | |
| 980 | fn init(exp: []const u8) Self { | | |
| 981 | return .{ .expected_remaining = exp }; | | |
| 982 | } | | |
| 983 | | | |
| 984 | pub fn writer(self: *Self) Writer { | | |
| 985 | return .{ .context = self }; | | |
| 986 | } | | |
| 987 | | | |
| 988 | fn write(self: *Self, bytes: []const u8) Error!usize { | | |
| 989 | if (self.expected_remaining.len < bytes.len) { | | |
| 990 | std.debug.print( | | |
| 991 | \\====== expected this output: ========= | | |
| 992 | \\{s} | | |
| 993 | \\======== instead found this: ========= | | |
| 994 | \\{s} | | |
| 995 | \\====================================== | | |
| 996 | , .{ | | |
| 997 | self.expected_remaining, | | |
| 998 | bytes, | | |
| 999 | }); | | |
| 1000 | return error.TooMuchData; | | |
| 1001 | } | | |
| 1002 | if (!std.mem.eql(u8, self.expected_remaining[0..bytes.len], bytes)) { | | |
| 1003 | std.debug.print( | | |
| 1004 | \\====== expected this output: ========= | | |
| 1005 | \\{s} | | |
| 1006 | \\======== instead found this: ========= | | |
| 1007 | \\{s} | | |
| 1008 | \\====================================== | | |
| 1009 | , .{ | | |
| 1010 | self.expected_remaining[0..bytes.len], | | |
| 1011 | bytes, | | |
| 1012 | }); | | |
| 1013 | return error.DifferentData; | | |
| 1014 | } | | |
| 1015 | self.expected_remaining = self.expected_remaining[bytes.len..]; | | |
| 1016 | return bytes.len; | | |
| 1017 | } | | |
| 1018 | }; | | |
| 1019 | | | |
| 1020 | var vos = ValidationWriter.init(expected); | | |
| 1021 | try value(v, options, vos.writer()); | | |
| 1022 | if (vos.expected_remaining.len > 0) return error.NotEnoughData; | | |
| 1023 | } | 976 | } |
| 1024 | | 977 | |
| 1025 | test "raw streaming" { | 978 | test "raw streaming" { |
| ... | @@ -1030,12 +983,12 @@ test "raw streaming" { | ... | @@ -1030,12 +983,12 @@ test "raw streaming" { |
| 1030 | var w: Stringify = .{ .writer = &out, .options = .{ .whitespace = .indent_2 } }; | 983 | var w: Stringify = .{ .writer = &out, .options = .{ .whitespace = .indent_2 } }; |
| 1031 | try w.beginObject(); | 984 | try w.beginObject(); |
| 1032 | try w.beginObjectFieldRaw(); | 985 | try w.beginObjectFieldRaw(); |
| 1033 | try w.stream.writeAll("\"long"); | 986 | try w.writer.writeAll("\"long"); |
| 1034 | try w.stream.writeAll(" key\""); | 987 | try w.writer.writeAll(" key\""); |
| 1035 | w.endObjectFieldRaw(); | 988 | w.endObjectFieldRaw(); |
| 1036 | try w.beginWriteRaw(); | 989 | try w.beginWriteRaw(); |
| 1037 | try w.stream.writeAll("\"long"); | 990 | try w.writer.writeAll("\"long"); |
| 1038 | try w.stream.writeAll(" value\""); | 991 | try w.writer.writeAll(" value\""); |
| 1039 | w.endWriteRaw(); | 992 | w.endWriteRaw(); |
| 1040 | try w.endObject(); | 993 | try w.endObject(); |
| 1041 | | 994 | |