╭ changed ─────────╮ │src/event/User.zig│ ╰──────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │11 password_hash: []const u8, │ │12 enable_ansi: bool = true, │ │13 ssh_keys: []const u8 = "", // newline-separated authorized_keys lines (one OpenSSH public key per line)│ │14 -created_ts: u64 = 0, // the commit timestamp of the event that first created this user │ │14 + │ │15 +// what the db stores: the event's data plus the commit-derived fields │ │16 +pub const Record = struct { │ │17 + event: Self, │ │18 + created_ts: u64 = 0, // the commit timestamp of the event that first created this user │ │19 + │ │20 + // a user's key in the name index │ │21 + pub fn indexKey(self: Record, allocator: std.mem.Allocator) ![]const u8 { │ │22 + _ = allocator; │ │23 + return self.event.name; │ │24 + } │ │25 +}; │ │26 + │ │27 +// the subset of a user that anyone may see │ │28 +pub const Public = struct { │ │29 + name: []const u8, │ │30 + display_name: []const u8, │ │31 +}; │ │32 │ │33 const Self = @This(); │ │34 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │44 // resolves a commit's author email to its user at read time │ │45 pub const email_to_user_id_key = "email->user-id"; │ │46 │ │30 -// a user's key in the name index │ │31 -pub fn indexKey(self: Self, allocator: std.mem.Allocator) ![]const u8 {│ │32 - _ = allocator; │ │33 - return self.name; │ │34 -} │ │35 - │ │47 pub fn validateName(name: []const u8) !void { │ │48 if (name.len == 0) return error.NameEmpty; │ │49 if (name.len > name_max_len) return error.NameTooLong; │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │58 } else { │ │59 return error.InvalidName; │ │60 } │ │50 - } │ │51 -} │ │52 - │ │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| { │ │57 - if (comptime evt.derivedField(field.name)) continue; │ │58 - try jw.objectField(field.name); │ │59 - try jw.write(@field(self, field.name)); │ │61 } │ │61 - try jw.endObject(); │ │62 } │ │63 │ │64 -// the subset of a user that's safe to hand to clients │ │65 -pub const Safe = struct { │ │66 - name: []const u8, │ │67 - display_name: []const u8, │ │68 - │ │69 - pub fn init(user: Self) Safe { │ │70 - return .{ .name = user.name, .display_name = user.display_name };│ │71 - } │ │72 -}; │ │73 - │ │64 pub fn consume( │ │65 comptime DB: type, │ │66 comptime hash_kind: hash.HashKind, │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────╮ │67 haxy_moment: DB.HashMap(.read_write), │ │68 event_id: *const [evt.event_id_size]u8, │ │79 - event_maybe: ?@This(), │ │69 + record_maybe: ?Record, │ │70 arena: *std.heap.ArenaAllocator, │ │71 // the commit this event came from, recorded against every field it │ │72 // changes. null from `merge`, which sets oids and conflicts itself.│ ╰────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 89 const user_id_to_field_to_oid_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, id_to_field_to_oid_key));│ │ 90 const user_id_to_field_to_oid = try DB.HashMap(.read_write).init(user_id_to_field_to_oid_cursor); │ │ 91 │ │102 - if (event_maybe) |event| { │ │103 - try validateName(event.name); │ │ 92 + if (record_maybe) |record| { │ │ 93 + try validateName(record.event.name); │ │ 94 │ │105 - var event_to_write = event; │ │ 95 + var record_to_write = record; │ │ 96 │ │ 97 // if this event_id already maps to a user with a different name or │ │ 98 // email, drop the stale index entries first │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │109 - var existing_event_maybe: ?@This() = null; │ │ 99 + var existing_record_maybe: ?Record = null; │ │100 const existing_cursor_maybe = try event_id_to_user.getCursor(user_key); │ │101 if (existing_cursor_maybe) |existing_cursor| { │ │102 const existing_user = try DB.HashMap(.read_only).init(existing_cursor);│ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │113 - const existing_event = try evt.read(@This(), DB, hash_kind, arena, existing_user); │ │114 - existing_event_maybe = existing_event; │ │103 + const existing_record = try evt.read(Record, DB, hash_kind, arena, existing_user); │ │104 + existing_record_maybe = existing_record; │ │105 // updates preserve the original creation timestamp │ │116 - event_to_write.created_ts = existing_event.created_ts; │ │117 - if (!std.mem.eql(u8, existing_event.name, event.name)) { │ │118 - _ = try name_to_user_id.remove(hash.hashInt(hash_kind, existing_event.name)); │ │106 + record_to_write.created_ts = existing_record.created_ts; │ │107 + if (!std.mem.eql(u8, existing_record.event.name, record.event.name)) { │ │108 + _ = try name_to_user_id.remove(hash.hashInt(hash_kind, existing_record.event.name)); │ │109 } │ │120 - if (!std.mem.eql(u8, existing_event.email, event.email)) { │ │121 - _ = try email_to_user_id.remove(hash.hashInt(hash_kind, existing_event.email)); │ │110 + if (!std.mem.eql(u8, existing_record.event.email, record.event.email)) { │ │111 + _ = try email_to_user_id.remove(hash.hashInt(hash_kind, existing_record.event.email));│ │112 } │ │113 │ │114 // any event settles the conflict, since resolving in the ui may │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │115 // keep our own values and so change nothing to detect │ │116 if (event_oid != null) { │ │127 - _ = try conflicts.remove(&evt.orderKeyDesc(existing_event.created_ts, event_id)); │ │117 + _ = try conflicts.remove(&evt.orderKeyDesc(existing_record.created_ts, event_id));│ │118 } │ │119 } │ │120 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │131 - try evt.writeOid(@This(), DB, hash_kind, user_id_to_field_to_oid, user_key, existing_event_maybe, event, event_oid); │ │121 + try evt.writeOid(Self, DB, hash_kind, user_id_to_field_to_oid, user_key, if (existing_record_maybe) |existing| existing.event else null, record.event, event_oid);│ │122 │ │123 const user_cursor = try event_id_to_user.putCursor(user_key); │ │124 const user = try DB.HashMap(.read_write).init(user_cursor); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │135 - try evt.upsert(@This(), DB, hash_kind, user, event_to_write); │ │125 + try evt.upsert(Record, DB, hash_kind, user, record_to_write); │ │126 │ │137 - try name_to_user_id.put(hash.hashInt(hash_kind, event.name), .{ .bytes = event_id }); │ │138 - try email_to_user_id.put(hash.hashInt(hash_kind, event.email), .{ .bytes = event_id }); │ │127 + try name_to_user_id.put(hash.hashInt(hash_kind, record.event.name), .{ .bytes = event_id }); │ │128 + try email_to_user_id.put(hash.hashInt(hash_kind, record.event.email), .{ .bytes = event_id });│ │129 │ │130 // first time we've seen this user: add it to the ordered set the users │ │131 // view paginates through. the key embeds the event id, so the set needs │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯