1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
// TODO: ALSO IMPLEMENT jsonStringify !!!
const std = @import("std");
const Allocator = std.mem.Allocator;
const ParseError = std.json.ParseError;
const ParseFromValueError = std.json.ParseFromValueError;
const ParseOptions = std.json.ParseOptions;
const Value = std.json.Value;
pub fn JsonParse(T: type) type {
const f = struct {
pub fn f(allocator: Allocator, source: anytype, options: ParseOptions) ParseError(@TypeOf(source.*))!T {
_ = allocator;
_ = options;
unreachable;
}
}.f;
return @TypeOf(f);
}
pub fn makeJsonParse(T: type) JsonParse(T) {
comptime if (!std.meta.hasFn(T, "jsonParseFromValue")) {
@compileError("Did you forget `pub const jsonParseFromValue = json.makeJsonParseFromValue(" ++ @typeName(T) ++ ") ?");
};
return struct {
pub fn jsonParse(allocator: Allocator, source: anytype, options: ParseOptions) ParseError(@TypeOf(source.*))!T {
const value = try std.json.innerParse(Value, allocator, source, options);
return try T.jsonParseFromValue(allocator, value, options);
}
}.jsonParse;
}
pub fn JsonParseFromValue(T: type) type {
const f = struct {
pub fn f(allocator: Allocator, source: Value, options: ParseOptions) ParseFromValueError!T {
_ = allocator;
_ = source;
_ = options;
unreachable;
}
}.f;
return @TypeOf(f);
}
pub fn makeJsonParseFromValue(T: type) JsonParseFromValue(T) {
return makeJsonParseFromValueWithTag(T, "type");
}
pub fn makeJsonParseFromValueWithTag(T: type, comptime tag: [:0]const u8) JsonParseFromValue(T) {
const union_info = switch (@typeInfo(T)) {
.@"union" => |info| blk: {
if (info.tag_type == null) {
@compileError("Only tagged unions supported, got '" ++ @typeName(T) ++ "'");
}
break :blk info;
},
else => @compileError("Only unions supported, got '" ++ @typeName(T) ++ "'"),
};
const TagType = union_info.tag_type.?;
const Base = blk: {
const info = std.builtin.Type{ .@"struct" = .{
.layout = .auto,
.fields = &.{.{
.name = tag,
.type = TagType,
.default_value_ptr = null,
.is_comptime = false,
.alignment = 0,
}},
.decls = &.{},
.is_tuple = false,
} };
break :blk @Type(info);
};
return struct {
pub fn jsonParseFromValue(
allocator: Allocator,
source: Value,
options: ParseOptions,
) ParseFromValueError!T {
const new_options = blk: {
var opt = options;
opt.ignore_unknown_fields = true;
break :blk opt;
};
const base = try std.json.innerParseFromValue(Base, allocator, source, new_options);
const typeName = @tagName(@field(base, tag));
// TODO: Upgrade to StaticStringMap
inline for (union_info.fields) |field_info| {
if (std.mem.eql(u8, typeName, field_info.name)) {
return @unionInit(
T,
field_info.name,
try std.json.innerParseFromValue(field_info.type, allocator, source, new_options),
);
}
}
unreachable;
}
}.jsonParseFromValue;
}
|