╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭────────────────────────────────╮ │12 │ │13 pub const EventKind = enum {│ │14 user, │ │15 + repo, │ │16 issue, │ │17 }; │ │18 │ ╰────────────────────────────────╯ ╭──────────────────────────────────────╮ │22 email: []const u8, │ │23 password_hash: []const u8,│ │24 }, │ │25 + repo: struct { │ │26 + user_id: []const u8, │ │27 + name: []const u8, │ │28 + enable_issue: bool, │ │29 + }, │ │30 issue: struct { │ │31 title: []const u8, │ │32 description: []const u8, │ ╰──────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │46 .name = try readBytes(DB, hash_kind, allocator, map, "name"), │ │47 .email = try readBytes(DB, hash_kind, allocator, map, "email"), │ │48 .password_hash = try readBytes(DB, hash_kind, allocator, map, "password_hash"),│ │49 + }, │ │50 + }, │ │51 + .repo => .{ │ │52 + .repo = .{ │ │53 + .user_id = try readBytes(DB, hash_kind, allocator, map, "user_id"), │ │54 + .name = try readBytes(DB, hash_kind, allocator, map, "name"), │ │55 + .enable_issue = try readBool(DB, hash_kind, map, "enable_issue"), │ │56 }, │ │57 }, │ │58 .issue => .{ │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │74 ) ![]const u8 { │ │75 const cursor = try map.getCursor(hash.hashInt(hash_kind, field_name)) orelse return error.NotFound; │ │76 return try cursor.readBytesAlloc(allocator, null); │ │77 + } │ │78 + │ │79 + fn readBool( │ │80 + comptime DB: type, │ │81 + comptime hash_kind: hash.HashKind, │ │82 + map: DB.HashMap(.read_only), │ │83 + field_name: []const u8, │ │84 + ) !bool { │ │85 + const cursor = try map.getCursor(hash.hashInt(hash_kind, field_name)) orelse return error.NotFound; │ │86 + var buffer: [5]u8 = undefined; │ │87 + const bytes = try cursor.readBytesObject(&buffer); │ │88 + if (bytes.format_tag == null or !std.mem.eql(u8, &bytes.format_tag.?, "bl")) return error.InvalidFormatTag;│ │89 + if (std.mem.eql(u8, bytes.value, "true")) return true; │ │90 + if (std.mem.eql(u8, bytes.value, "false")) return false; │ │91 + return error.InvalidBool; │ │92 } │ │93 }; │ │94 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │365 │ │366 try upsert(DB, repo_opts.hash, user_map, @TypeOf(data), data); │ │367 }, │ │368 + .repo => |data| { │ │369 + const event_id_to_repo_cursor = try haxy_moment.putCursor(hash.hashInt(repo_opts.hash, "event-id->repo")); │ │370 + const event_id_to_repo = try DB.HashMap(.read_write).init(event_id_to_repo_cursor); │ │371 + │ │372 + const repo_cursor = try event_id_to_repo.putCursor(hash.hashInt(repo_opts.hash, &current_event_id)); │ │373 + const repo_map = try DB.HashMap(.read_write).init(repo_cursor); │ │374 + │ │375 + try upsert(DB, repo_opts.hash, repo_map, @TypeOf(data), data); │ │376 + }, │ │377 .issue => |data| { │ │378 const event_id_to_issue_cursor = try haxy_moment.putCursor(hash.hashInt(repo_opts.hash, "event-id->issue"));│ │379 const event_id_to_issue = try DB.HashMap(.read_write).init(event_id_to_issue_cursor); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │515 │ │516 try map.put(key, .{ .int = value }); │ │517 }, │ │518 + }, │ │519 + .bool => { │ │520 + const bytes = if (value) "true" else "false"; │ │521 + if (try map.getCursor(key)) |value_cursor| { │ │522 + var buffer: [5]u8 = undefined; │ │523 + const existing = try value_cursor.readBytesObject(&buffer); │ │524 + if (existing.format_tag != null and │ │525 + std.mem.eql(u8, &existing.format_tag.?, "bl") and │ │526 + std.mem.eql(u8, existing.value, bytes)) │ │527 + { │ │528 + return; │ │529 + } │ │530 + } │ │531 + │ │532 + try map.put(key, .{ .bytes_object = .{ .value = bytes, .format_tag = "bl".* } });│ │533 }, │ │534 else => @compileError("unsupported upsert field type: " ++ @typeName(Field)), │ │535 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────╮ │src/test/event.zig│ ╰──────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │783 try std.testing.expectError(error.MergeConflict, evt.consume(repo_opts, io, allocator, &repo, .{ .kind = .head, .name = "haxy/meta" }));│ │784 } │ │785 │ │786 -test "user" { │ │786 +test "user and repo" { │ │787 const io = std.testing.io; │ │788 const allocator = std.testing.allocator; │ │789 - const temp_dir_name = "temp-event-user"; │ │789 + const temp_dir_name = "temp-event-user-and-repo"; │ │790 │ │791 // create the temp dir │ │792 const cwd = std.Io.Dir.cwd(); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │819 │ │820 var prng = std.Random.DefaultPrng.init(std.testing.random_seed); │ │821 const user_event_id = evt.randomId(prng.random()); │ │822 + const repo_event_id = evt.randomId(prng.random()); │ │823 │ │824 var first_password_hash_buf: [usr.password_hash_max_len]u8 = undefined; │ │825 const first_password_hash = try usr.hashPassword("correct horse battery staple", &first_password_hash_buf, io);│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────╮ │849 }, │ │850 }, │ │851 }, │ │852 + .{ │ │853 + .id = std.fmt.bytesToHex(repo_event_id, .lower),│ │854 + .data = .{ │ │855 + .repo = .{ │ │856 + .user_id = &user_event_id, │ │857 + .name = "ziglings", │ │858 + .enable_issue = true, │ │859 + }, │ │860 + }, │ │861 + }, │ │862 }; │ │863 │ │864 // │ ╰─────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────╮ │854 - // insert users as commits in the repo │ │865 + // insert users and repos as commits in the repo│ │866 // │ │867 │ │868 { │ ╰─────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯