Skip to content

Commit 32477dc

Browse files
claude[bot]lalinsky
andcommitted
Fix enum optional handling issues
- Fix getEnumSize to properly handle optional enum types - Fix unpackEnum to support null handling for optional enums - Add comprehensive tests for optional enum functionality - Addresses CodeRabbit review feedback Co-authored-by: Lukáš Lalinský <lalinsky@users.noreply.github.com>
1 parent 54d1632 commit 32477dc

File tree

1 file changed

+106
-8
lines changed

1 file changed

+106
-8
lines changed

src/enum.zig

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,21 @@ pub fn getMaxEnumSize(comptime T: type) usize {
2626
}
2727

2828
pub fn getEnumSize(comptime T: type, value: T) usize {
29-
const Type = assertEnumType(T);
30-
const tag_type = @typeInfo(Type).@"enum".tag_type;
31-
const int_value = @intFromEnum(value);
32-
return getIntSize(tag_type, int_value);
29+
switch (@typeInfo(T)) {
30+
.@"enum" => {
31+
const tag_type = @typeInfo(T).@"enum".tag_type;
32+
const int_value = @intFromEnum(value);
33+
return getIntSize(tag_type, int_value);
34+
},
35+
.optional => |opt_info| {
36+
if (value) |v| {
37+
return getEnumSize(opt_info.child, v);
38+
} else {
39+
return 1; // size of null
40+
}
41+
},
42+
else => @compileError("Expected enum or optional enum, got " ++ @typeName(T)),
43+
}
3344
}
3445

3546
pub fn packEnum(writer: anytype, comptime T: type, value_or_maybe_null: T) !void {
@@ -43,10 +54,53 @@ pub fn packEnum(writer: anytype, comptime T: type, value_or_maybe_null: T) !void
4354
}
4455

4556
pub fn unpackEnum(reader: anytype, comptime T: type) !T {
46-
const Type = assertEnumType(T);
47-
const tag_type = @typeInfo(Type).@"enum".tag_type;
48-
const int_value = try unpackInt(reader, tag_type);
49-
return @enumFromInt(int_value);
57+
switch (@typeInfo(T)) {
58+
.@"enum" => {
59+
const tag_type = @typeInfo(T).@"enum".tag_type;
60+
const int_value = try unpackInt(reader, tag_type);
61+
return @enumFromInt(int_value);
62+
},
63+
.optional => |opt_info| {
64+
const header = try reader.readByte();
65+
if (header == hdrs.NIL) {
66+
return null;
67+
}
68+
69+
// Put the header back and unpack as non-optional enum
70+
// We need to create a buffered reader that includes the header
71+
const backup_reader = struct {
72+
header: u8,
73+
reader: @TypeOf(reader),
74+
header_consumed: bool = false,
75+
76+
const Self = @This();
77+
78+
pub fn readByte(self: *Self) !u8 {
79+
if (!self.header_consumed) {
80+
self.header_consumed = true;
81+
return self.header;
82+
}
83+
return try self.reader.readByte();
84+
}
85+
86+
pub fn readBytesNoEof(self: *Self, buf: []u8) !void {
87+
if (!self.header_consumed and buf.len > 0) {
88+
buf[0] = self.header;
89+
self.header_consumed = true;
90+
if (buf.len > 1) {
91+
try self.reader.readBytesNoEof(buf[1..]);
92+
}
93+
} else {
94+
try self.reader.readBytesNoEof(buf);
95+
}
96+
}
97+
};
98+
99+
var backup = backup_reader{ .header = header, .reader = reader };
100+
return try unpackEnum(backup.reader(), opt_info.child);
101+
},
102+
else => @compileError("Expected enum or optional enum, got " ++ @typeName(T)),
103+
}
50104
}
51105

52106
test "getMaxEnumSize" {
@@ -127,4 +181,48 @@ test "enum edge cases" {
127181
const result = try unpackEnum(stream.reader(), MixedEnum);
128182
try std.testing.expectEqual(MixedEnum.second, result);
129183
try std.testing.expectEqual(11, @intFromEnum(result));
184+
}
185+
186+
test "optional enum" {
187+
const TestEnum = enum(u8) { foo = 1, bar = 2 };
188+
const OptionalEnum = ?TestEnum;
189+
190+
// Test non-null optional enum
191+
{
192+
var buffer = std.ArrayList(u8).init(std.testing.allocator);
193+
defer buffer.deinit();
194+
195+
const value: OptionalEnum = .bar;
196+
try packEnum(buffer.writer(), OptionalEnum, value);
197+
198+
var stream = std.io.fixedBufferStream(buffer.items);
199+
const result = try unpackEnum(stream.reader(), OptionalEnum);
200+
try std.testing.expectEqual(@as(OptionalEnum, .bar), result);
201+
}
202+
203+
// Test null optional enum
204+
{
205+
var buffer = std.ArrayList(u8).init(std.testing.allocator);
206+
defer buffer.deinit();
207+
208+
const value: OptionalEnum = null;
209+
try packEnum(buffer.writer(), OptionalEnum, value);
210+
211+
var stream = std.io.fixedBufferStream(buffer.items);
212+
const result = try unpackEnum(stream.reader(), OptionalEnum);
213+
try std.testing.expectEqual(@as(OptionalEnum, null), result);
214+
}
215+
}
216+
217+
test "getEnumSize with optional" {
218+
const TestEnum = enum(u8) { foo = 0, bar = 150 };
219+
const OptionalEnum = ?TestEnum;
220+
221+
// Test non-null optional enum size
222+
const value: OptionalEnum = .bar;
223+
try std.testing.expectEqual(2, getEnumSize(OptionalEnum, value)); // requires u8 format
224+
225+
// Test null optional enum size
226+
const null_value: OptionalEnum = null;
227+
try std.testing.expectEqual(1, getEnumSize(OptionalEnum, null_value)); // size of null
130228
}

0 commit comments

Comments
 (0)