config: implement comparison for keybinding change

This commit is contained in:
Mitchell Hashimoto
2023-03-12 21:52:48 -07:00
parent 0d93da5f30
commit 11e4215f9f

View File

@ -1,3 +1,4 @@
const config = @This();
const std = @import("std");
const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
@ -711,37 +712,6 @@ pub const Config = struct {
return !equal(field.type, old_value, new_value);
}
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");
},
}
}
/// This yields a key for every changed field between old and new.
pub const ChangeIterator = struct {
old: *const Config,
@ -799,6 +769,78 @@ pub const Config = struct {
}
};
/// A config-specific helper to determine if two values of the same
/// type are equal. This isn't the same as std.mem.eql or std.testing.equals
/// because we expect structs to implement their own equality.
///
/// This also doesn't support ALL Zig types, because we only add to it
/// as we need types for the config.
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)) {
.Void => return true,
inline .Bool,
.Int,
.Enum,
=> 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 => |info| {
if (@hasDecl(T, "equal")) return old.equal(new);
// If a struct doesn't declare an "equal" function, we fall back
// to a recursive field-by-field compare.
inline for (info.fields) |field_info| {
if (!equal(
field_info.type,
@field(old, field_info.name),
@field(new, field_info.name),
)) return false;
}
return true;
},
.Union => |info| {
const tag_type = info.tag_type.?;
const old_tag = std.meta.activeTag(old);
const new_tag = std.meta.activeTag(new);
if (old_tag != new_tag) return false;
inline for (info.fields) |field_info| {
if (@field(tag_type, field_info.name) == old_tag) {
return equal(
field_info.type,
@field(old, field_info.name),
@field(new, field_info.name),
);
}
}
unreachable;
},
else => {
@compileLog(T);
@compileError("unsupported field type");
},
}
}
/// Color represents a color using RGB.
pub const Color = struct {
r: u8,
@ -1006,9 +1048,21 @@ 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;
const self_map = self.set.bindings;
const other_map = other.set.bindings;
if (self_map.count() != other_map.count()) return false;
var it = self_map.iterator();
while (it.next()) |self_entry| {
const other_entry = other_map.getEntry(self_entry.key_ptr.*) orelse
return false;
if (!config.equal(
inputpkg.Binding.Action,
self_entry.value_ptr.*,
other_entry.value_ptr.*,
)) return false;
}
return true;
}