Handle partial union matches

This commit is contained in:
jaina heartles 2022-12-16 01:04:01 -08:00
parent cd311cfa3c
commit 91edd17801
1 changed files with 14 additions and 9 deletions

View File

@ -278,37 +278,42 @@ pub fn DeserializerContext(comptime Result: type, comptime From: type, comptime
util.deepFree(allocator, val);
}
const DeserializeError = error{ ParseFailure, MissingField, DuplicateUnionMember, SparseSlice, OutOfMemory };
fn deserialize(
self: *@This(),
allocator: std.mem.Allocator,
comptime T: type,
intermediary: anytype,
comptime field_ref: FieldRef,
) !?T {
) DeserializeError!?T {
if (comptime Context.options.isScalar(T)) {
const val = @field(intermediary.static, util.comptimeJoin(".", field_ref));
return try self.context.deserializeScalar(allocator, T, val orelse return null);
return self.context.deserializeScalar(allocator, T, val orelse return null) catch return error.ParseFailure;
}
switch (@typeInfo(T)) {
// At most one of any union field can be active at a time, and it is embedded
// in its parent container
// At most one of any union field can be active at a time
.Union => |info| {
var result: ?T = null;
errdefer if (result) |v| self.deserializeFree(allocator, v);
// TODO: errdefer cleanup
const union_ref: FieldRef = if (Context.options.embed_unions) field_ref[0 .. field_ref.len - 1] else field_ref;
var partial_match_found: bool = false;
inline for (info.fields) |field| {
const F = field.field_type;
const new_field_ref = union_ref ++ &[_][]const u8{field.name};
const maybe_value = try self.deserialize(allocator, F, intermediary, new_field_ref);
const maybe_value = self.deserialize(allocator, F, intermediary, field_ref) catch |err| switch (err) {
error.MissingField => blk: {
partial_match_found = true;
break :blk @as(?F, null);
},
else => |e| return e,
};
if (maybe_value) |value| {
// TODO: errdefer cleanup
errdefer self.deserializeFree(allocator, value);
if (result != null) return error.DuplicateUnionMember;
result = @unionInit(T, field.name, value);
}
}
if (partial_match_found and result == null) return error.MissingField;
return result;
},