std.json: support tuples

This commit is contained in:
Marcus Ramse 2023-03-08 16:04:57 +01:00 committed by Veikka Tuominen
parent cd3575b0f0
commit 1e087d3a64
2 changed files with 91 additions and 7 deletions

View File

@ -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();

View File

@ -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,