From c70ffd095a6de5cd5b872796a0d82a8c5afc1511 Mon Sep 17 00:00:00 2001 From: Uko Kokņevičs Date: Sat, 20 Jul 2024 17:22:25 +0300 Subject: Initial commit --- src/json.zig | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/json.zig (limited to 'src/json.zig') diff --git a/src/json.zig b/src/json.zig new file mode 100644 index 0000000..9252344 --- /dev/null +++ b/src/json.zig @@ -0,0 +1,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 = 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; +} -- cgit v1.2.3