diff options
| author | Christian Cleberg <[email protected]> | 2026-07-15 22:41:33 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-07-15 22:41:33 -0500 |
| commit | 922502a2c74c66034f3ec2db6612f2a36d242042 (patch) | |
| tree | 0867730c955e308437b8c86dd37638c9bff6c9c5 /Hutch/Models/Patchset.swift | |
| parent | b9ec80716ea015de5b6b31395fdc5ff03191398c (diff) | |
| parent | 1d2769fc7a347939275e9130ee174d61d96ea401 (diff) | |
| download | hutch-922502a2c74c66034f3ec2db6612f2a36d242042.tar.gz hutch-922502a2c74c66034f3ec2db6612f2a36d242042.tar.bz2 hutch-922502a2c74c66034f3ec2db6612f2a36d242042.zip | |
Merge pull request #4 from zerolabsco/phase-2-patchsets
Phase 2: patchset review
Diffstat (limited to 'Hutch/Models/Patchset.swift')
| -rw-r--r-- | Hutch/Models/Patchset.swift | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/Hutch/Models/Patchset.swift b/Hutch/Models/Patchset.swift new file mode 100644 index 0000000..330bbbc --- /dev/null +++ b/Hutch/Models/Patchset.swift @@ -0,0 +1,132 @@ +import Foundation + +/// Review state of a patchset on lists.sr.ht. +enum PatchsetStatus: String, Codable, Sendable, CaseIterable { + case unknown = "UNKNOWN" + case proposed = "PROPOSED" + case needsRevision = "NEEDS_REVISION" + case superseded = "SUPERSEDED" + case approved = "APPROVED" + case rejected = "REJECTED" + case applied = "APPLIED" + + var displayName: String { + switch self { + case .unknown: "Unknown" + case .proposed: "Proposed" + case .needsRevision: "Needs Revision" + case .superseded: "Superseded" + case .approved: "Approved" + case .rejected: "Rejected" + case .applied: "Applied" + } + } + + var systemImage: String { + switch self { + case .unknown: "questionmark.circle" + case .proposed: "paperplane" + case .needsRevision: "exclamationmark.arrow.circlepath" + case .superseded: "arrow.triangle.branch" + case .approved: "checkmark.seal" + case .rejected: "xmark.circle" + case .applied: "checkmark.circle.fill" + } + } + + /// Whether the patchset is still awaiting a decision. + var isOpen: Bool { + switch self { + case .unknown, .proposed, .needsRevision: true + case .superseded, .approved, .rejected, .applied: false + } + } + + /// Statuses a reviewer can set directly. + /// + /// `unknown` is a sentinel for patchsets sr.ht could not classify, and + /// `superseded` is set by the server when a later version arrives, so neither + /// is offered as a choice. + static var assignable: [PatchsetStatus] { + [.proposed, .needsRevision, .approved, .rejected, .applied] + } +} + +/// A patchset as it appears in a mailing list listing, derived from the thread's +/// root email rather than a dedicated patchsets query — `MailingList` exposes no +/// such field. +struct PatchsetSummary: Identifiable, Hashable, Sendable { + let id: Int + let subject: String + let version: Int + let prefix: String? + let status: PatchsetStatus + + /// The `[PATCH v2]`-style prefix sr.ht parsed from the subject, if any. + var versionLabel: String? { + guard version > 1 else { return nil } + return "v\(version)" + } +} + +/// One email within a patchset: either the cover letter or a single patch. +struct PatchsetEmail: Identifiable, Hashable, Sendable { + let id: Int + let subject: String + let date: Date? + let sender: Entity + /// Split into commit message and diff blocks for rendering. + let contentBlocks: [InboxMessageContentBlock] + /// Position within the series, from the `[PATCH 2/5]` prefix. + let index: Int? + let count: Int? + + var seriesLabel: String? { + guard let index, let count, count > 1 else { return nil } + return "\(index)/\(count)" + } +} + +/// A build or check reported against a patchset. +struct PatchsetToolResult: Identifiable, Hashable, Sendable { + let id: Int + let icon: PatchsetToolIcon + let details: String +} + +enum PatchsetToolIcon: String, Codable, Sendable { + case pending = "PENDING" + case waiting = "WAITING" + case success = "SUCCESS" + case failed = "FAILED" + case cancelled = "CANCELLED" + + var systemImage: String { + switch self { + case .pending, .waiting: "clock" + case .success: "checkmark.circle.fill" + case .failed: "xmark.circle.fill" + case .cancelled: "minus.circle" + } + } +} + +/// A patchset with its cover letter, patches, and review context. +struct PatchsetDetail: Sendable { + let id: Int + let created: Date + let updated: Date + let subject: String + let version: Int + let prefix: String? + let status: PatchsetStatus + let submitter: Entity + let coverLetter: PatchsetEmail? + let patches: [PatchsetEmail] + /// Set when a newer version of this series exists. + let supersededBy: Int? + /// Set when this series revises an earlier one. + let supersedes: Int? + let tools: [PatchsetToolResult] + let mbox: URL? +} |
