Commit 1e087d3a64
lib/std/json/test.zig
@@ -2459,6 +2459,56 @@ test "parse into struct ignoring unknown fields" {
try testing.expectEqualSlices(u8, "zig", r.language);
}
+test "parse into tuple" {
+ const options = ParseOptions{ .allocator = testing.allocator };
+ const Union = union(enum) {
+ char: u8,
+ float: f64,
+ string: []const u8,
+ };
+ const T = std.meta.Tuple(&.{
+ i64,
+ f64,
+ bool,
+ []const u8,
+ ?bool,
+ struct {
+ foo: i32,
+ bar: []const u8,
+ },
+ std.meta.Tuple(&.{ u8, []const u8, u8 }),
+ Union,
+ });
+ var ts = TokenStream.init(
+ \\[
+ \\ 420,
+ \\ 3.14,
+ \\ true,
+ \\ "zig",
+ \\ null,
+ \\ {
+ \\ "foo": 1,
+ \\ "bar": "zero"
+ \\ },
+ \\ [4, "två", 42],
+ \\ 12.34
+ \\]
+ );
+ const r = try parse(T, &ts, options);
+ defer parseFree(T, r, options);
+ try testing.expectEqual(@as(i64, 420), r[0]);
+ try testing.expectEqual(@as(f64, 3.14), r[1]);
+ try testing.expectEqual(true, r[2]);
+ try testing.expectEqualSlices(u8, "zig", r[3]);
+ try testing.expectEqual(@as(?bool, null), r[4]);
+ try testing.expectEqual(@as(i32, 1), r[5].foo);
+ try testing.expectEqualSlices(u8, "zero", r[5].bar);
+ try testing.expectEqual(@as(u8, 4), r[6][0]);
+ try testing.expectEqualSlices(u8, "två", r[6][1]);
+ try testing.expectEqual(@as(u8, 42), r[6][2]);
+ try testing.expectEqual(Union{ .float = 12.34 }, r[7]);
+}
+
const ParseIntoRecursiveUnionDefinitionValue = union(enum) {
integer: i64,
array: []const ParseIntoRecursiveUnionDefinitionValue,
lib/std/json.zig
@@ -1511,6 +1511,34 @@ fn parseInternal(
}
},
.Struct => |structInfo| {
+ if (structInfo.is_tuple) {
+ switch (token) {
+ .ArrayBegin => {},
+ else => return error.UnexpectedToken,
+ }
+ var r: T = undefined;
+ var child_options = options;
+ child_options.allow_trailing_data = true;
+ var fields_seen: usize = 0;
+ errdefer {
+ inline for (0..structInfo.fields.len) |i| {
+ if (i < fields_seen) {
+ parseFree(structInfo.fields[i].type, r[i], options);
+ }
+ }
+ }
+ inline for (0..structInfo.fields.len) |i| {
+ r[i] = try parse(structInfo.fields[i].type, tokens, child_options);
+ fields_seen = i + 1;
+ }
+ const tok = (try tokens.next()) orelse return error.UnexpectedEndOfJson;
+ switch (tok) {
+ .ArrayEnd => {},
+ else => return error.UnexpectedToken,
+ }
+ return r;
+ }
+
switch (token) {
.ObjectBegin => {},
else => return error.UnexpectedToken,
@@ -2290,7 +2318,7 @@ pub fn stringify(
return value.jsonStringify(options, out_stream);
}
- try out_stream.writeByte('{');
+ try out_stream.writeByte(if (S.is_tuple) '[' else '{');
var field_output = false;
var child_options = options;
if (child_options.whitespace) |*child_whitespace| {
@@ -2320,11 +2348,13 @@ pub fn stringify(
if (child_options.whitespace) |child_whitespace| {
try child_whitespace.outputIndent(out_stream);
}
- try encodeJsonString(Field.name, options, out_stream);
- try out_stream.writeByte(':');
- if (child_options.whitespace) |child_whitespace| {
- if (child_whitespace.separator) {
- try out_stream.writeByte(' ');
+ if (!S.is_tuple) {
+ try encodeJsonString(Field.name, options, out_stream);
+ try out_stream.writeByte(':');
+ if (child_options.whitespace) |child_whitespace| {
+ if (child_whitespace.separator) {
+ try out_stream.writeByte(' ');
+ }
}
}
try stringify(@field(value, Field.name), child_options, out_stream);
@@ -2335,7 +2365,7 @@ pub fn stringify(
try whitespace.outputIndent(out_stream);
}
}
- try out_stream.writeByte('}');
+ try out_stream.writeByte(if (S.is_tuple) ']' else '}');
return;
},
.ErrorSet => return stringify(@as([]const u8, @errorName(value)), options, out_stream),
@@ -2649,6 +2679,10 @@ test "stringify vector" {
try teststringify("[1,1]", @splat(2, @as(u32, 1)), StringifyOptions{});
}
+test "stringify tuple" {
+ try teststringify("[\"foo\",42]", std.meta.Tuple(&.{ []const u8, usize }){ "foo", 42 }, StringifyOptions{});
+}
+
fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions) !void {
const ValidationWriter = struct {
const Self = @This();