╭ changed ──────────╮ │src/event/Issue.zig│ ╰───────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────╮ │ 6 title: []const u8, │ │ 7 description: []const u8, │ │ 8 tags: []const u8, │ │ 9 +created_ts: u64 = 0, // the commit timestamp of the event that first created this issue│ │10 │ │11 +// `created_ts` comes from the commit timestamp, not the event │ │12 +// payload, so it must not appear in the JSON we output │ │13 +pub fn jsonStringify(self: @This(), jw: anytype) !void { │ │14 + try jw.beginObject(); │ │15 + inline for (std.meta.fields(@This())) |field| { │ │16 + if (comptime std.mem.eql(u8, field.name, "created_ts")) continue; │ │17 + try jw.objectField(field.name); │ │18 + try jw.write(@field(self, field.name)); │ │19 + } │ │20 + try jw.endObject(); │ │21 +} │ │22 + │ │23 pub fn consume( │ │24 comptime DB: type, │ │25 comptime hash_kind: hash.HashKind, │ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────╮ │26 haxy_moment: DB.HashMap(.read_write), │ │27 event_id: *const [evt.event_id_size]u8, │ │28 event_maybe: ?@This(), │ │29 + arena: *std.heap.ArenaAllocator, │ │30 + created_ts: u64, │ │31 ) !void { │ │32 const issue_key = hash.hashInt(hash_kind, event_id);│ │33 │ ╰────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │35 const event_id_to_issue = try DB.HashMap(.read_write).init(event_id_to_issue_cursor); │ │36 │ │37 if (event_maybe) |event| { │ │38 + var event_to_write = event; │ │39 + │ │40 + const existing_cursor_maybe = try event_id_to_issue.getCursor(issue_key); │ │41 + if (existing_cursor_maybe) |existing_cursor| { │ │42 + // updates preserve the original creation timestamp │ │43 + const existing_issue = try DB.HashMap(.read_only).init(existing_cursor); │ │44 + const existing_event = try evt.read(@This(), DB, hash_kind, arena, existing_issue); │ │45 + event_to_write.created_ts = existing_event.created_ts; │ │46 + } else { │ │47 + // first time we've seen this issue: stamp it with the commit timestamp │ │48 + event_to_write.created_ts = created_ts; │ │49 + } │ │50 + │ │51 const issue_cursor = try event_id_to_issue.putCursor(issue_key); │ │52 const issue = try DB.HashMap(.read_write).init(issue_cursor); │ │25 - try evt.upsert(@This(), DB, hash_kind, issue, event); │ │53 + try evt.upsert(@This(), DB, hash_kind, issue, event_to_write); │ │54 + │ │55 + // first time we've seen this issue: add it to the set the issues view │ │56 + // lists, ordered by creation time. the key (orderKey) embeds the event │ │57 + // id, so the set needs no value. │ │58 + if (existing_cursor_maybe == null) { │ │59 + const issue_id_set_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, "issue-id-set"));│ │60 + const issue_id_set = try DB.SortedSet(.read_write).init(issue_id_set_cursor); │ │61 + const order_key = evt.orderKey(event_to_write.created_ts, event_id); │ │62 + try issue_id_set.put(&order_key); │ │63 + } │ │64 } else { │ │65 + // drop it from the ordered set using its recorded creation timestamp │ │66 + if (try event_id_to_issue.getCursor(issue_key)) |existing_cursor| { │ │67 + const existing_issue = try DB.HashMap(.read_only).init(existing_cursor); │ │68 + const existing_event = try evt.read(@This(), DB, hash_kind, arena, existing_issue); │ │69 + const issue_id_set_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, "issue-id-set"));│ │70 + const issue_id_set = try DB.SortedSet(.read_write).init(issue_id_set_cursor); │ │71 + const order_key = evt.orderKey(existing_event.created_ts, event_id); │ │72 + _ = try issue_id_set.remove(&order_key); │ │73 + } │ │74 if (!try event_id_to_issue.remove(issue_key)) return error.EventNotFound; │ │75 } │ │76 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯