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