╭ changed ─────────╮ │src/event/Repo.zig│ ╰──────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────────────────────────────────────────╮ │ 8 description: []const u8, │ │ 9 read_access: Access = .private, │ │10 write_access: Access = .private, │ │11 -read_user_ids: []const u8 = "", │ │12 -write_user_ids: []const u8 = "", │ │13 -// collaborators who hold the owner's role without being the creator │ │14 -owner_user_ids: []const u8 = "", │ │11 │ │12 // what the db stores: the event's data plus the commit-derived fields│ │13 pub const Record = struct { │ ╰──────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │46 │ │47 pub const name_max_len = 32; │ │48 │ │53 -// the role `user_id_maybe` holds in the repo. null is a logged-out user, who │ │54 -// gets the base role the access fields give everyone. │ │55 -pub fn roleOf(record: Record, user_id_maybe: ?[evt.event_id_size]u8) Role { │ │56 - const event = record.event; │ │57 - const base: Role = if (event.write_access == .public) .write else if (event.read_access == .public) .read else .none; │ │49 +// the role `user_id_maybe` holds in the repo: its creator owns it, and anyone │ │50 +// else gets the higher of the base role and their grant. `user_moment` is the │ │51 +// owner's, which holds the grants. null is a logged-out user. │ │52 +pub fn roleOf( │ │53 + comptime DB: type, │ │54 + comptime hash_kind: hash.HashKind, │ │55 + user_moment: DB.HashMap(.read_only), │ │56 + arena: *std.heap.ArenaAllocator, │ │57 + record: Record, │ │58 + repo_id: *const [evt.event_id_size]u8, │ │59 + user_id_maybe: ?[evt.event_id_size]u8, │ │60 +) !Role { │ │61 + // what the access fields give everyone, logged in or not │ │62 + const base: Role = if (record.event.write_access == .public) .write else if (record.event.read_access == .public) .read else .none;│ │63 const user_id = user_id_maybe orelse return base; │ │59 - if (std.mem.eql(u8, event.user_id, &user_id)) return .owner; │ │64 + if (std.mem.eql(u8, record.event.user_id, &user_id)) return .owner; │ │65 │ │61 - const user_id_hex = std.fmt.bytesToHex(user_id, .lower); │ │62 - if (containsUserId(event.owner_user_ids, &user_id_hex)) return .owner; │ │63 - if (base == .write or containsUserId(event.write_user_ids, &user_id_hex)) return .write; │ │64 - if (base == .read or containsUserId(event.read_user_ids, &user_id_hex)) return .read; │ │65 - return .none; │ │66 + const granted = (try evt.Grant.readRole(DB, hash_kind, user_moment, arena, repo_id, &user_id)) orelse return base; │ │67 + return if (granted.atLeast(base)) granted else base; │ │68 } │ │69 │ │68 -fn containsUserId(user_ids: []const u8, user_id_hex: []const u8) bool { │ │69 - var lines = std.mem.tokenizeScalar(u8, user_ids, '\n'); │ │70 - while (lines.next()) |id| { │ │71 - if (std.mem.eql(u8, id, user_id_hex)) return true; │ │72 - } │ │73 - return false; │ │74 -} │ │75 - │ │70 // the moment keys `evt.merge` reads and writes for this kind │ │71 pub const merge_policy: evt.MergePolicy = .target_wins; │ │72 pub const record_map_key = "event-id->repo"; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────╮ │84 } │ │85 } │ │86 │ │93 -fn validateUserIds(user_ids: []const u8) !void { │ │94 - var lines = std.mem.tokenizeScalar(u8, user_ids, '\n');│ │95 - while (lines.next()) |id| _ = try evt.parseEventId(id);│ │96 -} │ │97 - │ │87 pub fn consume( │ │88 comptime DB: type, │ │89 comptime hash_kind: hash.HashKind, │ ╰───────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │110 │ │111 var record_to_write = record_maybe orelse try evt.removedRecord(Record, DB, hash_kind, haxy_moment.readOnly(), existing_record_maybe);│ │112 │ │124 - if (!record_to_write.removed) { │ │125 - try validateName(record_to_write.event.name); │ │126 - try validateUserIds(record_to_write.event.read_user_ids); │ │127 - try validateUserIds(record_to_write.event.write_user_ids); │ │128 - try validateUserIds(record_to_write.event.owner_user_ids); │ │129 - } │ │113 + if (!record_to_write.removed) try validateName(record_to_write.event.name); │ │114 │ │115 if (existing_record_maybe) |existing_record| { │ │116 // updates preserve the original creation metadata │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────╮ │148 event_id: [evt.event_id_size]u8, │ │149 }; │ │150 │ │151 +// a repo as one viewer sees it │ │152 +pub const RepoWithRole = struct { │ │153 + repo: Record, │ │154 + event_id: [evt.event_id_size]u8, │ │155 + role: Role, │ │156 +}; │ │157 + │ │158 // read a repo by its name from its owner's user repo│ │159 pub fn readByName( │ │160 comptime DB: type, │ ╰──────────────────────────────────────────────────────────╯