╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭───────────────────────────────────────────────────────────╮ │ 9 pub const User = @import("event/User.zig"); │ │10 pub const Repo = @import("event/Repo.zig"); │ │11 pub const Issue = @import("event/Issue.zig"); │ │12 +pub const Discussion = @import("event/Discussion.zig");│ │13 pub const Comment = @import("event/Comment.zig"); │ │14 pub const Attachment = @import("event/Attachment.zig");│ │15 │ ╰───────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────╮ │55 return result; │ │56 } │ │57 │ │57 -// kept to eight bytes or fewer, so the db stores them as short bytes│ │58 pub const EventKind = enum { │ │59 user, │ │60 repo, │ ╰─────────────────────────────────────────────────────────────────────────╯ ╭───────────────────╮ │61 issue, │ │62 + discussion,│ │63 comment, │ │64 attach, │ │65 }; │ ╰───────────────────╯ ╭────────────────────────────────╮ │74 user: ?User, │ │75 repo: ?Repo, │ │76 issue: ?Issue, │ │77 + discussion: ?Discussion,│ │78 comment: ?Comment, │ │79 attach: ?Attachment, │ │80 }; │ ╰────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │140 .issue => .{ │ │141 .issue = if (json_event.data) |value| │ │142 try std.json.parseFromValueLeaky(Issue, arena.allocator(), value, .{ .ignore_unknown_fields = true }) │ │143 + else │ │144 + null, │ │145 + }, │ │146 + .discussion => .{ │ │147 + .discussion = if (json_event.data) |value| │ │148 + try std.json.parseFromValueLeaky(Discussion, arena.allocator(), value, .{ .ignore_unknown_fields = true })│ │149 else │ │150 null, │ │151 }, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────╮ │203 .user => .{ .user = null }, │ │204 .repo => .{ .repo = null }, │ │205 .issue => .{ .issue = null }, │ │206 + .discussion => .{ .discussion = null },│ │207 .comment => .{ .comment = null }, │ │208 .attach => .{ .attach = null }, │ │209 }; │ ╰────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │863 } else null; │ │864 try Issue.consume(DB, repo_opts.hash, haxy_moment, &current_event_id, record_maybe, &arena, &repo_event_oid); │ │865 }, │ │866 + .discussion => |event_maybe| { │ │867 + const record_maybe: ?Discussion.Record = if (event_maybe) |event| .{ │ │868 + .event = event, │ │869 + .author_email = authorEmail(commit_object.content.commit.metadata.author orelse ""), │ │870 + .created_ts = created_ts, │ │871 + } else null; │ │872 + try Discussion.consume(DB, repo_opts.hash, haxy_moment, &current_event_id, record_maybe, &arena, &repo_event_oid);│ │873 + }, │ │874 .comment => |event_maybe| { │ │875 const record_maybe: ?Comment.Record = if (event_maybe) |event| .{ │ │876 .event = event, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ added ─────────────────╮ │src/event/Discussion.zig│ ╰────────────────────────╯ ╭ changed ──╮ │src/try.zig│ ╰───────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │729 }; │ │730 } │ │731 try evt.consume(.xit, .{}, io, allocator, &template_repo, evt.events_ref, &comment_events); │ │732 + │ │733 + const discussion_data = [_]struct { │ │734 + title: []const u8, │ │735 + description: []const u8, │ │736 + tags: []const u8, │ │737 + }{ │ │738 + .{ │ │739 + .title = "How should plugins declare capabilities?", │ │740 + .description = "I'd like the manifest to make privileged capabilities explicit without making simple plugins verbose.", │ │741 + .tags = "plugins design", │ │742 + }, │ │743 + .{ │ │744 + .title = "Ideas for making large repositories faster", │ │745 + .description = "This is a place to collect profiling results and discuss which indexing work is worth pursuing first.", │ │746 + .tags = "performance indexing", │ │747 + }, │ │748 + .{ │ │749 + .title = "What should the next release focus on?", │ │750 + .description = "Let's compare the most important reliability fixes with the larger features already in progress.", │ │751 + .tags = "release planning", │ │752 + }, │ │753 + .{ │ │754 + .title = "Improving keyboard navigation", │ │755 + .description = "Share workflows that still require a mouse and suggestions for making their focus behavior predictable.", │ │756 + .tags = "ui keyboard accessibility", │ │757 + }, │ │758 + .{ │ │759 + .title = "Configuration format discussion", │ │760 + .description = "Should the next configuration format favor strict validation or accept common conveniences such as trailing commas?",│ │761 + .tags = "config design", │ │762 + }, │ │763 + }; │ │764 + │ │765 + var discussion_events: [discussion_data.len]evt.EventWithId = undefined; │ │766 + for (discussion_data, 0..) |discussion, i| { │ │767 + discussion_events[i] = .{ │ │768 + .id = std.fmt.bytesToHex(evt.EventWithId.randomId(prng.random()), .lower), │ │769 + .timestamp = @intCast(300 + i), │ │770 + .author = .{ .name = user_data[i % user_data.len].name, .email = user_data[i % user_data.len].email }, │ │771 + .event = .{ .discussion = .{ │ │772 + .title = discussion.title, │ │773 + .description = discussion.description, │ │774 + .tags = discussion.tags, │ │775 + } }, │ │776 + }; │ │777 + } │ │778 + try evt.consume(.xit, .{}, io, allocator, &template_repo, evt.events_ref, &discussion_events); │ │779 + │ │780 + var discussion_comment_ids: [discussion_data.len][3][evt.event_id_size]u8 = undefined; │ │781 + for (&discussion_comment_ids) |*ids| { │ │782 + for (ids) |*id| id.* = evt.EventWithId.randomId(prng.random()); │ │783 + } │ │784 + var discussion_comment_events: [discussion_data.len * 3]evt.EventWithId = undefined; │ │785 + for (&discussion_comment_events, 0..) |*event, i| { │ │786 + const discussion_index = i / 3; │ │787 + const comment_index = i % 3; │ │788 + event.* = .{ │ │789 + .id = std.fmt.bytesToHex(discussion_comment_ids[discussion_index][comment_index], .lower), │ │790 + .timestamp = @intCast(400 + i), │ │791 + .author = .{ .name = user_data[(i + 1) % user_data.len].name, .email = user_data[(i + 1) % user_data.len].email }, │ │792 + .event = .{ .comment = .{ │ │793 + .thread_id = discussion_events[discussion_index].id, │ │794 + .parent_id = if (comment_index < 2) │ │795 + discussion_events[discussion_index].id │ │796 + else │ │797 + std.fmt.bytesToHex(discussion_comment_ids[discussion_index][0], .lower), │ │798 + .body = switch (comment_index) { │ │799 + 0 => "My preference is to start with the smallest useful version and expand it after we have real usage to learn from.", │ │800 + 1 => "It would help to write down the tradeoffs we are accepting so the decision is easy to revisit later.", │ │801 + 2 => "Agreed. A narrow first version should also make it easier to keep the implementation understandable.", │ │802 + else => unreachable, │ │803 + }, │ │804 + } }, │ │805 + }; │ │806 + } │ │807 + try evt.consume(.xit, .{}, io, allocator, &template_repo, evt.events_ref, &discussion_comment_events); │ │808 } │ │809 │ │810 // copy the template to each repo's on-disk location, named by its │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │54 }; │ │55 }, │ │56 .repo => switch (route) { │ │57 - .repo_files, .repo_commits, .repo_refs, .repo_issues, .repo_events, .repo_settings, .repo_auth => .{ .repo = try Repo.init(arena, session, route) }, │ │57 + .repo_files, .repo_commits, .repo_refs, .repo_issues, .repo_discussions, .repo_events, .repo_settings, .repo_auth => .{ .repo = try Repo.init(arena, session, route) },│ │58 else => return error.UnexpectedRoute, │ │59 }, │ │60 }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯