╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Discussion.zig ───────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
49 record_maybe: ?Record, 50 arena: *std.heap.ArenaAllocator, 51 _: ?[]const u8, 52 ) !void { 53 const discussion_key = hash.hashInt(hash_kind, event_id); 54 const records = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, record_map_key))); 55 const active = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, active_id_set_key))); 56 const labels = try DB.SortedMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, label_to_id_set_key))); 57 var existing_record_maybe: ?Record = null; 58 const existing_cursor_maybe = try records.getCursor(discussion_key); 59 if (existing_cursor_maybe) |existing_cursor| { 60 existing_record_maybe = try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(existing_cursor)); 61 } 62 63 var record_to_write = record_maybe orelse try evt.removedRecord(Record, DB, hash_kind, haxy_moment.readOnly(), existing_record_maybe); 64 65 if (existing_record_maybe) |existing| { 66 record_to_write.created_order = existing.created_order; 67 record_to_write.author_email = existing.author_email; 68 } 69 70 const activity_order = if (existing_record_maybe != null) 71 try activityOrder(DB, hash_kind, haxy_moment.readOnly(), event_id) 72 else 73 record_to_write.created_order; 74 if (existing_record_maybe) |existing| { 75 if (!existing.removed) { 76 const old_key = evt.orderKeyDesc(activity_order, event_id); 77 _ = try active.remove(&old_key); 78 try removeFromLabelSets(DB, labels, existing.event.labels, &old_key); 79 } 80 } 81 82 const record = try DB.HashMap(.read_write).init(try records.putCursor(discussion_key)); 83 try evt.upsert(Record, DB, hash_kind, record, record_to_write); 84 try evt.indexEvent(DB, hash_kind, haxy_moment, event_id, .discuss, existing_record_maybe, record_to_write); 85 86 const order_key = evt.orderKeyDesc(activity_order, event_id); 87 if (existing_cursor_maybe == null) { 88 const activity = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, activity_order_key))); 89 try activity.put(discussion_key, .{ .uint = activity_order }); 90 const ids = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, all_id_set_key))); 91 try ids.put(&evt.orderKeyDesc(record_to_write.created_order, event_id)); 92 } 93 94 // the search index is keyed by creation, not activity: ordering is the 95 // list's business, so a comment moving the discussion up leaves it alone 96 const search_key = evt.orderKeyDesc(record_to_write.created_order, event_id); 97 try srch_thrd.update(DB, hash_kind, .discuss, haxy_moment, arena.child_allocator, &search_key, existing_record_maybe, record_to_write); 98 99 if (!record_to_write.removed) { 100 try active.put(&order_key); 101 try addToLabelSets(DB, labels, record_to_write.event.labels, &order_key); 102 } 103 } 104 105 pub fn touch( 106 comptime DB: type, 107 comptime hash_kind: hash.HashKind, 108 haxy_moment: DB.HashMap(.read_write), 109 event_id: *const [evt.event_id_size]u8, 110 order: u64, 111 arena: *std.heap.ArenaAllocator, 112 ) !void { 113 const discussion_key = hash.hashInt(hash_kind, event_id); 114 const records_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, record_map_key)) orelse return; 115 const records = try DB.HashMap(.read_only).init(records_cursor); 116 const record_cursor = try records.getCursor(discussion_key) orelse return; 117 const record = try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(record_cursor)); 118 const current = try activityOrder(DB, hash_kind, haxy_moment.readOnly(), event_id); 119 if (order <= current) return; 120 121 const active = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, active_id_set_key))); 122 const labels = try DB.SortedMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, label_to_id_set_key))); 123 if (!record.removed) { 124 const old_key = evt.orderKeyDesc(current, event_id); 125 _ = try active.remove(&old_key); 126 try removeFromLabelSets(DB, labels, record.event.labels, &old_key); 127 } 128 129 const activity = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, activity_order_key))); 130 try activity.put(discussion_key, .{ .uint = order }); 131 if (!record.removed) { 132 const new_key = evt.orderKeyDesc(order, event_id); 133 try active.put(&new_key); 134 try addToLabelSets(DB, labels, record.event.labels, &new_key); 135 } 136 } 137 138 pub fn activityOrder( 139 comptime DB: type, 140 comptime hash_kind: hash.HashKind, 141 haxy_moment: DB.HashMap(.read_only), 142 event_id: *const [evt.event_id_size]u8, 143 ) !u64 { 144 const map_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, activity_order_key)) orelse return error.CursorNotFound; 145 const map = try DB.HashMap(.read_only).init(map_cursor); 146 const order_cursor = try map.getCursor(hash.hashInt(hash_kind, event_id)) orelse return error.CursorNotFound; 147 return try order_cursor.readUint(); 148 } 149 150 pub fn update( 151 host: evt.Host, 152 comptime repo_kind: rp.RepoKind, 153 comptime repo_opts: rp.RepoOpts(repo_kind), 154 io: std.Io, 155 allocator: std.mem.Allocator, 156 repo: *rp.Repo(repo_kind, repo_opts), 157 id: *const [evt.event_id_size]u8, 158 title: []const u8, 159 labels: []const u8, 160 description: []const u8, 161 author: evt.CommitAuthor, 162 ) !void { 163 if (!fieldsValid(title, labels)) return error.InvalidFields; 164 165 var arena = std.heap.ArenaAllocator.init(allocator); 166 defer arena.deinit(); 167 const discussion = (try readById(repo_kind, repo_opts, io, allocator, &arena, repo, id)) orelse return error.NotFound; 168 if (discussion.removed) return error.NotFound; 169 170 try evt.consume(host, .repo, repo_kind, repo_opts, io, allocator, repo, evt.events_ref, &.{.{ 171 .id = std.fmt.bytesToHex(id.*, .lower), 172 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), 173 .author = author, 174 .event = .{ .discuss = .{ 175 .title = title, 176 .labels = labels, 177 .description = description, 178 } }, 179 }}); 180 } 181 182 fn removeFromLabelSets(comptime DB: type, labels: DB.SortedMap(.read_write), names: []const u8, order_key: []const u8) !void { 183 var label_iter = labelIterator(names); 184 while (label_iter.next()) |label| { 185 if (label.len == 0 or label.len > label_max_len) continue; 186 const cursor = try labels.putCursor(label); 187 const set = try DB.SortedSet(.read_write).init(cursor); 188 _ = try set.remove(order_key); 189 if (0 == try set.count()) _ = try labels.remove(label); 190 } 191 } 192 193 fn addToLabelSets(comptime DB: type, labels: DB.SortedMap(.read_write), names: []const u8, order_key: []const u8) !void { 194 var label_iter = labelIterator(names); 195 while (label_iter.next()) |label| { 196 if (label.len == 0 or label.len > label_max_len) continue; 197 const set = try DB.SortedSet(.read_write).init(try labels.putCursor(label)); 198 try set.put(order_key); 199 } 200 } 201 202 pub fn readById( 203 comptime repo_kind: rp.RepoKind, 204 comptime repo_opts: rp.RepoOpts(repo_kind), 205 io: std.Io, 206 allocator: std.mem.Allocator, 207 arena: *std.heap.ArenaAllocator, 208 repo: *rp.Repo(repo_kind, repo_opts), 209 id: *const [evt.event_id_size]u8, 210 ) !?Record { 211 const DB = evt.EventDB(repo_opts.hash); 212 var event_db_maybe: ?evt.LocalEventDB(repo_opts.hash) = if (repo_kind == .git) try evt.LocalEventDB(repo_opts.hash).openReadOnly(io, allocator, repo.core.repo_dir) else null; 213 defer if (event_db_maybe) |*event_db| event_db.deinit(io, allocator); 214 const moment = (if (event_db_maybe) |*event_db| 215 evt.currentMomentFromDb(repo_opts.hash, event_db.db) 216 else if (repo_kind == .git) 217 return null 218 else 219 evt.currentMoment(repo_opts, repo)) catch return null; 220 const records_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, record_map_key)) orelse return null; 221 const records = try DB.HashMap(.read_only).init(records_cursor); 222 const record_cursor = try records.getCursor(hash.hashInt(repo_opts.hash, id)) orelse return null; 223 return try evt.read(Record, DB, repo_opts.hash, arena, try DB.HashMap(.read_only).init(record_cursor)); 224 } 225