// 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; }