╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Issue.zig ────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
241 io: std.Io, 242 allocator: std.mem.Allocator, 243 arena: *std.heap.ArenaAllocator, 244 repo: *rp.Repo(repo_kind, repo_opts), 245 id_bytes: *const [evt.event_id_size]u8, 246 live: Record, 247 resolve: Resolve, 248 ) !Self { 249 const DB = evt.EventDB(repo_opts.hash); 250 const aa = arena.allocator(); 251 252 var updated = live.event; 253 if (resolve.title) |title| updated.title = title; 254 if (resolve.tags) |tags| updated.tags = tags; 255 256 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; 257 defer if (event_db_maybe) |*event_db| event_db.deinit(io, allocator); 258 const moment = (if (event_db_maybe) |*event_db| 259 evt.currentMomentFromDb(repo_opts.hash, event_db.db) 260 else if (repo_kind == .git) 261 return updated 262 else 263 evt.currentMoment(repo_opts, repo)) catch return updated; 264 265 const conflicts_cursor = (try moment.getCursor(hash.hashInt(repo_opts.hash, conflicts_key))) orelse return updated; 266 const conflicts = try DB.SortedMap(.read_only).init(conflicts_cursor); 267 const order_key = evt.orderKeyDesc(live.created_order, id_bytes); 268 const conflict_cursor = (try conflicts.getCursor(&order_key)) orelse return updated; 269 const entry = try DB.HashMap(.read_only).init(conflict_cursor); 270 271 const fields_cursor = (try entry.getCursor(hash.hashInt(repo_opts.hash, evt.conflicted_fields_key))) orelse return updated; 272 const conflicted_fields = try fields_cursor.readBytesAlloc(aa, null); 273 if (std.mem.indexOf(u8, conflicted_fields, "description") == null) return updated; 274 275 const their_cursor = (try entry.getCursor(hash.hashInt(repo_opts.hash, evt.their_record_key))) orelse return updated; 276 const theirs = try evt.read(Record, DB, repo_opts.hash, arena, try DB.HashMap(.read_only).init(their_cursor)); 277 278 // absent when both sides created the issue independently 279 var base_description: []const u8 = ""; 280 if (try entry.getCursor(hash.hashInt(repo_opts.hash, evt.base_record_key))) |base_cursor| { 281 const base = try evt.read(Record, DB, repo_opts.hash, arena, try DB.HashMap(.read_only).init(base_cursor)); 282 base_description = base.event.description; 283 } 284 285 const chunks = try diff3.chunks(allocator, arena, base_description, live.event.description, theirs.event.description); 286 var resolutions: std.ArrayList([]const u8) = .empty; 287 var hunk_index: usize = 0; 288 for (chunks) |chunk| { 289 if (chunk != .conflict) continue; 290 try resolutions.append(aa, if (hunk_index < resolve.hunks.len) resolve.hunks[hunk_index] else (chunk.conflict.ours orelse "")); 291 hunk_index += 1; 292 } 293 updated.description = try diff3.assemble(aa, chunks, resolutions.items); 294 return updated; 295 } 296 297 // the issue `id_bytes` names in the repo's consumed event db (the repo's own 298 // db for xit; the side db next to a git repo), or null 299 fn readById( 300 comptime repo_kind: rp.RepoKind, 301 comptime repo_opts: rp.RepoOpts(repo_kind), 302 io: std.Io, 303 allocator: std.mem.Allocator, 304 arena: *std.heap.ArenaAllocator, 305 repo: *rp.Repo(repo_kind, repo_opts), 306 id_bytes: *const [evt.event_id_size]u8, 307 ) !?Record { 308 const DB = evt.EventDB(repo_opts.hash); 309 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; 310 defer if (event_db_maybe) |*event_db| event_db.deinit(io, allocator); 311 const moment = (if (event_db_maybe) |*event_db| 312 evt.currentMomentFromDb(repo_opts.hash, event_db.db) 313 else if (repo_kind == .git) 314 return null 315 else 316 evt.currentMoment(repo_opts, repo)) catch return null; 317 const event_id_to_issue_cursor = (try moment.getCursor(hash.hashInt(repo_opts.hash, record_map_key))) orelse return null; 318 const event_id_to_issue = try DB.HashMap(.read_only).init(event_id_to_issue_cursor); 319 const issue_cursor = (try event_id_to_issue.getCursor(hash.hashInt(repo_opts.hash, id_bytes))) orelse return null; 320 const issue_map = try DB.HashMap(.read_only).init(issue_cursor); 321 return try evt.read(Record, DB, repo_opts.hash, arena, issue_map); 322 } 323 324 // remove an issue's order key from its tags' `status` sets, pruning entries 325 // whose set becomes empty 326 fn removeFromTagSets( 327 comptime DB: type, 328 tag_to_issues: DB.SortedMap(.read_write), 329 tags: []const u8, 330 status: Status, 331 order_key: []const u8, 332 ) !void { 333 var tag_iter = tagIterator(tags); 334 while (tag_iter.next()) |tag| { 335 // skipped the same way consume skipped it, so it was never indexed 336 if (tag.len == 0 or tag.len > tag_max_len) continue; 337 var key_buffer: TagStatusKey = undefined; 338 const key = try tagStatusKey(&key_buffer, tag, status); 339 const tag_set_cursor = try tag_to_issues.putCursor(key); 340 const tag_set = try DB.SortedSet(.read_write).init(tag_set_cursor); 341 _ = try tag_set.remove(order_key); 342 if (0 == try tag_set.count()) _ = try tag_to_issues.remove(key); 343 } 344 } 345