mirror of
https://codeberg.org/fairyglade/ly.git
synced 2026-08-09 07:09:13 +02:00
feat(LuaParser): add custom bind and custom label parsing
Adds support for custom_commands and custom_labels tables in the lua config file. Fixes memory leak. Fixes wrong use of lateConfigHandler.
This commit is contained in:
parent
0b99be00a9
commit
9783da93d1
2 changed files with 130 additions and 6 deletions
|
|
@ -8,6 +8,7 @@ pub const interop = @import("interop.zig");
|
|||
pub const UidRange = @import("UidRange.zig");
|
||||
pub const LogFile = @import("LogFile.zig");
|
||||
pub const SharedError = @import("SharedError.zig");
|
||||
pub const custom = @import("custom.zig");
|
||||
|
||||
pub fn Parser(comptime T: type) type {
|
||||
return union(enum) {
|
||||
|
|
@ -120,6 +121,7 @@ pub fn LuaParser(comptime Struct: type) type {
|
|||
structure: Struct,
|
||||
errors: std.ArrayList(Error),
|
||||
maybe_load_error: ?anyerror,
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
pub fn init(
|
||||
allocator: std.mem.Allocator,
|
||||
|
|
@ -142,6 +144,10 @@ pub fn LuaParser(comptime Struct: type) type {
|
|||
var data: Struct = .{};
|
||||
switch (@typeInfo(Struct)) {
|
||||
.@"struct" => |struc| {
|
||||
const ly_type = lua.getGlobal("ly");
|
||||
defer lua.pop(1); // pop ly table
|
||||
if (ly_type == .nil) return error.MissingLyTable;
|
||||
|
||||
inline for (struc.fields) |field| {
|
||||
setField(allocator, lua, field, &data) catch {};
|
||||
}
|
||||
|
|
@ -151,11 +157,19 @@ pub fn LuaParser(comptime Struct: type) type {
|
|||
if (global_errors.items.len != 0) {
|
||||
maybe_load_error = error.InvalidConfig;
|
||||
}
|
||||
return .{ .structure = data, .errors = global_errors, .maybe_load_error = maybe_load_error };
|
||||
|
||||
// Parse custom binds and labels
|
||||
try parseCustom(lua);
|
||||
|
||||
return .{
|
||||
.structure = data,
|
||||
.errors = global_errors,
|
||||
.maybe_load_error = maybe_load_error,
|
||||
.allocator = allocator,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setField(allocator: std.mem.Allocator, lua: *Lua, comptime field: std.builtin.Type.StructField, data: *Struct) !void {
|
||||
_ = lua.getGlobal("ly");
|
||||
defer lua.pop(1); // pop ly table
|
||||
const type_info = @typeInfo(field.type);
|
||||
const actual_type, const is_optional = blk: {
|
||||
if (type_info == .optional) {
|
||||
|
|
@ -240,8 +254,119 @@ pub fn LuaParser(comptime Struct: type) type {
|
|||
} else unreachable;
|
||||
}
|
||||
|
||||
pub fn parseCustom(lua: *Lua) !void {
|
||||
_ = lua.getGlobal("ly");
|
||||
defer lua.pop(1); // pop ly table
|
||||
if (!lua.isTable(-1)) return error.MissingLyTable;
|
||||
|
||||
_ = lua.getField(-1, "custom_commands");
|
||||
// custom_commands can be omitted or empty, so we just skip instead of erroring
|
||||
binds: {
|
||||
if (lua.isTable(-1)) {
|
||||
const len = @as(usize, @intCast(lua.lenRaiseErr(-1)));
|
||||
if (len == 0) break :binds;
|
||||
|
||||
for (1..len + 1) |i| {
|
||||
// push i-th table to stack
|
||||
const ith_table_type = lua.getIndex(-1, @intCast(i));
|
||||
defer lua.pop(1); // i-th table in custom_commands
|
||||
if (ith_table_type != .table) continue;
|
||||
|
||||
// skip command if binding isn't set or not a string
|
||||
const binding_type = lua.getField(-1, "binding");
|
||||
if (binding_type != .string) continue;
|
||||
const binding = lua.toString(-1) catch continue;
|
||||
const bindingZ = temporary_allocator.dupe(u8, binding) catch "";
|
||||
lua.pop(1); // binding value
|
||||
|
||||
if (!custom.binds.contains(bindingZ)) {
|
||||
custom.binds.put(temporary_allocator, bindingZ, .{}) catch {};
|
||||
}
|
||||
if (custom.binds.getPtr(bindingZ)) |command| {
|
||||
// binding name
|
||||
const name_type = lua.getField(-1, "name");
|
||||
if (name_type != .string) continue;
|
||||
const binding_name = lua.toString(-1) catch "";
|
||||
command.name = temporary_allocator.dupe(u8, binding_name) catch "";
|
||||
lua.pop(1); // name value
|
||||
|
||||
// binding command
|
||||
const cmd_type = lua.getField(-1, "cmd");
|
||||
if (cmd_type != .string) continue;
|
||||
const binding_cmd = lua.toString(-1) catch "";
|
||||
command.cmd = temporary_allocator.dupe(u8, binding_cmd) catch "";
|
||||
lua.pop(1); // cmd value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lua.pop(1);
|
||||
|
||||
_ = lua.getField(-1, "custom_labels");
|
||||
// custom_labels can be omitted, so we just skip instead of erroring
|
||||
labels: {
|
||||
if (lua.isTable(-1)) {
|
||||
const len = @as(usize, @intCast(lua.lenRaiseErr(-1)));
|
||||
if (len == 0) break :labels;
|
||||
|
||||
for (1..len + 1) |i| {
|
||||
// push i-th table to stack
|
||||
const ith_table_type = lua.getIndex(-1, @intCast(i));
|
||||
defer lua.pop(1); // i-th table in custom_labels
|
||||
if (ith_table_type != .table) continue;
|
||||
|
||||
// skip command if binding isn't set or not a string
|
||||
const label_type = lua.getField(-1, "label");
|
||||
if (label_type != .string) continue;
|
||||
const label = lua.toString(-1) catch continue;
|
||||
const labelZ = temporary_allocator.dupe(u8, label) catch "";
|
||||
lua.pop(1); // label value
|
||||
|
||||
if (!custom.labels.contains(labelZ)) {
|
||||
custom.labels.put(temporary_allocator, labelZ, .{ .name = labelZ }) catch {};
|
||||
}
|
||||
if (custom.labels.getPtr(labelZ)) |label_ptr| {
|
||||
// label command
|
||||
const cmd_type = lua.getField(-1, "cmd");
|
||||
if (cmd_type != .string) continue;
|
||||
const label_cmd = lua.toString(-1) catch "";
|
||||
label_ptr.cmd = temporary_allocator.dupe(u8, label_cmd) catch "";
|
||||
lua.pop(1); // cmd value
|
||||
|
||||
// label refresh
|
||||
const name_type = lua.getField(-1, "refresh");
|
||||
if (name_type != .number) continue;
|
||||
const label_refresh: u32 = @intCast(lua.toInteger(-1) catch 0);
|
||||
label_ptr.refresh = label_refresh;
|
||||
lua.pop(1); // name value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lua.pop(1);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
_ = self; // autofix
|
||||
inline for (@typeInfo(Struct).@"struct".fields) |field| {
|
||||
const type_info = @typeInfo(field.type);
|
||||
const actual_type = blk: {
|
||||
if (type_info == .optional) {
|
||||
break :blk type_info.optional.child;
|
||||
}
|
||||
break :blk field.type;
|
||||
};
|
||||
|
||||
const value = @field(self.structure, field.name);
|
||||
if (actual_type == []const u8 or actual_type == [:0]const u8) {
|
||||
if (type_info == .optional) {
|
||||
if (value) |inner| {
|
||||
self.allocator.free(inner);
|
||||
}
|
||||
} else {
|
||||
self.allocator.free(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (0..global_errors.items.len) |i| {
|
||||
const err = global_errors.items[i];
|
||||
temporary_allocator.free(err.type_name);
|
||||
|
|
|
|||
|
|
@ -274,7 +274,6 @@ pub fn main(init: std.process.Init) !void {
|
|||
};
|
||||
|
||||
state.config = config_parser.structure();
|
||||
std.debug.print("=== {}\n", .{state.config.gameoflife_fg});
|
||||
|
||||
var lang_buffer: [16]u8 = undefined;
|
||||
const lang_file = try std.fmt.bufPrint(&lang_buffer, "{s}.ini", .{state.config.lang});
|
||||
|
|
@ -292,7 +291,7 @@ pub fn main(init: std.process.Init) !void {
|
|||
state.old_save_path = try std.Io.Dir.path.join(state.allocator, &[_][]const u8{ config_parent_path, "save.ini" });
|
||||
}
|
||||
|
||||
if (config_parser.maybe_load_error() == null) {
|
||||
if (config_parser.maybe_load_error() == null and config_parser == .ini) {
|
||||
migrator.lateConfigFieldHandler(&state.config);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue