From 11e4215f9fba4878d35239e4851fc721a3943019 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 12 Mar 2023 21:52:48 -0700 Subject: [PATCH] config: implement comparison for keybinding change --- src/config.zig | 122 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 34 deletions(-) diff --git a/src/config.zig b/src/config.zig index 262a59a8b..06626b3e7 100644 --- a/src/config.zig +++ b/src/config.zig @@ -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; }