╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Attachment.zig ───────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
104 const attachment_id_set_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, all_id_set_key)); 105 const attachment_id_set = try DB.SortedSet(.read_write).init(attachment_id_set_cursor); 106 try attachment_id_set.put(&evt.orderKeyDesc(record_to_write.created_order, event_id)); 107 } 108 109 // removed attachments stay in the parent's set; the views skip them 110 const parent_attachments_cursor = try parent_id_to_attachment_id_set.putCursor(hash.hashInt(hash_kind, &record_to_write.event.parent_id)); 111 const parent_attachments = try DB.SortedSet(.read_write).init(parent_attachments_cursor); 112 try parent_attachments.put(&evt.orderKey(record_to_write.created_order, event_id)); 113 } 114 115 // attach `blob` to a parent event and return the event id. `repo` must be 116 // writable. 117 pub fn create( 118 comptime repo_kind: rp.RepoKind, 119 comptime repo_opts: rp.RepoOpts(repo_kind), 120 io: std.Io, 121 allocator: std.mem.Allocator, 122 repo: *rp.Repo(repo_kind, repo_opts), 123 parent_id: *const [evt.event_id_size * 2]u8, 124 blob: evt.Blob, 125 author: evt.CommitAuthor, 126 ) ![evt.event_id_size * 2]u8 { 127 if (!nameValid(blob.name)) return error.InvalidFields; 128 if (blob.size == 0 or blob.size > max_size) return error.InvalidFields; 129 130 const parent_id_bytes = evt.parseEventId(parent_id) catch return error.InvalidFields; 131 132 { 133 const DB = evt.EventDB(repo_opts.hash); 134 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; 135 defer if (event_db_maybe) |*event_db| event_db.deinit(io, allocator); 136 const haxy_moment = (if (event_db_maybe) |*event_db| 137 evt.currentMomentFromDb(repo_opts.hash, event_db.db) 138 else if (repo_kind == .git) 139 return error.ParentNotFound 140 else 141 evt.currentMoment(repo_opts, repo)) catch return error.ParentNotFound; 142 if (!try parentExists(DB, repo_opts.hash, haxy_moment, &parent_id_bytes)) return error.ParentNotFound; 143 } 144 145 var id_bytes: [evt.event_id_size]u8 = undefined; 146 io.random(&id_bytes); 147 const event_id = std.fmt.bytesToHex(id_bytes, .lower); 148 try evt.consume(.repo, repo_kind, repo_opts, io, allocator, repo, evt.events_ref, &.{.{ 149 .id = event_id, 150 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), 151 .author = author, 152 .tree_entries = &.{.{ .blob = blob }}, 153 .event = .{ .attach = .{ .parent_id = parent_id.* } }, 154 }}); 155 return event_id; 156 } 157 158 // read an attachment by event id, or null if the id isn't a known attachment. 159 // field byte slices are allocated in `arena`. 160 pub fn readById( 161 comptime DB: type, 162 comptime hash_kind: hash.HashKind, 163 haxy_moment: DB.HashMap(.read_only), 164 arena: *std.heap.ArenaAllocator, 165 attachment_id: []const u8, 166 ) !?Record { 167 const records_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, record_map_key)) orelse return null; 168 const records = try DB.HashMap(.read_only).init(records_cursor); 169 const record_cursor = try records.getCursor(hash.hashInt(hash_kind, attachment_id)) orelse return null; 170 const record_map = try DB.HashMap(.read_only).init(record_cursor); 171 return try evt.read(Record, DB, hash_kind, arena, record_map); 172 } 173