config: implement change iterator (one todo)

This commit is contained in:
Mitchell Hashimoto
2023-03-12 15:07:23 -07:00
parent 510f4b4699
commit 16166b6297

View File

@ -166,6 +166,30 @@ pub const Config = struct {
/// This is set by the CLI parser for deinit. /// This is set by the CLI parser for deinit.
_arena: ?ArenaAllocator = null, _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 { pub fn deinit(self: *Config) void {
if (self._arena) |arena| arena.deinit(); if (self._arena) |arena| arena.deinit();
self.* = undefined; 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" { test "clone default" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
@ -664,6 +759,10 @@ pub const Config = struct {
var dest = try source.clone(alloc); var dest = try source.clone(alloc);
defer dest.deinit(); 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) // I want to do this but this doesn't work (the API doesn't work)
// try testing.expectEqualDeep(dest, source); // try testing.expectEqualDeep(dest, source);
} }
@ -693,6 +792,11 @@ pub const Color = struct {
return self; 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 "#" /// 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 {
@ -761,6 +865,11 @@ pub const Palette = struct {
return self; 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" { test "parseCLI" {
const testing = std.testing; 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" { test "parseCLI" {
const testing = std.testing; const testing = std.testing;
var arena = ArenaAllocator.init(testing.allocator); 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" { test "parseCLI" {
const testing = std.testing; const testing = std.testing;
var arena = ArenaAllocator.init(testing.allocator); var arena = ArenaAllocator.init(testing.allocator);