╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Discussion.zig ───────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
108 event_id: *const [evt.event_id_size]u8, 109 order: u64, 110 arena: *std.heap.ArenaAllocator, 111 ) !void { 112 const discussion_key = hash.hashInt(hash_kind, event_id); 113 const records_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, record_map_key)) orelse return; 114 const records = try DB.HashMap(.read_only).init(records_cursor); 115 const record_cursor = try records.getCursor(discussion_key) orelse return; 116 const record = try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(record_cursor)); 117 const current = try activityOrder(DB, hash_kind, haxy_moment.readOnly(), event_id); 118 if (order <= current) return; 119 120 const active = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, active_id_set_key))); 121 const tags = try DB.SortedMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, tag_to_id_set_key))); 122 if (!record.removed) { 123 const old_key = evt.orderKeyDesc(current, event_id); 124 _ = try active.remove(&old_key); 125 try removeFromTagSets(DB, tags, record.event.tags, &old_key); 126 } 127 128 const activity = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, activity_order_key))); 129 try activity.put(discussion_key, .{ .uint = order }); 130 if (!record.removed) { 131 const new_key = evt.orderKeyDesc(order, event_id); 132 try active.put(&new_key); 133 try addToTagSets(DB, tags, record.event.tags, &new_key); 134 } 135 } 136 137 pub fn activityOrder( 138 comptime DB: type, 139 comptime hash_kind: hash.HashKind, 140 haxy_moment: DB.HashMap(.read_only), 141 event_id: *const [evt.event_id_size]u8, 142 ) !u64 { 143 const map_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, activity_order_key)) orelse return error.CursorNotFound; 144 const map = try DB.HashMap(.read_only).init(map_cursor); 145 const order_cursor = try map.getCursor(hash.hashInt(hash_kind, event_id)) orelse return error.CursorNotFound; 146 return try order_cursor.readUint(); 147 } 148 149 pub fn update( 150 comptime repo_kind: rp.RepoKind, 151 comptime repo_opts: rp.RepoOpts(repo_kind), 152 io: std.Io, 153 allocator: std.mem.Allocator, 154 repo: *rp.Repo(repo_kind, repo_opts), 155 id: *const [evt.event_id_size]u8, 156 title: []const u8, 157 tags: []const u8, 158 description: []const u8, 159 author: evt.CommitAuthor, 160 ) !void { 161 if (!fieldsValid(title, tags)) return error.InvalidFields; 162 163 var arena = std.heap.ArenaAllocator.init(allocator); 164 defer arena.deinit(); 165 const discussion = (try readById(repo_kind, repo_opts, io, allocator, &arena, repo, id)) orelse return error.NotFound; 166 if (discussion.removed) return error.NotFound; 167 168 try evt.consume(.repo, repo_kind, repo_opts, io, allocator, repo, evt.events_ref, &.{.{ 169 .id = std.fmt.bytesToHex(id.*, .lower), 170 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), 171 .author = author, 172 .event = .{ .discuss = .{ 173 .title = title, 174 .tags = tags, 175 .description = description, 176 } }, 177 }}); 178 } 179 180 fn removeFromTagSets(comptime DB: type, tags: DB.SortedMap(.read_write), names: []const u8, order_key: []const u8) !void { 181 var tag_iter = tagIterator(names); 182 while (tag_iter.next()) |tag| { 183 if (tag.len == 0 or tag.len > tag_max_len) continue; 184 const cursor = try tags.putCursor(tag); 185 const set = try DB.SortedSet(.read_write).init(cursor); 186 _ = try set.remove(order_key); 187 if (0 == try set.count()) _ = try tags.remove(tag); 188 } 189 } 190 191 fn addToTagSets(comptime DB: type, tags: DB.SortedMap(.read_write), names: []const u8, order_key: []const u8) !void { 192 var tag_iter = tagIterator(names); 193 while (tag_iter.next()) |tag| { 194 if (tag.len == 0 or tag.len > tag_max_len) continue; 195 const set = try DB.SortedSet(.read_write).init(try tags.putCursor(tag)); 196 try set.put(order_key); 197 } 198 } 199 200 pub fn readById( 201 comptime repo_kind: rp.RepoKind, 202 comptime repo_opts: rp.RepoOpts(repo_kind), 203 io: std.Io, 204 allocator: std.mem.Allocator, 205 arena: *std.heap.ArenaAllocator, 206 repo: *rp.Repo(repo_kind, repo_opts), 207 id: *const [evt.event_id_size]u8, 208 ) !?Record { 209 const DB = evt.EventDB(repo_opts.hash); 210 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; 211 defer if (event_db_maybe) |*event_db| event_db.deinit(io, allocator); 212 const moment = (if (event_db_maybe) |*event_db| 213 evt.currentMomentFromDb(repo_opts.hash, event_db.db) 214 else if (repo_kind == .git) 215 return null 216 else 217 evt.currentMoment(repo_opts, repo)) catch return null; 218 const records_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, record_map_key)) orelse return null; 219 const records = try DB.HashMap(.read_only).init(records_cursor); 220 const record_cursor = try records.getCursor(hash.hashInt(repo_opts.hash, id)) orelse return null; 221 return try evt.read(Record, DB, repo_opts.hash, arena, try DB.HashMap(.read_only).init(record_cursor)); 222 } 223