--config-file to load a config file

The config file is just CLI args one per line.
This commit is contained in:
Mitchell Hashimoto
2022-08-01 18:04:39 -07:00
parent bf526fb217
commit 782ddfe722
3 changed files with 69 additions and 0 deletions

View File

@ -276,3 +276,54 @@ test "parseIntoField: struct with parse func" {
try parseIntoField(@TypeOf(data), alloc, &data, "a", "42");
try testing.expectEqual(@as([]const u8, "HELLO!"), data.a.v);
}
/// Returns an interator (implements "next") that reads CLI args by line.
/// Each CLI arg is expected to be a single line. This is used to implement
/// configuration files.
pub fn LineIterator(comptime ReaderType: type) type {
return struct {
const Self = @This();
/// The maximum size a single line can be. We don't expect any
/// CLI arg to exceed this size. Can't wait to git blame this in
/// like 4 years and be wrong about this.
pub const MAX_LINE_SIZE = 4096;
r: ReaderType,
entry: [MAX_LINE_SIZE]u8 = [_]u8{ '-', '-' } ++ ([_]u8{0} ** (MAX_LINE_SIZE - 2)),
pub fn next(self: *Self) ?[]const u8 {
// TODO: detect "--" prefixed lines and give a friendlier error
const buf = self.r.readUntilDelimiterOrEof(self.entry[2..], '\n') catch {
// TODO: handle errors
unreachable;
} orelse return null;
// We need to reslice so that we include our '--' at the beginning
// of our buffer so that we can trick the CLI parser to treat it
// as CLI args.
return self.entry[0 .. buf.len + 2];
}
};
}
// Constructs a LineIterator (see docs for that).
pub fn lineIterator(reader: anytype) LineIterator(@TypeOf(reader)) {
return .{ .r = reader };
}
test "LineIterator" {
const testing = std.testing;
var fbs = std.io.fixedBufferStream(
\\A
\\B
\\C
);
var iter = lineIterator(fbs.reader());
try testing.expectEqualStrings("--A", iter.next().?);
try testing.expectEqualStrings("--B", iter.next().?);
try testing.expectEqualStrings("--C", iter.next().?);
try testing.expectEqual(@as(?[]const u8, null), iter.next());
try testing.expectEqual(@as(?[]const u8, null), iter.next());
}

View File

@ -2,6 +2,8 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
/// Config is the main config struct. These fields map directly to the
/// CLI flag names hence we use a lot of `@""` syntax to support hyphens.
pub const Config = struct {
/// Font size
@"font-size": u8 = 14,

View File

@ -43,6 +43,22 @@ pub fn main() !void {
break :config result;
};
defer config.deinit();
// Parse the config files
// TODO(mitchellh): support nesting (config-file in a config file)
// TODO(mitchellh): detect cycles when nesting
if (config.@"config-file".list.items.len > 0) {
const cwd = std.fs.cwd();
for (config.@"config-file".list.items) |path| {
var file = try cwd.openFile(path, .{});
defer file.close();
var buf_reader = std.io.bufferedReader(file.reader());
var iter = cli_args.lineIterator(buf_reader.reader());
try cli_args.parse(Config, alloc, &config, &iter);
}
}
log.info("config={}", .{config});
// We want to log all our errors