╭ changed ─────────╮ │src/test/event.zig│ ╰──────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────╮ │40 │ │41 const first_event_id = evt.randomId(prng.random()); │ │42 │ │43 - const events_to_process = [_]evt.Event{ │ │43 + const events_to_consume = [_]evt.Event{ │ │44 .{ │ │45 .id = std.fmt.bytesToHex(first_event_id, .lower),│ │46 .data = .{ │ ╰─────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │ 95 var json: std.Io.Writer.Allocating = .init(std.testing.allocator);│ │ 96 defer json.deinit(); │ │ 97 │ │ 98 - for (events_to_process) |event| { │ │ 98 + for (events_to_consume) |event| { │ │ 99 json.clearRetainingCapacity(); │ │100 │ │101 try std.json.Stringify.value(event, .{}, &json.writer); │ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │109 // read and parse all of the events from the repo │ │110 // │ │111 │ │112 - var events: std.ArrayList(evt.Event) = .empty; │ │112 + var events: std.ArrayList(evt.RepoEvent(repo_opts.hash)) = .empty;│ │113 defer events.deinit(allocator); │ │114 │ │115 { │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │116 - var event_strs: std.ArrayList([]const u8) = .empty; │ │117 - defer event_strs.deinit(allocator); │ │116 + const Commit = struct { │ │117 + oid: [hash.byteLen(repo_opts.hash)]u8, │ │118 + message: []const u8, │ │119 + }; │ │120 + var commits: std.ArrayList(Commit) = .empty; │ │121 + defer commits.deinit(allocator); │ │122 │ │123 const ref_haxy_meta = try repo.readRef(io, .{ .kind = .head, .name = "haxy/meta" }) orelse return error.RefNotFound;│ │124 var commit_iter = try repo.log(io, allocator, &.{ref_haxy_meta}); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │131 try commit_object.object_reader.seekTo(commit_object.content.commit.message_position); │ │132 const message = try commit_object.object_reader.interface.allocRemaining(arena.allocator(), .unlimited);│ │133 │ │130 - try event_strs.append(allocator, message); │ │134 + var commit: Commit = .{ │ │135 + .oid = undefined, │ │136 + .message = message, │ │137 + }; │ │138 + _ = try std.fmt.hexToBytes(&commit.oid, &commit_object.oid); │ │139 + │ │140 + try commits.append(allocator, commit); │ │141 } │ │142 │ │143 // parse commit messages as JSON into event values. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │144 // add events in reverse order so the earliest event is first. │ │135 - for (0..event_strs.items.len) |i| { │ │136 - const event_str = event_strs.items[event_strs.items.len - i - 1]; │ │137 - const event = try std.json.parseFromSliceLeaky(evt.Event, arena.allocator(), event_str, .{}); │ │138 - try events.append(allocator, event); │ │145 + for (0..commits.items.len) |i| { │ │146 + const commit = commits.items[commits.items.len - i - 1]; │ │147 + const event = try std.json.parseFromSliceLeaky(evt.Event, arena.allocator(), commit.message, .{});│ │148 + try events.append(allocator, .{ .oid = commit.oid, .event = event }); │ │149 } │ │150 } │ │151 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │152 // │ │143 - // process events into the database │ │153 + // consume events into the database │ │154 // │ │155 │ │146 - const Ctx = struct { │ │147 - events: []evt.Event, │ │156 + try evt.consume(Repo.DB, repo_opts.hash, io, &repo.core.db, repo.core.db_file, events.items); │ │157 │ │149 - pub fn run(ctx: @This(), cursor: *Repo.DB.Cursor(.read_write)) !void { │ │150 - const moment = try Repo.DB.HashMap(.read_write).init(cursor.*); │ │151 - │ │152 - // the map with all of haxy's state and materialized views │ │153 - const haxy_cursor = try moment.putCursor(hash.hashInt(repo_opts.hash, "haxy")); │ │154 - const haxy = try Repo.DB.HashMap(.read_write).init(haxy_cursor); │ │155 - │ │156 - // try reading the last event id that was processed in the db │ │157 - var last_event_id_maybe: ?[evt.id_size]u8 = null; │ │158 - if (try haxy.getCursor(hash.hashInt(repo_opts.hash, "last-event-id"))) |last_event_id_cursor| { │ │159 - var last_event_id_buffer: [evt.id_size]u8 = undefined; │ │160 - _ = try last_event_id_cursor.readBytes(&last_event_id_buffer); │ │161 - last_event_id_maybe = last_event_id_buffer; │ │162 - } │ │158 + const history = try Repo.DB.ArrayList(.read_only).init(repo.core.db.rootCursor().readOnly()); │ │159 │ │164 - // for each event we want to process... │ │165 - for (ctx.events) |event| { │ │166 - // get the id of the current event as bytes │ │167 - var current_event_id_buffer: [evt.id_size]u8 = undefined; │ │168 - _ = try std.fmt.hexToBytes(&current_event_id_buffer, &event.id); │ │169 - │ │170 - // map with the views as they appeared when each event was processed. │ │171 - // we can use this to see (and revert) the views to any previous state. │ │172 - const event_id_to_views_cursor = try haxy.putCursor(hash.hashInt(repo_opts.hash, "event-id->views")); │ │173 - const event_id_to_views = try Repo.DB.HashMap(.read_write).init(event_id_to_views_cursor); │ │174 - │ │175 - // create a new views map for the current event we are processing │ │176 - var views_cursor = try event_id_to_views.putCursor(hash.hashInt(repo_opts.hash, &current_event_id_buffer)); │ │177 - │ │178 - // if there was a previous event, set the views map to have the same value as it. │ │179 - // this efficiently "clones" the map so we make further modifications based on it. │ │180 - if (last_event_id_maybe) |*last_event_id| { │ │181 - if (try event_id_to_views.getCursor(hash.hashInt(repo_opts.hash, last_event_id))) |last_view_cursor| { │ │182 - try views_cursor.write(.{ .slot = last_view_cursor.slot() }); │ │183 - } │ │184 - } │ │185 - │ │186 - // process the event into the views map │ │187 - { │ │188 - const views = try Repo.DB.HashMap(.read_write).init(views_cursor); │ │189 - │ │190 - switch (event.data) { │ │191 - .issue => |data| { │ │192 - const event_id_to_issue_cursor = try views.putCursor(hash.hashInt(repo_opts.hash, "event-id->issue")); │ │193 - const event_id_to_issue = try Repo.DB.HashMap(.read_write).init(event_id_to_issue_cursor); │ │194 - │ │195 - const issue_cursor = try event_id_to_issue.putCursor(hash.hashInt(repo_opts.hash, &current_event_id_buffer));│ │196 - const issue = try Repo.DB.HashMap(.read_write).init(issue_cursor); │ │197 - │ │198 - try putIfDifferent(issue, hash.hashInt(repo_opts.hash, "id"), &current_event_id_buffer); │ │199 - try putIfDifferent(issue, hash.hashInt(repo_opts.hash, "title"), data.title); │ │200 - try putIfDifferent(issue, hash.hashInt(repo_opts.hash, "description"), data.description); │ │201 - }, │ │202 - } │ │203 - } │ │204 - │ │205 - // the current event id is now the last one │ │206 - last_event_id_maybe = current_event_id_buffer; │ │207 - │ │208 - // prevent any of the data created above from being mutated by future iterations of this loop │ │209 - try cursor.db.freeze(); │ │210 - } │ │211 - │ │212 - if (last_event_id_maybe) |*last_event_id| { │ │213 - try haxy.put(hash.hashInt(repo_opts.hash, "last-event-id"), .{ .bytes = last_event_id }); │ │214 - } │ │215 - } │ │216 - │ │217 - fn putIfDifferent(map: Repo.DB.HashMap(.read_write), key: hash.HashInt(repo_opts.hash), value: []const u8) !void { │ │218 - if (try map.getCursor(key)) |value_cursor| { │ │219 - var buffer: [4096]u8 = undefined; │ │220 - const existing_value = try value_cursor.readBytes(&buffer); │ │221 - if (std.mem.eql(u8, existing_value, value)) { │ │222 - return; │ │223 - } │ │224 - } │ │225 - │ │226 - try map.put(key, .{ .bytes = value }); │ │227 - } │ │228 - }; │ │229 - │ │230 - try repo.core.db_file.lock(io, .exclusive); │ │231 - defer repo.core.db_file.unlock(io); │ │232 - │ │233 - // create a new transaction in the database that runs the above-defined Ctx function │ │234 - const history = try Repo.DB.ArrayList(.read_write).init(repo.core.db.rootCursor()); │ │235 - try history.appendContext(.{ .slot = try history.getSlot(-1) }, Ctx{ .events = events.items }); │ │236 - │ │160 // read the moment we just created │ │161 const moment_cursor = try history.getCursor(-1) orelse return error.NotFound; │ │162 const moment = try Repo.DB.HashMap(.read_only).init(moment_cursor); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │164 const haxy_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy")) orelse return error.NotFound; │ │165 const haxy = try Repo.DB.HashMap(.read_only).init(haxy_cursor); │ │166 │ │244 - const event_id_to_views_cursor = try haxy.getCursor(hash.hashInt(repo_opts.hash, "event-id->views")) orelse return error.NotFound; │ │245 - const event_id_to_views = try Repo.DB.HashMap(.read_only).init(event_id_to_views_cursor); │ │167 + const object_id_to_views_cursor = try haxy.getCursor(hash.hashInt(repo_opts.hash, "object-id->views")) orelse return error.NotFound;│ │168 + const object_id_to_views = try Repo.DB.HashMap(.read_only).init(object_id_to_views_cursor); │ │169 │ │170 // make sure the issue from the first event was correctly edited │ │171 { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │249 - // get the last event id │ │250 - const last_event_id_cursor = try haxy.getCursor(hash.hashInt(repo_opts.hash, "last-event-id")) orelse return error.NotFound; │ │251 - var last_event_id_buffer: [evt.id_size]u8 = undefined; │ │252 - _ = try last_event_id_cursor.readBytes(&last_event_id_buffer); │ │172 + // get the last object id │ │173 + const last_object_id_cursor = try haxy.getCursor(hash.hashInt(repo_opts.hash, "last-object-id")) orelse return error.NotFound; │ │174 + var last_object_id: [hash.byteLen(repo_opts.hash)]u8 = undefined; │ │175 + _ = try last_object_id_cursor.readBytes(&last_object_id); │ │176 │ │254 - // get the latest views (the views generated by the last event id) │ │255 - const views_cursor = try event_id_to_views.getCursor(hash.hashInt(repo_opts.hash, &last_event_id_buffer)) orelse return error.NotFound;│ │177 + // get the latest views (the views generated by the last object id) │ │178 + const views_cursor = try object_id_to_views.getCursor(hash.bytesToInt(repo_opts.hash, &last_object_id)) orelse return error.NotFound; │ │179 const views = try Repo.DB.HashMap(.read_only).init(views_cursor); │ │180 │ │181 // get the map of issues │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │190 const first_issue_description_cursor = try first_issue.getCursor(hash.hashInt(repo_opts.hash, "description")) orelse return error.NotFound;│ │191 const first_issue_description_value = try first_issue_description_cursor.readBytesAlloc(allocator, null); │ │192 defer allocator.free(first_issue_description_value); │ │270 - try std.testing.expectEqualStrings(events_to_process[1].data.issue.description, first_issue_description_value); │ │193 + try std.testing.expectEqualStrings(events_to_consume[1].data.issue.description, first_issue_description_value); │ │194 } │ │195 } │ │196 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯