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