╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │301 if (user_data_maybe) |user_data| { │ │302 const user_cursor = try event_id_to_user.putCursor(user_key); │ │303 const user = try DB.HashMap(.read_write).init(user_cursor); │ │304 - try upsert(DB, repo_opts.hash, user, User, user_data); │ │304 + try upsert(User, DB, repo_opts.hash, user, user_data); │ │305 } else { │ │306 if (!try event_id_to_user.remove(user_key)) return error.EventNotFound;│ │307 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │319 if (repo_data_maybe) |repo_data| { │ │320 const repo_cursor = try event_id_to_repo.putCursor(repo_key); │ │321 const repo = try DB.HashMap(.read_write).init(repo_cursor); │ │322 - try upsert(DB, repo_opts.hash, repo, Repo, repo_data); │ │322 + try upsert(Repo, DB, repo_opts.hash, repo, repo_data); │ │323 │ │324 const user_id_to_repos_cursor = try haxy_moment.putCursor(hash.hashInt(repo_opts.hash, "user-id->repos"));│ │325 const user_id_to_repos = try DB.HashMap(.read_write).init(user_id_to_repos_cursor); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │330 } else { │ │331 if (try event_id_to_repo.getCursor(repo_key)) |existing_repo_cursor| { │ │332 const existing_repo = try DB.HashMap(.read_only).init(existing_repo_cursor); │ │333 - const existing_repo_data = try Repo.read(DB, repo_opts.hash, &arena, existing_repo); │ │333 + const existing_repo_data = try read(Repo, DB, repo_opts.hash, &arena, existing_repo); │ │334 │ │335 const user_id_to_repos_cursor = try haxy_moment.putCursor(hash.hashInt(repo_opts.hash, "user-id->repos"));│ │336 const user_id_to_repos = try DB.HashMap(.read_write).init(user_id_to_repos_cursor); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │352 if (issue_data_maybe) |issue_data| { │ │353 const issue_cursor = try event_id_to_issue.putCursor(issue_key); │ │354 const issue = try DB.HashMap(.read_write).init(issue_cursor); │ │355 - try upsert(DB, repo_opts.hash, issue, Issue, issue_data); │ │355 + try upsert(Issue, DB, repo_opts.hash, issue, issue_data); │ │356 } else { │ │357 if (!try event_id_to_issue.remove(issue_key)) return error.EventNotFound;│ │358 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │376 // reading from xitdb │ │377 // │ │378 │ │379 -pub fn readBytes( │ │379 +pub fn read( │ │380 + comptime T: type, │ │381 + comptime DB: type, │ │382 + comptime hash_kind: hash.HashKind, │ │383 + arena: *std.heap.ArenaAllocator, │ │384 + map: DB.HashMap(.read_only), │ │385 +) !T { │ │386 + var value: T = undefined; │ │387 + │ │388 + switch (@typeInfo(T)) { │ │389 + .@"struct" => |struct_info| { │ │390 + inline for (struct_info.fields) |field| { │ │391 + switch (@typeInfo(field.type)) { │ │392 + .pointer => |pointer_info| { │ │393 + if (pointer_info.size == .slice and pointer_info.child == u8 and pointer_info.is_const) { │ │394 + @field(value, field.name) = try readBytes(DB, hash_kind, arena.allocator(), map, field.name);│ │395 + } else { │ │396 + @compileError("unsupported read field type: " ++ @typeName(field.type)); │ │397 + } │ │398 + }, │ │399 + .array => |array_info| { │ │400 + if (array_info.child == u8) { │ │401 + const bytes = try readBytes(DB, hash_kind, arena.allocator(), map, field.name); │ │402 + if (bytes.len != array_info.len) return error.InvalidByteArrayLength; │ │403 + @memcpy(@field(value, field.name)[0..], bytes); │ │404 + } else { │ │405 + @compileError("unsupported read field type: " ++ @typeName(field.type)); │ │406 + } │ │407 + }, │ │408 + .bool => @field(value, field.name) = try readBool(DB, hash_kind, map, field.name), │ │409 + else => @compileError("unsupported read field type: " ++ @typeName(field.type)), │ │410 + } │ │411 + } │ │412 + }, │ │413 + else => @compileError("read expects a struct"), │ │414 + } │ │415 + │ │416 + return value; │ │417 +} │ │418 + │ │419 +fn readBytes( │ │420 comptime DB: type, │ │421 comptime hash_kind: hash.HashKind, │ │422 allocator: std.mem.Allocator, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────╮ │427 return try cursor.readBytesAlloc(allocator, null);│ │428 } │ │429 │ │390 -pub fn readBool( │ │430 +fn readBool( │ │431 comptime DB: type, │ │432 comptime hash_kind: hash.HashKind, │ │433 map: DB.HashMap(.read_only), │ ╰───────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────╮ │448 // │ │449 │ │450 fn upsert( │ │451 + comptime T: type, │ │452 comptime DB: type, │ │453 comptime hash_kind: hash.HashKind,│ │454 map: DB.HashMap(.read_write), │ ╰───────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │414 - comptime Data: type, │ │415 - data: Data, │ │455 + data: T, │ │456 ) !void { │ │417 - switch (@typeInfo(Data)) { │ │457 + switch (@typeInfo(T)) { │ │458 .@"struct" => |struct_info| { │ │459 inline for (struct_info.fields) |field| { │ │460 try upsertField(DB, hash_kind, map, field.name, field.type, @field(data, field.name));│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯