fix(LuaParser): use an arena allocator for config fields

The config parsing can error at any time.
Using an arena allocator, only the string fields that were actually
allocated are freed.
This commit is contained in:
Titanium Brain 2026-07-11 09:55:59 +01:00
commit 200d210a8e

View file

@ -122,13 +122,39 @@ pub fn LuaParser(comptime Struct: type) type {
errors: std.ArrayList(Error),
maybe_load_error: ?anyerror,
allocator: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
pub fn init(
allocator: std.mem.Allocator,
path: []const u8,
) !Self {
var arena = std.heap.ArenaAllocator.init(allocator);
const arena_alloc = arena.allocator();
var maybe_load_error: ?anyerror = null;
errdefer |err| maybe_load_error = err;
const data = parseLua(arena_alloc, path) catch load_error: {
break :load_error Struct{};
};
if (global_errors.items.len != 0) {
maybe_load_error = error.InvalidConfig;
}
return .{
.structure = data,
.errors = global_errors,
.maybe_load_error = maybe_load_error,
.allocator = allocator,
.arena = arena,
};
}
fn parseLua(
allocator: std.mem.Allocator,
path: []const u8,
) !Struct {
var lua: *Lua = try .init(allocator);
defer lua.deinit();
@ -152,19 +178,10 @@ pub fn LuaParser(comptime Struct: type) type {
},
else => @compileError("Expected a struct."),
}
if (global_errors.items.len != 0) {
maybe_load_error = error.InvalidConfig;
}
// Parse custom binds and labels
try parseCustom(lua);
return .{
.structure = data,
.errors = global_errors,
.maybe_load_error = maybe_load_error,
.allocator = allocator,
};
return data;
}
pub fn setField(allocator: std.mem.Allocator, lua: *Lua, comptime field: std.builtin.Type.StructField, data: *Struct) !void {
@ -187,7 +204,7 @@ pub fn LuaParser(comptime Struct: type) type {
// handle missing required fields
if (lua.isNil(-1)) {
return error.MissingRequiredField;
return;
}
const actual_type_info = @typeInfo(actual_type);
@ -342,19 +359,7 @@ pub fn LuaParser(comptime Struct: type) type {
}
pub fn deinit(self: *Self) void {
inline for (@typeInfo(Struct).@"struct".fields) |field| {
const type_info = @typeInfo(field.type);
const actual_type = if (type_info == .optional) type_info.optional.child else 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);
}
}
}
self.arena.deinit();
for (0..global_errors.items.len) |i| {
const err = global_errors.items[i];
temporary_allocator.free(err.type_name);