summaryrefslogtreecommitdiff
path: root/src/Config.zig
blob: 60f25adeba4c135aa9b27b0a9a9e95cae6754f48 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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);
};