const std = @import("std"); const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const Config = @This(); pub const Wrapper = struct { arena: ArenaAllocator, config: Config, pub fn deinit(self: Wrapper) void { self.arena.deinit(); } pub fn merge(self: *Wrapper, filename: []const u8) !void { const file = try std.fs.cwd().openFile(filename, .{}); defer file.close(); const allocator = self.arena.allocator(); var reader = std.json.reader(allocator, file.reader()); defer reader.deinit(); const new_config = try std.json.parseFromTokenSourceLeaky( Nullable, allocator, &reader, .{ .duplicate_field_behavior = .use_last, .ignore_unknown_fields = true, .allocate = .alloc_always, }, ); inline for (std.meta.fields(Config)) |field| { if (@field(new_config, field.name)) |value| { @field(self.config, field.name) = value; } } } }; bot_name: []const u8, bot_token: []const u8, db_path: [:0]const u8, dev_group: i64, owner: i64, pub fn load(parent_allocator: Allocator, filename: []const u8) !Wrapper { const file = try std.fs.cwd().openFile(filename, .{}); defer file.close(); var arena = ArenaAllocator.init(parent_allocator); errdefer arena.deinit(); const allocator = arena.allocator(); var reader = std.json.reader(allocator, file.reader()); defer reader.deinit(); const config = try std.json.parseFromTokenSourceLeaky( Config, allocator, &reader, .{ .duplicate_field_behavior = .use_last, .ignore_unknown_fields = true, .allocate = .alloc_always, }, ); return .{ .arena = arena, .config = config }; } const Nullable = blk: { const template = @typeInfo(Config); var fields: [template.@"struct".fields.len]std.builtin.Type.StructField = undefined; for (template.@"struct".fields, 0..) |template_field, field_idx| { fields[field_idx] = template_field; fields[field_idx].type = ?template_field.type; } var new = template; new.@"struct".fields = &fields; new.@"struct".decls = &.{}; break :blk @Type(new); };