mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 16:26:08 +03:00
config: implement change iterator (one todo)
This commit is contained in:
127
src/config.zig
127
src/config.zig
@ -166,6 +166,30 @@ pub const Config = struct {
|
||||
/// This is set by the CLI parser for deinit.
|
||||
_arena: ?ArenaAllocator = null,
|
||||
|
||||
/// Key is an enum of all the available configuration keys. This is used
|
||||
/// when paired with diff to determine what fields have changed in a config,
|
||||
/// amongst other things.
|
||||
pub const Key = key: {
|
||||
const field_infos = std.meta.fields(Config);
|
||||
var enumFields: [field_infos.len]std.builtin.Type.EnumField = undefined;
|
||||
var decls = [_]std.builtin.Type.Declaration{};
|
||||
inline for (field_infos, 0..) |field, i| {
|
||||
enumFields[i] = .{
|
||||
.name = field.name,
|
||||
.value = i,
|
||||
};
|
||||
}
|
||||
|
||||
break :key @Type(.{
|
||||
.Enum = .{
|
||||
.tag_type = std.math.IntFittingRange(0, field_infos.len - 1),
|
||||
.fields = &enumFields,
|
||||
.decls = &decls,
|
||||
.is_exhaustive = true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
pub fn deinit(self: *Config) void {
|
||||
if (self._arena) |arena| arena.deinit();
|
||||
self.* = undefined;
|
||||
@ -655,6 +679,77 @@ pub const Config = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator that goes through each changed field from
|
||||
/// old to new.
|
||||
pub fn changeIterator(old: *const Config, new: *const Config) ChangeIterator {
|
||||
return .{
|
||||
.old = old,
|
||||
.new = new,
|
||||
};
|
||||
}
|
||||
|
||||
fn equal(comptime T: type, old: T, new: T) bool {
|
||||
// Do known named types first
|
||||
switch (T) {
|
||||
inline []const u8,
|
||||
[:0]const u8,
|
||||
=> return std.mem.eql(u8, old, new),
|
||||
|
||||
else => {},
|
||||
}
|
||||
|
||||
// Back into types of types
|
||||
switch (@typeInfo(T)) {
|
||||
inline .Bool,
|
||||
.Int,
|
||||
=> return old == new,
|
||||
|
||||
.Optional => |info| {
|
||||
if (old == null and new == null) return true;
|
||||
if (old == null or new == null) return false;
|
||||
return equal(info.child, old.?, new.?);
|
||||
},
|
||||
|
||||
.Struct => return old.equal(new),
|
||||
|
||||
else => {
|
||||
@compileLog(T);
|
||||
@compileError("unsupported field type");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub const ChangeIterator = struct {
|
||||
old: *const Config,
|
||||
new: *const Config,
|
||||
i: usize = 0,
|
||||
|
||||
pub fn next(self: *ChangeIterator) ?Key {
|
||||
const fields = comptime std.meta.fields(Config);
|
||||
|
||||
while (self.i < fields.len) {
|
||||
switch (self.i) {
|
||||
inline 0...(fields.len - 1) => |i| {
|
||||
const field = fields[i];
|
||||
self.i += 1;
|
||||
|
||||
if (field.name[0] == '_') return self.next();
|
||||
|
||||
const old_value = @field(self.old, field.name);
|
||||
const new_value = @field(self.new, field.name);
|
||||
if (!equal(field.type, old_value, new_value)) {
|
||||
return @field(Key, field.name);
|
||||
}
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
test "clone default" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -664,6 +759,10 @@ pub const Config = struct {
|
||||
var dest = try source.clone(alloc);
|
||||
defer dest.deinit();
|
||||
|
||||
// Should have no changes
|
||||
var it = source.changeIterator(&dest);
|
||||
try testing.expectEqual(@as(?Key, null), it.next());
|
||||
|
||||
// I want to do this but this doesn't work (the API doesn't work)
|
||||
// try testing.expectEqualDeep(dest, source);
|
||||
}
|
||||
@ -693,6 +792,11 @@ pub const Color = struct {
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Compare if two of our value are requal. Required by Config.
|
||||
pub fn equal(self: Color, other: Color) bool {
|
||||
return std.meta.eql(self, other);
|
||||
}
|
||||
|
||||
/// fromHex parses a color from a hex value such as #RRGGBB. The "#"
|
||||
/// is optional.
|
||||
pub fn fromHex(input: []const u8) !Color {
|
||||
@ -761,6 +865,11 @@ pub const Palette = struct {
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Compare if two of our value are requal. Required by Config.
|
||||
pub fn equal(self: Self, other: Self) bool {
|
||||
return std.meta.eql(self, other);
|
||||
}
|
||||
|
||||
test "parseCLI" {
|
||||
const testing = std.testing;
|
||||
|
||||
@ -801,6 +910,16 @@ pub const RepeatableString = struct {
|
||||
};
|
||||
}
|
||||
|
||||
/// Compare if two of our value are requal. Required by Config.
|
||||
pub fn equal(self: Self, other: Self) bool {
|
||||
const itemsA = self.list.items;
|
||||
const itemsB = other.list.items;
|
||||
if (itemsA.len != itemsB.len) return false;
|
||||
for (itemsA, itemsB) |a, b| {
|
||||
if (!std.mem.eql(u8, a, b)) return false;
|
||||
} else return true;
|
||||
}
|
||||
|
||||
test "parseCLI" {
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
@ -854,6 +973,14 @@ pub const Keybinds = struct {
|
||||
};
|
||||
}
|
||||
|
||||
/// Compare if two of our value are requal. Required by Config.
|
||||
pub fn equal(self: Keybinds, other: Keybinds) bool {
|
||||
// TODO
|
||||
_ = self;
|
||||
_ = other;
|
||||
return true;
|
||||
}
|
||||
|
||||
test "parseCLI" {
|
||||
const testing = std.testing;
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
Reference in New Issue
Block a user