diff --git a/src/util/serialize.zig b/src/util/serialize.zig index 0cc7a05..a7ca8e1 100644 --- a/src/util/serialize.zig +++ b/src/util/serialize.zig @@ -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; },