Skip to content

Commit 2ced050

Browse files
authored
Add support for enums and slices in pushAny and toAny
1 parent 0f0f489 commit 2ced050

File tree

2 files changed

+188
-56
lines changed

2 files changed

+188
-56
lines changed

src/lib.zig

Lines changed: 116 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,14 +1651,15 @@ pub const Lua = struct {
16511651
/// For userdata it is the size of the block of memory
16521652
/// For other values the call returns 0
16531653
/// See https://www.lua.org/manual/5.4/manual.html#lua_rawlen
1654-
pub fn rawLen(lua: *Lua, index: i32) switch (lang) {
1655-
.lua52, .lua53 => usize,
1656-
else => Unsigned,
1657-
} {
1658-
return c.lua_rawlen(lua.state, index);
1654+
pub fn rawLen(lua: *Lua, index: i32) usize {
1655+
switch (lang) {
1656+
.lua51, .luau => return @intCast(c.lua_objlen(lua.state, index)),
1657+
else => return @intCast(c.lua_rawlen(lua.state, index)),
1658+
}
16591659
}
16601660

1661-
/// Similar to `Lua.setTable()` but does a raw assignment (without metamethods)
1661+
/// Similar to `Lua.setTable()` but does a raw asskdjfal;sdkfjals;dkfj;dk:q
1662+
/// gnment (without metamethods)
16621663
/// See https://www.lua.org/manual/5.4/manual.html#lua_rawset
16631664
pub fn rawSetTable(lua: *Lua, index: i32) void {
16641665
c.lua_rawset(lua.state, index);
@@ -3026,6 +3027,49 @@ pub const Lua = struct {
30263027
_ = c.luaopen_bit32(lua.state);
30273028
}
30283029

3030+
/// Returns if given typeinfo is a string type
3031+
fn isTypeString(typeinfo: std.builtin.Type.Pointer) bool {
3032+
const childinfo = @typeInfo(typeinfo.child);
3033+
if (typeinfo.child == u8 and typeinfo.size != .One) {
3034+
return true;
3035+
} else if (typeinfo.size == .One and childinfo == .Array and childinfo.Array.child == u8) {
3036+
return true;
3037+
}
3038+
return false;
3039+
}
3040+
3041+
/// Pushes any string type
3042+
fn pushAnyString(lua: *Lua, value: anytype) !void {
3043+
const info = @typeInfo(@TypeOf(value)).Pointer;
3044+
switch (info.size) {
3045+
.One => {
3046+
const childinfo = @typeInfo(info.child).Array;
3047+
std.debug.assert(childinfo.child == u8);
3048+
std.debug.assert(childinfo.sentinel != null);
3049+
3050+
const casted: *childinfo.child = @ptrCast(@constCast(childinfo.sentinel.?));
3051+
if (casted.* != 0) {
3052+
@compileError("Sentinel of slice must be a null terminator");
3053+
}
3054+
_ = lua.pushString(value);
3055+
},
3056+
.C, .Many, .Slice => {
3057+
std.debug.assert(info.child == u8);
3058+
if (info.sentinel) |sentinel| {
3059+
const casted: *info.child = @ptrCast(@constCast(sentinel));
3060+
if (casted.* != 0) {
3061+
@compileError("Sentinel of slice must be a null terminator");
3062+
}
3063+
_ = lua.pushString(value);
3064+
} else {
3065+
const null_terminated = try lua.allocator().dupeZ(u8, value);
3066+
defer lua.allocator().free(null_terminated);
3067+
_ = lua.pushString(null_terminated);
3068+
}
3069+
},
3070+
}
3071+
}
3072+
30293073
/// Pushes any valid zig value onto the stack,
30303074
/// Works with ints, floats, booleans, structs,
30313075
/// optionals, and strings
@@ -3038,42 +3082,41 @@ pub const Lua = struct {
30383082
lua.pushNumber(@floatCast(value));
30393083
},
30403084
.Pointer => |info| {
3041-
switch (info.size) {
3085+
if (comptime isTypeString(info)) {
3086+
try lua.pushAnyString(value);
3087+
} else switch (info.size) {
30423088
.One => {
3043-
if (@typeInfo(info.child) == .Array) {
3044-
if (@typeInfo(info.child).Array.child != u8) {
3045-
@compileError("only u8 arrays can be pushed");
3046-
}
3047-
_ = lua.pushString(&(value.*));
3048-
} else {
3049-
if (info.is_const) {
3050-
@compileLog(value);
3051-
@compileError("Pointer must not be const");
3052-
}
3053-
lua.pushLightUserdata(@ptrCast(value));
3089+
if (info.is_const) {
3090+
@compileLog(value);
3091+
@compileLog("Lua cannot guarantee that references will not be modified");
3092+
@compileError("Pointer must not be const");
30543093
}
3094+
lua.pushLightUserdata(@ptrCast(value));
30553095
},
30563096
.C, .Many, .Slice => {
3057-
if (info.child != u8) {
3058-
@compileError("Only u8 slices (strings) are valid slice types");
3059-
}
3060-
if (info.sentinel) |sentinel| {
3061-
const casted: *info.child = @ptrCast(@constCast(sentinel));
3062-
if (casted.* != 0) {
3063-
@compileError("Sentinel of slice must be a null terminator");
3064-
}
3065-
_ = lua.pushString(value);
3066-
} else {
3067-
const null_terminated = try lua.allocator().dupeZ(u8, value);
3068-
defer lua.allocator().free(null_terminated);
3069-
_ = lua.pushString(null_terminated);
3097+
lua.createTable(0, 0);
3098+
for (value, 0..) |index_value, i| {
3099+
try lua.pushAny(i);
3100+
try lua.pushAny(index_value);
3101+
lua.setTable(-3);
30703102
}
30713103
},
30723104
}
30733105
},
3106+
.Array => {
3107+
lua.createTable(0, 0);
3108+
for (value, 0..) |index_value, i| {
3109+
try lua.pushAny(i);
3110+
try lua.pushAny(index_value);
3111+
lua.setTable(-3);
3112+
}
3113+
},
30743114
.Bool => {
30753115
lua.pushBoolean(value);
30763116
},
3117+
.Enum => {
3118+
_ = lua.pushString(@tagName(value));
3119+
},
30773120
.Optional, .Null => {
30783121
if (value == null) {
30793122
lua.pushNil();
@@ -3103,8 +3146,6 @@ pub const Lua = struct {
31033146
/// Converts the specified index of the lua stack to the specified
31043147
/// type if possible and returns it
31053148
pub fn toAny(lua: *Lua, comptime T: type, index: i32) !T {
3106-
3107-
//TODO implement enums
31083149
switch (@typeInfo(T)) {
31093150
.Int => {
31103151
switch (comptime lang) {
@@ -3130,32 +3171,37 @@ pub const Lua = struct {
31303171
},
31313172
}
31323173
},
3133-
.Pointer => |param_info| {
3134-
switch (param_info.size) {
3174+
.Pointer => |info| {
3175+
if (comptime isTypeString(info)) {
3176+
const string: [*:0]const u8 = try lua.toString(index);
3177+
const end = std.mem.indexOfSentinel(u8, 0, string);
3178+
3179+
if (info.sentinel == null) {
3180+
return string[0..end];
3181+
} else {
3182+
return string[0..end :0];
3183+
}
3184+
} else switch (info.size) {
31353185
.Slice, .Many => {
3136-
if (param_info.child != u8) {
3137-
@compileError("Only u8 arrays (strings) may be parameters");
3138-
}
3139-
if (!param_info.is_const) {
3140-
@compileError("Slice must be a const slice");
3141-
}
3142-
const string: [*:0]const u8 = try lua.toString(index);
3143-
const end = std.mem.indexOfSentinel(u8, 0, string);
3144-
3145-
if (param_info.sentinel == null) {
3146-
return string[0..end];
3147-
} else {
3148-
return string[0..end :0];
3149-
}
3186+
return try lua.toSlice(info.child, index);
31503187
},
31513188
else => {
3152-
return try lua.toUserdata(param_info.child, index);
3189+
return try lua.toUserdata(info.child, index);
31533190
},
31543191
}
31553192
},
31563193
.Bool => {
31573194
return lua.toBoolean(index);
31583195
},
3196+
.Enum => |info| {
3197+
const string = try lua.toAny([]const u8, index);
3198+
inline for (info.fields) |enum_member| {
3199+
if (std.mem.eql(u8, string, enum_member.name)) {
3200+
return @field(T, enum_member.name);
3201+
}
3202+
}
3203+
return error.InvalidEnumTagName;
3204+
},
31593205
.Struct => {
31603206
return try lua.toStruct(T, index);
31613207
},
@@ -3173,6 +3219,26 @@ pub const Lua = struct {
31733219
}
31743220
}
31753221

3222+
/// Converts a lua array to a zig slice, memory is owned by the caller
3223+
fn toSlice(lua: *Lua, comptime ChildType: type, raw_index: i32) ![]ChildType {
3224+
const index = lua.absIndex(raw_index);
3225+
3226+
if (!lua.isTable(index)) {
3227+
return error.ValueNotATable;
3228+
}
3229+
3230+
const size = lua.rawLen(index);
3231+
var result = try lua.allocator().alloc(ChildType, size);
3232+
3233+
for (1..size + 1) |i| {
3234+
_ = try lua.pushAny(i);
3235+
_ = lua.getTable(index);
3236+
result[i - 1] = try lua.toAny(ChildType, -1);
3237+
}
3238+
3239+
return result;
3240+
}
3241+
31763242
/// Converts value at given index to a zig struct if possible
31773243
fn toStruct(lua: *Lua, comptime T: type, raw_index: i32) !T {
31783244
const index = lua.absIndex(raw_index);

src/tests.zig

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,6 +2447,12 @@ test "toAny" {
24472447
lua.pushNil();
24482448
const maybe = try lua.toAny(?i32, -1);
24492449
try testing.expect(maybe == null);
2450+
2451+
//enum
2452+
const MyEnumType = enum { hello, goodbye };
2453+
_ = lua.pushString("hello");
2454+
const my_enum = try lua.toAny(MyEnumType, -1);
2455+
try testing.expect(my_enum == MyEnumType.hello);
24502456
}
24512457

24522458
test "toAny struct" {
@@ -2495,6 +2501,23 @@ test "toAny struct recursive" {
24952501
_ = my_struct;
24962502
}
24972503

2504+
test "toAny slice" {
2505+
var lua = try Lua.init(&testing.allocator);
2506+
defer lua.deinit();
2507+
2508+
const program =
2509+
\\list = {1, 2, 3, 4, 5}
2510+
;
2511+
try lua.doString(program);
2512+
_ = try lua.getGlobal("list");
2513+
const sliced = try lua.toAny([]u32, -1);
2514+
defer lua.allocator().free(sliced);
2515+
2516+
try testing.expect(
2517+
std.mem.eql(u32, &[_]u32{ 1, 2, 3, 4, 5 }, sliced),
2518+
);
2519+
}
2520+
24982521
test "pushAny" {
24992522
var lua = try Lua.init(&testing.allocator);
25002523
defer lua.deinit();
@@ -2528,6 +2551,12 @@ test "pushAny" {
25282551
const my_optional: ?i32 = -1;
25292552
try lua.pushAny(my_optional);
25302553
try testing.expect(try lua.toAny(?i32, -1) == my_optional);
2554+
2555+
//enum
2556+
const MyEnumType = enum { hello, goodbye };
2557+
try lua.pushAny(MyEnumType.goodbye);
2558+
const my_enum = try lua.toAny(MyEnumType, -1);
2559+
try testing.expect(my_enum == MyEnumType.goodbye);
25312560
}
25322561

25332562
test "pushAny struct" {
@@ -2546,6 +2575,16 @@ test "pushAny struct" {
25462575
try testing.expect(value.bar == (MyType{}).bar);
25472576
}
25482577

2578+
test "pushAny slice/array" {
2579+
var lua = try Lua.init(&testing.allocator);
2580+
defer lua.deinit();
2581+
2582+
var my_array = [_]u32{ 1, 2, 3, 4, 5 };
2583+
const my_slice: []u32 = my_array[0..];
2584+
try lua.pushAny(my_slice);
2585+
try lua.pushAny(my_array);
2586+
}
2587+
25492588
fn foo(a: i32, b: i32) i32 {
25502589
return a + b;
25512590
}
@@ -2566,14 +2605,25 @@ test "autoPushFunction" {
25662605
lua.autoPushFunction(bar);
25672606
lua.setGlobal("bar");
25682607

2569-
try lua.doString("result = foo(1, 2)");
2570-
2571-
const program =
2608+
try lua.doString(
2609+
\\result = foo(1, 2)
2610+
);
2611+
try lua.doString(
25722612
\\local status, result = pcall(bar, 1, 2)
2573-
;
2574-
lua.doString(program) catch |err| {
2575-
std.debug.print("{!}\n\n", .{err});
2613+
);
2614+
2615+
//automatic api construction
2616+
const my_api = .{
2617+
.foo = foo,
2618+
.bar = bar,
25762619
};
2620+
2621+
try lua.pushAny(my_api);
2622+
lua.setGlobal("api");
2623+
2624+
try lua.doString(
2625+
\\api.foo(1, 2)
2626+
);
25772627
}
25782628

25792629
test "autoCall" {
@@ -2604,3 +2654,19 @@ test "get set" {
26042654
try lua.set("foo", 'a');
26052655
try testing.expect(try lua.get(u8, "foo") == 'a');
26062656
}
2657+
2658+
test "array of strings" {
2659+
var lua = try Lua.init(&testing.allocator);
2660+
defer lua.deinit();
2661+
2662+
const program =
2663+
\\function strings()
2664+
\\ return {"hello", "world", "my name", "is foobar"}
2665+
\\end
2666+
;
2667+
2668+
try lua.doString(program);
2669+
2670+
const strings = try lua.autoCall([]const []const u8, "strings", .{});
2671+
lua.allocator().free(strings);
2672+
}

0 commit comments

Comments
 (0)