╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1143 .status = .not_found, │ │1144 .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }}, │ │1145 }); │ │1056 -} │ │1057 - │ │1058 -// re-emit the issue `parts` names with `update` applied, in the repo the │ │1059 -// host resolves the base to. error.NotFound when the base names no repo or │ │1060 -// the id no issue. │ │1061 -fn updateIssue( │ │1062 - io: std.Io, │ │1063 - allocator: std.mem.Allocator, │ │1064 - host: Host, │ │1065 - parts: IssueBaseParts, │ │1066 - update: evt.Issue.Update, │ │1067 - author: evt.CommitAuthor, │ │1068 -) !void { │ │1069 - const request_repo = (try requestRepoSource(io, allocator, host, parts.repo_base)) orelse return error.NotFound; │ │1070 - defer request_repo.deinit(allocator); │ │1071 - const source = request_repo.source; │ │1072 - switch (source.repo_kind) { │ │1073 - inline else => |repo_kind| { │ │1074 - var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, source.localInitOpts()); │ │1075 - defer any_repo.deinit(io, allocator); │ │1076 - switch (any_repo) { │ │1077 - inline else => |*repo| try evt.Issue.update(repo_kind, repo.self_repo_opts, io, allocator, repo, &parts.id_bytes, update, author),│ │1078 - } │ │1079 - }, │ │1080 - } │ │1146 } │ │1147 │ │1148 fn updateDiscussion( │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │1173 // set the status of the issue the url names (base is │ │1174 // "/repo/<owner>/<name>/issue:<id>", identity elided in local mode) by│ │1175 // re-emitting its event, then redirect back to it │ │1111 -fn handleIssueStatus( │ │1176 +fn handleThreadStatus( │ │1177 io: std.Io, │ │1178 request: *std.http.Server.Request, │ │1179 allocator: std.mem.Allocator, │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │1180 base: []const u8, │ │1181 host: Host, │ │1117 - status: evt.Issue.Status, │ │1182 + open: bool, │ │1183 ) !void { │ │1119 - const not_found = "issue not found"; │ │1120 - const parts = issueBaseParts(base) orelse { │ │1184 + const not_found = "thread not found"; │ │1185 + const parts = commentBaseParts(base) orelse { │ │1186 try request.respond(not_found, .{ │ │1187 .status = .not_found, │ │1188 .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }},│ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1194 defer author_arena.deinit(); │ │1195 const author = (try eventAuthor(io, allocator, &author_arena, request, host)) orelse return respondLoginRequired(request); │ │1196 │ │1132 - updateIssue(io, allocator, host, parts, .{ .status = status }, author) catch |err| switch (err) { │ │1197 + (switch (parts.thread_kind) { │ │1198 + .issue => updateThread(evt.Issue, io, allocator, host, parts.repo_base, &parts.thread_id, .{ .status = if (open) .open else .closed }, author),│ │1199 + .patch => updateThread(evt.Patch, io, allocator, host, parts.repo_base, &parts.thread_id, .{ .status = if (open) .open else .closed }, author),│ │1200 + else => return respondRemoveNotFound(request), │ │1201 + }) catch |err| switch (err) { │ │1202 error.NotFound => { │ │1203 try request.respond(not_found, .{ │ │1204 .status = .not_found, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1231 defer author_arena.deinit(); │ │1232 const author = (try eventAuthor(io, allocator, &author_arena, request, host)) orelse return respondLoginRequired(request);│ │1233 │ │1165 - var body_buf: [256]u8 = undefined; │ │1166 - const reader = request.readerExpectNone(&body_buf); │ │1167 - const body = reader.allocRemaining(allocator, .limited(65536)) catch &[_]u8{}; │ │1234 + const body = try readFormBody(request, allocator); │ │1235 defer allocator.free(body); │ │1236 │ │1237 const title = (try parseFormField(allocator, body, "title")) orelse try allocator.dupe(u8, ""); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │1248 // invalid fields send the user back to the edit form │ │1249 const valid = switch (parts.thread_kind) { │ │1250 .issue => evt.Issue.fieldsValid(title, tags), │ │1251 + .patch => evt.Patch.fieldsValid(title, tags), │ │1252 .discuss => evt.Discussion.fieldsValid(title, tags),│ │1253 else => unreachable, │ │1254 }; │ ╰──────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────╮ │1264 │ │1265 const not_found = switch (parts.thread_kind) {│ │1266 .issue => "issue not found", │ │1267 + .patch => "patch not found", │ │1268 .discuss => "discussion not found", │ │1269 else => unreachable, │ │1270 }; │ ╰────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1271 (switch (parts.thread_kind) { │ │1203 - .issue => updateIssue(io, allocator, host, .{ .repo_base = parts.repo_base, .id_bytes = parts.thread_id }, .{ .fields = .{│ │1272 + .issue => updateThread(evt.Issue, io, allocator, host, parts.repo_base, &parts.thread_id, .{ .fields = .{ │ │1273 + .title = title, │ │1274 + .tags = tags, │ │1275 + .description = description, │ │1276 + } }, author), │ │1277 + .patch => updateThread(evt.Patch, io, allocator, host, parts.repo_base, &parts.thread_id, .{ .fields = .{ │ │1278 .title = title, │ │1279 .tags = tags, │ │1280 .description = description, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────╮ │1298 }); │ │1299 } │ │1300 │ │1227 -// resolve the conflict of the issue the url names (base is │ │1228 -// "/repo/<owner>/<name>/issue:<id>", identity elided in local mode; the│ │1229 -// form posts to "<base>/resolve") │ │1230 -fn handleIssueResolve( │ │1301 +// resolve every conflicted field of the thread the url names │ │1302 +fn handleThreadResolve( │ │1303 io: std.Io, │ │1304 request: *std.http.Server.Request, │ │1305 allocator: std.mem.Allocator, │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1310 defer author_arena.deinit(); │ │1311 const author = (try eventAuthor(io, allocator, &author_arena, request, host)) orelse return respondLoginRequired(request);│ │1312 │ │1241 - var body_buf: [256]u8 = undefined; │ │1242 - const reader = request.readerExpectNone(&body_buf); │ │1243 - const body = reader.allocRemaining(allocator, .limited(65536)) catch &[_]u8{}; │ │1313 + const body = try readFormBody(request, allocator); │ │1314 defer allocator.free(body); │ │1315 │ │1316 var field_arena = std.heap.ArenaAllocator.init(allocator); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯