╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ │1072 try consume(.xit, admin_repo_opts, io, allocator, &repo, events_ref, &[_]EventWithId{.{│ │1073 .id = event_id_hex, │ │1074 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), │ │1069 - .author_email = owner.email, │ │1075 + .author_email = owner.event.email, │ │1076 .event = .{ .repo = .{ │ │1077 .user_id = &owner_user_id, │ │1078 .name = repo_name, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1087 // reading from xitdb │ │1088 // │ │1089 │ │1090 +// a struct field's fields share its container's map, so their names must not │ │1091 +// collide with the container's or two fields would silently share a key │ │1092 +fn validateFlattenedFields(comptime T: type) void { │ │1093 + comptime { │ │1094 + for (std.meta.fields(T)) |field| { │ │1095 + if (@typeInfo(field.type) != .@"struct") continue; │ │1096 + for (std.meta.fields(field.type)) |nested| { │ │1097 + if (@hasField(T, nested.name)) │ │1098 + @compileError("flattened field name collision in " ++ @typeName(T) ++ ": " ++ nested.name);│ │1099 + } │ │1100 + } │ │1101 + } │ │1102 +} │ │1103 + │ │1104 pub fn read( │ │1105 comptime T: type, │ │1106 comptime DB: type, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────╮ │1112 │ │1113 switch (@typeInfo(T)) { │ │1114 .@"struct" => |struct_info| { │ │1115 + comptime validateFlattenedFields(T); │ │1116 inline for (struct_info.fields) |field| {│ │1117 switch (@typeInfo(field.type)) { │ │1118 .pointer => |pointer_info| { │ ╰───────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1148 else │ │1149 null; │ │1150 }, │ │1151 + // a struct field's own fields are read from the same map │ │1152 + .@"struct" => @field(event, field.name) = try read(field.type, DB, hash_kind, arena, map),│ │1153 else => @compileError("unsupported read field type: " ++ @typeName(field.type)), │ │1154 } │ │1155 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────╮ │1160 return event; │ │1161 } │ │1162 │ │1140 -// whether two events carry the same payload data (commit-derived fields│ │1141 -// aren't compared) │ │1163 +// whether two events carry the same data │ │1164 fn fieldsEqual(comptime T: type, a: T, b: T) bool { │ │1165 switch (@typeInfo(T)) { │ │1166 .@"struct" => |struct_info| { │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1167 inline for (struct_info.fields) |field| { │ │1146 - if (comptime derivedField(field.name)) continue; │ │1168 if (!fieldEqual(field.type, @field(a, field.name), @field(b, field.name))) return false;│ │1169 } │ │1170 }, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1255 ) !void { │ │1256 switch (@typeInfo(T)) { │ │1257 .@"struct" => |struct_info| { │ │1258 + comptime validateFlattenedFields(T); │ │1259 inline for (struct_info.fields) |field| { │ │1260 try upsertField(DB, hash_kind, map, field.name, field.type, @field(event, field.name));│ │1261 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────╮ │1333 _ = try map.remove(key); │ │1334 } │ │1335 }, │ │1336 + // a struct field's own fields are stored in the same map │ │1337 + .@"struct" => try upsert(Field, DB, hash_kind, map, value), │ │1338 else => @compileError("unsupported upsert field type: " ++ @typeName(Field)),│ │1339 } │ │1340 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────╯