╭ 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 user_id: []const u8, 7 repo_id: []const u8, 8 target: []const u8, 9 source_oid: ?[]const u8 = null, 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 }; 17 18 const Self = @This(); 19 20 pub const merge_policy: evt.MergePolicy = .target_wins; 21 pub const record_map_key = "event-id->fork"; 22 pub const id_set_key = "fork-id-set"; 23 pub const user_id_to_fork_id_set_key = "user-id->fork-id-set"; 24 pub const repo_id_to_fork_id_set_key = "repo-id->fork-id-set"; 25 26 fn validate(record: Record) !void { 27 if (record.event.user_id.len != evt.event_id_size) return error.InvalidUserId; 28 if (record.event.repo_id.len != evt.event_id_size) return error.InvalidRepoId; 29 if (!xit.ref.validateName(record.event.target)) return error.InvalidTarget; 30 if (record.event.source_oid) |oid| { 31 if (oid.len == 0 or oid.len % 2 != 0) return error.InvalidSourceOid; 32 for (oid) |c| { 33 if (!std.ascii.isDigit(c) and !(c >= 'a' and c <= 'f')) return error.InvalidSourceOid; 34 } 35 } 36 } 37 38 pub fn consume( 39 comptime DB: type, 40 comptime hash_kind: hash.HashKind, 41 haxy_moment: DB.HashMap(.read_write), 42 event_id: *const [evt.event_id_size]u8, 43 record_maybe: ?Record, 44 arena: *std.heap.ArenaAllocator, 45 _: ?[]const u8, 46 ) !void { 47 const fork_key = hash.hashInt(hash_kind, event_id); 48 const records = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, record_map_key))); 49 const user_forks = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, user_id_to_fork_id_set_key))); 50 const repo_forks = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, repo_id_to_fork_id_set_key))); 51 52 var existing_maybe: ?Record = null; 53 const existing_cursor_maybe = try records.getCursor(fork_key); 54 if (existing_cursor_maybe) |cursor| { 55 existing_maybe = try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(cursor)); 56 } 57 58 var record = if (record_maybe) |value| 59 value 60 else blk: { 61 var existing = existing_maybe orelse return error.EventNotFound; 62 existing.removed = true; 63 break :blk existing; 64 }; 65 66 if (!record.removed) try validate(record); 67 68 if (existing_maybe) |existing| { 69 record.created_order = existing.created_order; 70 if (!existing.removed) { 71 const order_key = evt.orderKeyDesc(existing.created_order, event_id); 72 try removeFromParentSet(DB, hash_kind, user_forks, existing.event.user_id, &order_key); 73 try removeFromParentSet(DB, hash_kind, repo_forks, existing.event.repo_id, &order_key); 74 } 75 } 76 77 const fork_cursor = try records.putCursor(fork_key); 78 try evt.upsert(Record, DB, hash_kind, try DB.HashMap(.read_write).init(fork_cursor), record); 79 try evt.indexEvent(DB, hash_kind, haxy_moment, event_id, .fork, record.created_order, record.removed); 80 81 const order_key = evt.orderKeyDesc(record.created_order, event_id); 82 if (existing_cursor_maybe == null) { 83 const ids = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, id_set_key))); 84 try ids.put(&order_key); 85 } 86 87 if (!record.removed) { 88 const by_user = try DB.SortedSet(.read_write).init(try user_forks.putCursor(hash.hashInt(hash_kind, record.event.user_id))); 89 try by_user.put(&order_key); 90 const by_repo = try DB.SortedSet(.read_write).init(try repo_forks.putCursor(hash.hashInt(hash_kind, record.event.repo_id))); 91 try by_repo.put(&order_key); 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 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