feat(config): migrate to a simpler config file format
This commit is contained in:
parent
4f5eb15392
commit
caeb3fc338
2 changed files with 94 additions and 14 deletions
104
src/main.zig
104
src/main.zig
|
@ -407,17 +407,48 @@ pub fn read_config(T: type, allocator: std.mem.Allocator) struct { T, [][]const
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][]const u8, file_name: []const u8) void {
|
fn read_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][]const u8, file_name: []const u8) void {
|
||||||
read_json_config_file(T, allocator, conf, bufs, file_name) catch
|
read_text_config_file(T, allocator, conf, bufs, file_name[0 .. file_name.len - 5]) catch |e| switch (e) {
|
||||||
std.log.err("error reading config file '{s}'", .{file_name});
|
error.FileNotFound => read_json_config_file(T, allocator, conf, bufs, file_name) catch |e_| switch (e_) {
|
||||||
return;
|
error.FileNotFound => return,
|
||||||
|
else => std.log.err("error reading config file '{s}': {s}", .{ file_name, @errorName(e_) }),
|
||||||
|
},
|
||||||
|
else => std.log.err("error reading config file '{s}': {s}", .{ file_name[0 .. file_name.len - 5], @errorName(e) }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8) !void {
|
||||||
|
const cbor = @import("cbor");
|
||||||
|
var file = try std.fs.openFileAbsolute(file_name, .{ .mode = .read_only });
|
||||||
|
defer file.close();
|
||||||
|
const text = try file.readToEndAlloc(allocator, 64 * 1024);
|
||||||
|
defer allocator.free(text);
|
||||||
|
var cbor_buf = std.ArrayList(u8).init(allocator);
|
||||||
|
defer cbor_buf.deinit();
|
||||||
|
const writer = cbor_buf.writer();
|
||||||
|
var it = std.mem.splitScalar(u8, text, '\n');
|
||||||
|
while (it.next()) |line| if (line.len > 0 and line[0] != '#') {
|
||||||
|
const sep = std.mem.indexOfScalar(u8, line, ' ') orelse {
|
||||||
|
std.log.err("invalid line in config '{s}'", .{line});
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
const cb = cbor.fromJsonAlloc(allocator, line[sep..]) catch {
|
||||||
|
std.log.err("invalid value in config line '{s}'", .{line});
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
defer allocator.free(cb);
|
||||||
|
try cbor.writeValue(writer, line[0..sep]);
|
||||||
|
try cbor_buf.appendSlice(cb);
|
||||||
|
};
|
||||||
|
const cb = try cbor_buf.toOwnedSlice();
|
||||||
|
var bufs = std.ArrayListUnmanaged([]const u8).fromOwnedSlice(bufs_.*);
|
||||||
|
bufs.append(allocator, cb) catch @panic("OOM:read_text_config_file");
|
||||||
|
bufs_.* = bufs.toOwnedSlice(allocator) catch @panic("OOM:read_text_config_file");
|
||||||
|
return read_cbor_config(T, conf, file_name, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_json_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8) !void {
|
fn read_json_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8) !void {
|
||||||
const cbor = @import("cbor");
|
const cbor = @import("cbor");
|
||||||
var file = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch |e| switch (e) {
|
var file = try std.fs.openFileAbsolute(file_name, .{ .mode = .read_only });
|
||||||
error.FileNotFound => return,
|
|
||||||
else => return e,
|
|
||||||
};
|
|
||||||
defer file.close();
|
defer file.close();
|
||||||
const json = try file.readToEndAlloc(allocator, 64 * 1024);
|
const json = try file.readToEndAlloc(allocator, 64 * 1024);
|
||||||
defer allocator.free(json);
|
defer allocator.free(json);
|
||||||
|
@ -427,11 +458,24 @@ fn read_json_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_:
|
||||||
bufs_.* = bufs.toOwnedSlice(allocator) catch @panic("OOM:read_json_config_file");
|
bufs_.* = bufs.toOwnedSlice(allocator) catch @panic("OOM:read_json_config_file");
|
||||||
const cb = try cbor.fromJson(json, cbor_buf);
|
const cb = try cbor.fromJson(json, cbor_buf);
|
||||||
var iter = cb;
|
var iter = cb;
|
||||||
var len = try cbor.decodeMapHeader(&iter);
|
_ = try cbor.decodeMapHeader(&iter);
|
||||||
while (len > 0) : (len -= 1) {
|
return read_cbor_config(T, conf, file_name, iter);
|
||||||
var field_name: []const u8 = undefined;
|
}
|
||||||
|
|
||||||
|
fn read_cbor_config(
|
||||||
|
T: type,
|
||||||
|
conf: *T,
|
||||||
|
file_name: []const u8,
|
||||||
|
cb: []const u8,
|
||||||
|
) !void {
|
||||||
|
const cbor = @import("cbor");
|
||||||
|
var iter = cb;
|
||||||
|
var field_name: []const u8 = undefined;
|
||||||
|
while (cbor.matchString(&iter, &field_name) catch |e| switch (e) {
|
||||||
|
error.TooShort => return,
|
||||||
|
else => return e,
|
||||||
|
}) {
|
||||||
var known = false;
|
var known = false;
|
||||||
if (!(try cbor.matchString(&iter, &field_name))) return error.InvalidConfig;
|
|
||||||
inline for (@typeInfo(T).Struct.fields) |field_info|
|
inline for (@typeInfo(T).Struct.fields) |field_info|
|
||||||
if (comptime std.mem.eql(u8, "include_files", field_info.name)) {
|
if (comptime std.mem.eql(u8, "include_files", field_info.name)) {
|
||||||
if (std.mem.eql(u8, field_name, field_info.name)) {
|
if (std.mem.eql(u8, field_name, field_info.name)) {
|
||||||
|
@ -474,7 +518,43 @@ fn read_nested_include_files(T: type, allocator: std.mem.Allocator, conf: *T, bu
|
||||||
pub fn write_config(conf: anytype, allocator: std.mem.Allocator) !void {
|
pub fn write_config(conf: anytype, allocator: std.mem.Allocator) !void {
|
||||||
config_mutex.lock();
|
config_mutex.lock();
|
||||||
defer config_mutex.unlock();
|
defer config_mutex.unlock();
|
||||||
return write_json_file(@TypeOf(conf), conf, allocator, try get_app_config_file_name(application_name, @typeName(@TypeOf(conf))));
|
_ = allocator;
|
||||||
|
const file_name = try get_app_config_file_name(application_name, @typeName(@TypeOf(conf)));
|
||||||
|
return write_text_config_file(@TypeOf(conf), conf, file_name[0 .. file_name.len - 5]);
|
||||||
|
// return write_json_file(@TypeOf(conf), conf, allocator, try get_app_config_file_name(application_name, @typeName(@TypeOf(conf))));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_text_config_file(comptime T: type, data: T, file_name: []const u8) !void {
|
||||||
|
const default: T = .{};
|
||||||
|
var file = try std.fs.createFileAbsolute(file_name, .{ .truncate = true });
|
||||||
|
defer file.close();
|
||||||
|
const writer = file.writer();
|
||||||
|
inline for (@typeInfo(T).Struct.fields) |field_info| {
|
||||||
|
if (config_eql(
|
||||||
|
field_info.type,
|
||||||
|
@field(data, field_info.name),
|
||||||
|
@field(default, field_info.name),
|
||||||
|
)) {
|
||||||
|
try writer.print("# {s} ", .{field_info.name});
|
||||||
|
} else {
|
||||||
|
try writer.print("{s} ", .{field_info.name});
|
||||||
|
}
|
||||||
|
var s = std.json.writeStream(writer, .{ .whitespace = .indent_4 });
|
||||||
|
try s.write(@field(data, field_info.name));
|
||||||
|
try writer.print("\n", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_eql(comptime T: type, a: T, b: T) bool {
|
||||||
|
switch (T) {
|
||||||
|
[]const u8 => return std.mem.eql(u8, a, b),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
switch (@typeInfo(T)) {
|
||||||
|
.Bool, .Int, .Float => return a == b,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
@compileError("unsupported config type " ++ @typeName(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_json_file(comptime T: type, data: T, allocator: std.mem.Allocator, file_name: []const u8) !void {
|
fn write_json_file(comptime T: type, data: T, allocator: std.mem.Allocator, file_name: []const u8) !void {
|
||||||
|
|
|
@ -379,13 +379,13 @@ const cmds = struct {
|
||||||
|
|
||||||
pub fn open_config(_: *Self, _: Ctx) Result {
|
pub fn open_config(_: *Self, _: Ctx) Result {
|
||||||
const file_name = try root.get_config_file_name(@import("config"));
|
const file_name = try root.get_config_file_name(@import("config"));
|
||||||
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } });
|
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name[0 .. file_name.len - 5] } });
|
||||||
}
|
}
|
||||||
pub const open_config_meta = .{ .description = "Edit configuration file" };
|
pub const open_config_meta = .{ .description = "Edit configuration file" };
|
||||||
|
|
||||||
pub fn open_gui_config(_: *Self, _: Ctx) Result {
|
pub fn open_gui_config(_: *Self, _: Ctx) Result {
|
||||||
const file_name = try root.get_config_file_name(@import("gui_config"));
|
const file_name = try root.get_config_file_name(@import("gui_config"));
|
||||||
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } });
|
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name[0 .. file_name.len - 5] } });
|
||||||
}
|
}
|
||||||
pub const open_gui_config_meta = .{ .description = "Edit gui configuration file" };
|
pub const open_gui_config_meta = .{ .description = "Edit gui configuration file" };
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue