import Foundation /// One issue; `issue list` rows and the header of `issue show`. nonisolated struct Issue: Decodable, Sendable, Hashable, Identifiable { let number: Int64 let title: String let state: String let author: String let milestone: String? let labels: [String]? let assignees: [String]? let body: String? let createdAt: Date enum CodingKeys: String, CodingKey { case number, title, state, author, milestone, labels, assignees, body case createdAt = "created_at" } var id: Int64 { number } var isOpen: Bool { state == "open" } } /// `issue show ` — the issue plus its comments. nonisolated struct IssueDetail: Decodable, Sendable, Hashable { let number: Int64 let title: String let state: String let author: String let milestone: String? let labels: [String]? let assignees: [String]? let body: String? let bodyFormat: String? let createdAt: Date let reactions: [ReactionCount]? let comments: [Comment]? enum CodingKeys: String, CodingKey { case number, title, state, author, milestone, labels, assignees, body, reactions, comments case bodyFormat = "body_format" case createdAt = "created_at" } var isOpen: Bool { state == "open" } nonisolated struct Comment: Decodable, Sendable, Hashable, Identifiable { /// What `--comment` names. Absent from a server older than v1.39.0. let commentID: Int64? let author: String let body: String let bodyFormat: String? let createdAt: Date let reactions: [ReactionCount]? /// "system" for a line the server posted (a retarget, a queued /// merge), which takes no reactions (krz/gitbay#332). let kind: String? enum CodingKeys: String, CodingKey { case author, body, reactions, kind case commentID = "id" case bodyFormat = "body_format" case createdAt = "created_at" } /// A server without `kind` names the author "system" on those lines; /// an account can carry that name too, so `kind` wins when present. var isSystem: Bool { kind.map { $0 == "system" } ?? (author == "system") } var id: String { author + createdAt.timeIntervalSince1970.description + body } } }