╭ Fork.zig ─────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯
1 const std = @import("std"); 2 const evt = @import("../event.zig"); 3 const xit = @import("xit"); 4 const hash = xit.hash; 5 6 repo_id: [evt.event_id_size * 2]u8, 7 // the target repo's owner, whose user repo holds the target 8 repo_user_id: [evt.event_id_size * 2]u8, 9 stage: Stage = .draft, 10 11 // what the db stores: the event's data plus the commit-derived fields 12 pub const Record = struct { 13 event: Self, 14 removed: bool = false, 15 created_order: u64 = 0, 16 updated_order: u64 = 0, 17 }; 18 19 const Self = @This(); 20 21 pub const Stage = enum { 22 draft, 23 publish, 24 }; 25 26 pub const merge_policy: evt.MergePolicy = .target_wins; 27 pub const record_map_key = "event-id->fork"; 28 pub const all_id_set_key = "fork-id-set"; 29 pub const active_id_set_key = "active-fork-id-set"; 30 pub const repo_to_draft_id_set_key = "repo->draft-id-set"; 31 32 fn validate(record: Record) !void { 33 _ = try evt.parseEventId(&record.event.repo_id); 34 _ = try evt.parseEventId(&record.event.repo_user_id); 35 } 36 37 pub fn consume( 38 comptime DB: type, 39 comptime hash_kind: hash.HashKind, 40 haxy_moment: DB.HashMap(.read_write), 41 event_id: *const [evt.event_id_size]u8, 42 record_maybe: ?Record, 43 arena: *std.heap.ArenaAllocator, 44 _: ?[]const u8, 45 ) !void { 46 const fork_key = hash.hashInt(hash_kind, event_id); 47 const records = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, record_map_key))); 48 const active = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, active_id_set_key))); 49 const drafts = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, repo_to_draft_id_set_key))); 50 51 var existing_maybe: ?Record = null; 52 const existing_cursor_maybe = try records.getCursor(fork_key); 53 if (existing_cursor_maybe) |cursor| { 54 existing_maybe = try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(cursor)); 55 } 56 57 var record = record_maybe orelse try evt.removedRecord(Record, DB, hash_kind, haxy_moment.readOnly(), existing_maybe); 58 59 if (!record.removed) try validate(record); 60 61 if (existing_maybe) |existing| { 62 if (!std.mem.eql(u8, &existing.event.repo_id, &record.event.repo_id) or 63 !std.mem.eql(u8, &existing.event.repo_user_id, &record.event.repo_user_id)) return error.ForkChanged; 64 if (existing.event.stage == .publish and record.event.stage != .publish) return error.ForkAlreadyPublished; 65 record.created_order = existing.created_order; 66 if (!existing.removed) { 67 const order_key = evt.orderKeyDesc(existing.created_order, event_id); 68 _ = try active.remove(&order_key); 69 if (existing.event.stage == .draft) { 70 try removeFromParentSet(DB, hash_kind, drafts, &try evt.parseEventId(&existing.event.repo_id), &order_key); 71 } 72 } 73 } 74 75 const fork_cursor = try records.putCursor(fork_key); 76 try evt.upsert(Record, DB, hash_kind, try DB.HashMap(.read_write).init(fork_cursor), record); 77 try evt.indexEvent(DB, hash_kind, haxy_moment, event_id, .fork, existing_maybe, record); 78 79 const order_key = evt.orderKeyDesc(record.created_order, event_id); 80 if (existing_cursor_maybe == null) { 81 const ids = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, all_id_set_key))); 82 try ids.put(&order_key); 83 } 84 85 if (!record.removed) { 86 try active.put(&order_key); 87 if (record.event.stage == .draft) { 88 const repo_id = try evt.parseEventId(&record.event.repo_id); 89 const by_repo = try DB.SortedSet(.read_write).init(try drafts.putCursor(hash.hashInt(hash_kind, &repo_id))); 90 try by_repo.put(&order_key); 91 } 92 } 93 } 94 95 fn removeFromParentSet( 96 comptime DB: type, 97 comptime hash_kind: hash.HashKind, 98 index: DB.HashMap(.read_write), 99 parent_id: *const [evt.event_id_size]u8, 100 order_key: *const [@sizeOf(u64) + evt.event_id_size]u8, 101 ) !void { 102 const cursor = try index.putCursor(hash.hashInt(hash_kind, parent_id)); 103 const ids = try DB.SortedSet(.read_write).init(cursor); 104 _ = try ids.remove(order_key); 105 } 106 107 pub fn readById( 108 comptime DB: type, 109 comptime hash_kind: hash.HashKind, 110 haxy_moment: DB.HashMap(.read_only), 111 arena: *std.heap.ArenaAllocator, 112 fork_id: []const u8, 113 ) !?Record { 114 const records_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, record_map_key)) orelse return null; 115 const records = try DB.HashMap(.read_only).init(records_cursor); 116 const fork_cursor = try records.getCursor(hash.hashInt(hash_kind, fork_id)) orelse return null; 117 return try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(fork_cursor)); 118 } 119