authorgravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2021-02-01 00:55:22+11:00
committergravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2021-02-01 01:01:50+11:00
loge0a04e7f6739aa0d81e3102c04af37ab51e550d4
treeb92180bd138c69b18937f9e407702711e8f31b51
parentf88bb56ee5be01b83ab35bd7b6c95539a4e04a9d
signaturelock-open Commit is signed but in an unrecognized format.

allow more complex comptime fields in std.json


1 files changed, 94 insertions(+), 13 deletions(-)

lib/std/json.zig+94-13
......@@ -1374,6 +1374,65 @@ test "Value.jsonStringify" {
13741374 }
13751375}
13761376
1377/// parse tokens from a stream, returning `false` if they do not decode to `value`
1378fn parsesTo(comptime T: type, value: T, tokens: *TokenStream, options: ParseOptions) !bool {
1379 // TODO: should be able to write this function to not require an allocator
1380 const tmp = try parse(T, tokens, options);
1381 defer parseFree(T, tmp, options);
1382
1383 return parsedEqual(tmp, value);
1384}
1385
1386/// Returns if a value returned by `parse` is deep-equal to another value
1387fn parsedEqual(a: anytype, b: @TypeOf(a)) bool {
1388 switch (@typeInfo(@TypeOf(a))) {
1389 .Optional => {
1390 if (a == null and b == null) return true;
1391 if (a == null or b == null) return false;
1392 return parsedEqual(a.?, b.?);
1393 },
1394 .Union => |unionInfo| {
1395 if (info.tag_type) |UnionTag| {
1396 const tag_a = std.meta.activeTag(a);
1397 const tag_b = std.meta.activeTag(b);
1398 if (tag_a != tag_b) return false;
1399
1400 inline for (info.fields) |field_info| {
1401 if (@field(UnionTag, field_info.name) == tag_a) {
1402 return parsedEqual(@field(a, field_info.name), @field(b, field_info.name));
1403 }
1404 }
1405 return false;
1406 } else {
1407 unreachable;
1408 }
1409 },
1410 .Array => {
1411 for (a) |e, i|
1412 if (!parsedEqual(e, b[i])) return false;
1413 return true;
1414 },
1415 .Struct => |info| {
1416 inline for (info.fields) |field_info| {
1417 if (!parsedEqual(@field(a, field_info.name), @field(b, field_info.name))) return false;
1418 }
1419 return true;
1420 },
1421 .Pointer => |ptrInfo| switch (ptrInfo.size) {
1422 .One => return parsedEqual(a.*, b.*),
1423 .Slice => {
1424 if (a.len != b.len) return false;
1425 for (a) |e, i|
1426 if (!parsedEqual(e, b[i])) return false;
1427 return true;
1428 },
1429 .Many, .C => unreachable,
1430 },
1431 else => return a == b,
1432 }
1433 unreachable;
1434}
1435
13771436pub const ParseOptions = struct {
13781437 allocator: ?*Allocator = null,
13791438
......@@ -1507,9 +1566,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
15071566 }
15081567 }
15091568 if (field.is_comptime) {
1510 const value = try parse(field.field_type, tokens, options);
1511 defer parseFree(field.field_type, value, options);
1512 if (value != @field(r, field.name)) {
1569 if (!try parsesTo(field.field_type, field.default_value.?, tokens, options)) {
15131570 return error.UnexpectedValue;
15141571 }
15151572 } else {
......@@ -1809,16 +1866,40 @@ test "parseFree descends into tagged union" {
18091866}
18101867
18111868test "parse with comptime field" {
1812 const T = struct {
1813 comptime a: i32 = 0,
1814 b: bool,
1815 };
1816 testing.expectEqual(T{ .a = 0, .b = true }, try parse(T, &TokenStream.init(
1817 \\{
1818 \\ "a": 0,
1819 \\ "b": true
1820 \\}
1821 ), ParseOptions{}));
1869 {
1870 const T = struct {
1871 comptime a: i32 = 0,
1872 b: bool,
1873 };
1874 testing.expectEqual(T{ .a = 0, .b = true }, try parse(T, &TokenStream.init(
1875 \\{
1876 \\ "a": 0,
1877 \\ "b": true
1878 \\}
1879 ), ParseOptions{}));
1880 }
1881
1882 { // string comptime values currently require an allocator
1883 const T = union(enum) {
1884 foo: struct {
1885 comptime kind: []const u8 = "boolean",
1886 b: bool,
1887 },
1888 bar: struct {
1889 comptime kind: []const u8 = "float",
1890 b: f64,
1891 },
1892 };
1893
1894 const r = try std.json.parse(T, &std.json.TokenStream.init(
1895 \\{
1896 \\ "kind": "float",
1897 \\ "b": 1.0
1898 \\}
1899 ), .{
1900 .allocator = std.testing.allocator,
1901 });
1902 }
18221903}
18231904
18241905test "parse into struct with no fields" {