summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMitchell Hashimoto <mitchell.hashimoto@gmail.com>2024-01-20 12:43:15 -0800
committerMitchell Hashimoto <mitchell.hashimoto@gmail.com>2024-01-20 12:43:15 -0800
commit33c4c328b661a1c0ef85d75be9fcb7af0455cec3 (patch)
tree896afd7ccbab3f16670f601b3398dedb2ef93d2f /src
parentf9996d46eca2956041dedecc543253bc541cef03 (diff)
config: file formatter
Diffstat (limited to 'src')
-rw-r--r--src/config.zig1
-rw-r--r--src/config/formatter.zig91
2 files changed, 92 insertions, 0 deletions
diff --git a/src/config.zig b/src/config.zig
index 78e033361..73c014a01 100644
--- a/src/config.zig
+++ b/src/config.zig
@@ -1,6 +1,7 @@
const builtin = @import("builtin");
pub usingnamespace @import("config/key.zig");
+pub usingnamespace @import("config/formatter.zig");
pub const Config = @import("config/Config.zig");
pub const string = @import("config/string.zig");
pub const edit = @import("config/edit.zig");
diff --git a/src/config/formatter.zig b/src/config/formatter.zig
new file mode 100644
index 000000000..ab5a8667f
--- /dev/null
+++ b/src/config/formatter.zig
@@ -0,0 +1,91 @@
+const std = @import("std");
+const Config = @import("Config.zig");
+
+/// FileFormatter is a formatter implementation that outputs the
+/// config in a file-like format. This uses more generous whitespace,
+/// can include comments, etc.
+pub const FileFormatter = struct {
+ config: *const Config,
+
+ /// Implements std.fmt so it can be used directly with std.fmt.
+ pub fn format(
+ self: FileFormatter,
+ comptime layout: []const u8,
+ opts: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = layout;
+ _ = opts;
+
+ inline for (@typeInfo(Config).Struct.fields) |field| {
+ if (field.name[0] == '_') continue;
+ try self.formatField(
+ field.type,
+ field.name,
+ @field(self.config, field.name),
+ writer,
+ );
+ }
+ }
+
+ fn formatField(
+ self: FileFormatter,
+ comptime T: type,
+ name: []const u8,
+ value: T,
+ writer: anytype,
+ ) !void {
+ switch (@typeInfo(T)) {
+ .Bool, .Int => {
+ try writer.print("{s} = {}\n", .{ name, value });
+ return;
+ },
+
+ .Float => {
+ try writer.print("{s} = {d}\n", .{ name, value });
+ return;
+ },
+
+ .Optional => |info| if (value) |inner| {
+ try self.formatField(
+ info.child,
+ name,
+ inner,
+ writer,
+ );
+ } else {
+ try writer.print("{s} = \n", .{name});
+ },
+
+ .Pointer => switch (T) {
+ []const u8,
+ [:0]const u8,
+ => {
+ try writer.print("{s} = {s}\n", .{ name, value });
+ },
+
+ else => {},
+ },
+
+ else => {},
+ }
+
+ // TODO: make a compiler error so we can detect when
+ // we don't support a type.
+ }
+};
+
+test "format default config" {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+ var cfg = try Config.default(alloc);
+ defer cfg.deinit();
+
+ var buf = std.ArrayList(u8).init(alloc);
+ defer buf.deinit();
+
+ const fmt: FileFormatter = .{ .config = &cfg };
+ try std.fmt.format(buf.writer(), "{}", .{fmt});
+
+ std.log.warn("{s}", .{buf.items});
+}