╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─────────╮ │src/event/Repo.zig│ ╰──────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │44 - if (comptime std.mem.eql(u8, field.name, "created_ts")) continue;│ │43 + if (comptime evt.derivedField(field.name)) continue; │ │44 try jw.objectField(field.name); │ │45 try jw.write(@field(self, field.name)); │ │46 } │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────╮ │54 event_id: *const [evt.event_id_size]u8, │ │55 event_maybe: ?@This(), │ │56 arena: *std.heap.ArenaAllocator, │ │58 - created_ts: u64, │ │57 // the commit this event came from, recorded against every field it │ │58 // changes. null from `merge`, which sets oids and conflicts itself.│ │59 event_oid: ?[]const u8, │ ╰────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │108 const order_key = evt.orderKeyDesc(existing_event.created_ts, event_id); │ │109 _ = try old_user_repos.remove(&order_key); │ │110 } │ │113 - } else { │ │114 - // first time we've seen this repo: stamp it with the commit timestamp │ │115 - event_to_write.created_ts = created_ts; │ │111 } │ │112 │ │113 try evt.writeOid(@This(), DB, hash_kind, repo_id_to_field_to_oid, repo_key, existing_event_maybe, event, event_oid);│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────╮ │src/event/User.zig│ ╰──────────────────╯ ╭──────────────────────────────────────────────────────────────────────╮ │50 } │ │51 } │ │52 │ │53 -// `created_ts` comes from the commit timestamp, not the event │ │54 -// payload, so it must not appear in the JSON we output │ │53 +// the commit-derived fields must not appear in the JSON we output│ │54 pub fn jsonStringify(self: @This(), jw: anytype) !void { │ │55 try jw.beginObject(); │ │56 inline for (std.meta.fields(@This())) |field| { │ ╰──────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │58 - if (comptime std.mem.eql(u8, field.name, "created_ts")) continue;│ │57 + if (comptime evt.derivedField(field.name)) continue; │ │58 try jw.objectField(field.name); │ │59 try jw.write(@field(self, field.name)); │ │60 } │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────╮ │78 event_id: *const [evt.event_id_size]u8, │ │79 event_maybe: ?@This(), │ │80 arena: *std.heap.ArenaAllocator, │ │82 - created_ts: u64, │ │81 // the commit this event came from, recorded against every field it │ │82 // changes. null from `merge`, which sets oids and conflicts itself.│ │83 event_oid: ?[]const u8, │ ╰────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │126 if (event_oid != null) { │ │127 _ = try conflicts.remove(&evt.orderKeyDesc(existing_event.created_ts, event_id)); │ │128 } │ │131 - } else { │ │132 - // first time we've seen this user: stamp it with the commit timestamp │ │133 - event_to_write.created_ts = created_ts; │ │129 } │ │130 │ │131 try evt.writeOid(@This(), DB, hash_kind, user_id_to_field_to_oid, user_key, existing_event_maybe, event, event_oid);│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │238 const user_cursor = try event_id_to_user.getCursor(hash.hashInt(hash_kind, user_id)) orelse return null; │ │239 const user_map = try DB.HashMap(.read_only).init(user_cursor); │ │240 return try evt.read(@This(), DB, hash_kind, arena, user_map); │ │241 +} │ │242 + │ │243 +// read a user by email via the email->user-id index, or null when no user has │ │244 +// the email. field byte slices are allocated in `arena`. │ │245 +pub fn readByEmail( │ │246 + comptime DB: type, │ │247 + comptime hash_kind: hash.HashKind, │ │248 + haxy_moment: DB.HashMap(.read_only), │ │249 + arena: *std.heap.ArenaAllocator, │ │250 + email: []const u8, │ │251 +) !?@This() { │ │252 + const email_to_user_id_cursor = (try haxy_moment.getCursor(hash.hashInt(hash_kind, email_to_user_id_key))) orelse return null;│ │253 + const email_to_user_id = try DB.HashMap(.read_only).init(email_to_user_id_cursor); │ │254 + const user_id_cursor = (try email_to_user_id.getCursor(hash.hashInt(hash_kind, email))) orelse return null; │ │255 + var user_id: [evt.event_id_size]u8 = undefined; │ │256 + _ = try user_id_cursor.readBytes(&user_id); │ │257 + return try readById(DB, hash_kind, haxy_moment, arena, &user_id); │ │258 } │ │259 │ │260 // read a user by name from the admin event store, or null if the admin repo or │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/try.zig│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────╮ │175 .id = std.fmt.bytesToHex(user_ids[i], .lower), │ │176 // stepped timestamps so the seeded users/repos list in a stable order│ │177 .timestamp = @intCast(slot + 1), │ │178 - .author_email = "admin@haxy", │ │178 + .author_email = "admin@example.test", │ │179 .event = .{ │ │180 .user = .{ │ │181 .name = u.name, │ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │214 defer template_repo.deinit(io, allocator); │ │215 │ │216 try template_repo.addConfig(io, allocator, .{ .name = "user.name", .value = "haxy" }); │ │217 - try template_repo.addConfig(io, allocator, .{ .name = "user.email", .value = "admin@haxy" }); │ │217 + try template_repo.addConfig(io, allocator, .{ .name = "user.email", .value = "admin@example.test" });│ │218 │ │219 // a README plus a nested doc so the file tree has a directory to │ │220 // descend into │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯