2024-11-23 23:26:03 +00:00
|
|
|
const std = @import("std");
|
2024-11-28 21:39:57 +00:00
|
|
|
const builtin = @import("builtin");
|
2024-11-23 23:26:03 +00:00
|
|
|
|
|
|
|
|
pub fn enumLiteral(comptime T: type) *const fn (value: []const u8) anyerror!T {
|
|
|
|
|
const container = struct {
|
|
|
|
|
fn func(value: []const u8) anyerror!T {
|
|
|
|
|
return parseEnumFromStr(T, value);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return &container.func;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-28 21:39:57 +00:00
|
|
|
const log = std.log.scoped(.args);
|
|
|
|
|
|
2024-11-23 23:26:03 +00:00
|
|
|
fn parseEnumFromStr(comptime T: type, str: []const u8) !T {
|
2024-11-28 21:39:57 +00:00
|
|
|
const heapAllocator = std.heap.page_allocator;
|
|
|
|
|
const lower = try std.ascii.allocLowerString(heapAllocator, str);
|
|
|
|
|
defer heapAllocator.free(lower);
|
2024-11-23 23:26:03 +00:00
|
|
|
const info = @typeInfo(T);
|
|
|
|
|
|
|
|
|
|
comptime {
|
|
|
|
|
if (std.meta.activeTag(info) != .Enum) {
|
|
|
|
|
@compileError("Expected enum type, got " ++ @typeName(T));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline for (info.Enum.fields) |field| {
|
2024-11-28 21:39:57 +00:00
|
|
|
const lowerFieldName = try std.ascii.allocLowerString(heapAllocator, field.name);
|
|
|
|
|
defer heapAllocator.free(lowerFieldName);
|
|
|
|
|
if (std.mem.eql(u8, lowerFieldName, lower)) {
|
2024-11-23 23:26:03 +00:00
|
|
|
return @enumFromInt(field.value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-28 21:39:57 +00:00
|
|
|
if (builtin.is_test) {
|
|
|
|
|
return error.CouldNotParse; // short circuit to prevent test output pollution
|
|
|
|
|
// todo: maybe find a way to get Zig tests to run without stderr *unless* they fail?
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-28 21:45:02 +00:00
|
|
|
log.err("could not parse `{s}` as enum `{s}`", .{ str, @typeName(T) });
|
2024-11-28 21:39:57 +00:00
|
|
|
log.warn("hint: try one of the following:", .{});
|
|
|
|
|
|
|
|
|
|
inline for (info.Enum.fields) |field| {
|
|
|
|
|
const lowerFieldName = try std.ascii.allocLowerString(heapAllocator, field.name);
|
|
|
|
|
defer heapAllocator.free(lowerFieldName);
|
|
|
|
|
log.warn("- {s}", .{lowerFieldName});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return error.CouldNotParse;
|
2024-11-23 23:26:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test "enum literal" {
|
|
|
|
|
const t = std.testing;
|
|
|
|
|
|
|
|
|
|
const Demo = enum(u8) {
|
|
|
|
|
A,
|
|
|
|
|
B,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try t.expectEqual(Demo.A, try parseEnumFromStr(Demo, "A"));
|
|
|
|
|
try t.expectEqual(Demo.B, try parseEnumFromStr(Demo, "B"));
|
|
|
|
|
|
2024-11-28 21:39:57 +00:00
|
|
|
try t.expectError(error.CouldNotParse, parseEnumFromStr(Demo, "DefinitelyNot"));
|
2024-11-23 23:26:03 +00:00
|
|
|
}
|