config: ability to format all field types except tagged unions

This commit is contained in:
Mitchell Hashimoto
2024-01-20 14:41:49 -08:00
parent 9369baac60
commit 32a1c6ec06
2 changed files with 219 additions and 12 deletions

View File

@ -2178,6 +2178,19 @@ pub const Color = packed struct(u24) {
return std.meta.eql(self, other); return std.meta.eql(self, other);
} }
/// Used by Formatter
pub fn formatEntry(self: Color, formatter: anytype) !void {
var buf: [128]u8 = undefined;
try formatter.formatEntry(
[]const u8,
std.fmt.bufPrint(
&buf,
"#{x:0>2}{x:0>2}{x:0>2}",
.{ self.r, self.g, self.b },
) catch return error.OutOfMemory,
);
}
/// fromHex parses a color from a hex value such as #RRGGBB. The "#" /// fromHex parses a color from a hex value such as #RRGGBB. The "#"
/// is optional. /// is optional.
pub fn fromHex(input: []const u8) !Color { pub fn fromHex(input: []const u8) !Color {
@ -2251,6 +2264,21 @@ pub const Palette = struct {
return std.meta.eql(self, other); return std.meta.eql(self, other);
} }
/// Used by Formatter
pub fn formatEntry(self: Self, formatter: anytype) !void {
var buf: [128]u8 = undefined;
for (0.., self.value) |k, v| {
try formatter.formatEntry(
[]const u8,
std.fmt.bufPrint(
&buf,
"{d}=#{x:0>2}{x:0>2}{x:0>2}",
.{ k, v.r, v.g, v.b },
) catch return error.OutOfMemory,
);
}
}
test "parseCLI" { test "parseCLI" {
const testing = std.testing; const testing = std.testing;
@ -2314,6 +2342,19 @@ pub const RepeatableString = struct {
} else return true; } else return true;
} }
/// Used by Formatter
pub fn formatEntry(self: Self, formatter: anytype) !void {
// If no items, we want to render an empty field.
if (self.list.items.len == 0) {
try formatter.formatEntry(void, {});
return;
}
for (self.list.items) |value| {
try formatter.formatEntry([]const u8, value);
}
}
test "parseCLI" { test "parseCLI" {
const testing = std.testing; const testing = std.testing;
var arena = ArenaAllocator.init(testing.allocator); var arena = ArenaAllocator.init(testing.allocator);
@ -2355,6 +2396,11 @@ pub const RepeatablePath = struct {
return self.value.equal(other.value); return self.value.equal(other.value);
} }
/// Used by Formatter
pub fn formatEntry(self: Self, formatter: anytype) !void {
try self.value.formatEntry(formatter);
}
/// Expand all the paths relative to the base directory. /// Expand all the paths relative to the base directory.
pub fn expand( pub fn expand(
self: *Self, self: *Self,
@ -2442,6 +2488,26 @@ pub const RepeatableFontVariation = struct {
} else return true; } else return true;
} }
/// Used by Formatter
pub fn formatEntry(
self: Self,
formatter: anytype,
) !void {
if (self.list.items.len == 0) {
try formatter.formatEntry(void, {});
return;
}
var buf: [128]u8 = undefined;
for (self.list.items) |value| {
const str = std.fmt.bufPrint(&buf, "{s}={d}", .{
value.id.str(),
value.value,
}) catch return error.OutOfMemory;
try formatter.formatEntry([]const u8, str);
}
}
test "parseCLI" { test "parseCLI" {
const testing = std.testing; const testing = std.testing;
var arena = ArenaAllocator.init(testing.allocator); var arena = ArenaAllocator.init(testing.allocator);
@ -2561,6 +2627,29 @@ pub const Keybinds = struct {
return true; return true;
} }
/// Used by Formatter
pub fn formatEntry(self: Keybinds, formatter: anytype) !void {
if (self.set.bindings.size == 0) {
try formatter.formatEntry(void, {});
return;
}
var buf: [1024]u8 = undefined;
var iter = self.set.bindings.iterator();
while (iter.next()) |next| {
const k = next.key_ptr.*;
const v = next.value_ptr.*;
try formatter.formatEntry(
[]const u8,
std.fmt.bufPrint(
&buf,
"{}={}",
.{ k, v },
) catch return error.OutOfMemory,
);
}
}
test "parseCLI" { test "parseCLI" {
const testing = std.testing; const testing = std.testing;
var arena = ArenaAllocator.init(testing.allocator); var arena = ArenaAllocator.init(testing.allocator);
@ -2618,6 +2707,49 @@ pub const RepeatableCodepointMap = struct {
} else return true; } else return true;
} }
/// Used by Formatter
pub fn formatEntry(
self: Self,
formatter: anytype,
) !void {
if (self.map.list.len == 0) {
try formatter.formatEntry(void, {});
return;
}
var buf: [1024]u8 = undefined;
const ranges = self.map.list.items(.range);
const descriptors = self.map.list.items(.descriptor);
for (ranges, descriptors) |range, descriptor| {
if (range[0] == range[1]) {
try formatter.formatEntry(
[]const u8,
std.fmt.bufPrint(
&buf,
"U+{X:0>4}={s}",
.{
range[0],
descriptor.family orelse "",
},
) catch return error.OutOfMemory,
);
} else {
try formatter.formatEntry(
[]const u8,
std.fmt.bufPrint(
&buf,
"U+{X:0>4}-U{X:0>4}={s}",
.{
range[0],
range[1],
descriptor.family orelse "",
},
) catch return error.OutOfMemory,
);
}
}
}
/// Parses the list of Unicode codepoint ranges. Valid syntax: /// Parses the list of Unicode codepoint ranges. Valid syntax:
/// ///
/// "" (empty returns null) /// "" (empty returns null)
@ -2836,6 +2968,13 @@ pub const RepeatableLink = struct {
_ = other; _ = other;
return true; return true;
} }
/// Used by Formatter
pub fn formatEntry(self: Self, formatter: anytype) !void {
// This currently can't be set so we don't format anything.
_ = self;
_ = formatter;
}
}; };
/// Options for copy on select behavior. /// Options for copy on select behavior.

View File

@ -19,7 +19,7 @@ pub const FileFormatter = struct {
inline for (@typeInfo(Config).Struct.fields) |field| { inline for (@typeInfo(Config).Struct.fields) |field| {
if (field.name[0] == '_') continue; if (field.name[0] == '_') continue;
try self.formatField( try self.formatEntry(
field.type, field.type,
field.name, field.name,
@field(self.config, field.name), @field(self.config, field.name),
@ -28,13 +28,32 @@ pub const FileFormatter = struct {
} }
} }
fn formatField( pub fn formatEntry(
self: FileFormatter, self: FileFormatter,
comptime T: type, comptime T: type,
name: []const u8, name: []const u8,
value: T, value: T,
writer: anytype, writer: anytype,
) !void { ) !void {
const EntryFormatter = struct {
parent: *const FileFormatter,
name: []const u8,
writer: @TypeOf(writer),
pub fn formatEntry(
self_entry: @This(),
comptime EntryT: type,
value_entry: EntryT,
) !void {
return self_entry.parent.formatEntry(
EntryT,
self_entry.name,
value_entry,
self_entry.writer,
);
}
};
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.Bool, .Int => { .Bool, .Int => {
try writer.print("{s} = {}\n", .{ name, value }); try writer.print("{s} = {}\n", .{ name, value });
@ -46,15 +65,29 @@ pub const FileFormatter = struct {
return; return;
}, },
.Optional => |info| if (value) |inner| { .Enum => {
try self.formatField( try writer.print("{s} = {s}\n", .{ name, @tagName(value) });
info.child, return;
name, },
inner,
writer, .Void => {
);
} else {
try writer.print("{s} = \n", .{name}); try writer.print("{s} = \n", .{name});
return;
},
.Optional => |info| {
if (value) |inner| {
try self.formatEntry(
info.child,
name,
inner,
writer,
);
} else {
try writer.print("{s} = \n", .{name});
}
return;
}, },
.Pointer => switch (T) { .Pointer => switch (T) {
@ -62,16 +95,51 @@ pub const FileFormatter = struct {
[:0]const u8, [:0]const u8,
=> { => {
try writer.print("{s} = {s}\n", .{ name, value }); try writer.print("{s} = {s}\n", .{ name, value });
return;
}, },
else => {}, else => {},
}, },
// Structs of all types require a "formatEntry" function
// to be defined which will be called to format the value.
// This is given the formatter in use so that they can
// call BACK to our formatEntry to write each primitive
// value.
.Struct => |info| if (@hasDecl(T, "formatEntry")) {
try value.formatEntry(EntryFormatter{
.parent = &self,
.name = name,
.writer = writer,
});
return;
} else switch (info.layout) {
// Packed structs we special case.
.Packed => {
try writer.print("{s} = ", .{name});
inline for (info.fields, 0..) |field, i| {
if (i > 0) try writer.print(",", .{});
try writer.print("{s}{s}", .{
if (!@field(value, field.name)) "no-" else "",
field.name,
});
}
try writer.print("\n", .{});
return;
},
else => {},
},
// TODO
.Union => return,
else => {}, else => {},
} }
// TODO: make a compiler error so we can detect when // Compile error so that we can catch missing cases.
// we don't support a type. @compileLog(T);
@compileError("missing case for type");
} }
}; };