krz/hutch
an ios client for sourcehut
clone: git clone https://gitbay.org/krz/hutch.git
v2.11.0: Hutch/Models/Builds.swift · raw
1import Foundation
2
3// MARK: - Enums
4
5/// Status of a build job.
6enum JobStatus: String, Codable, Sendable {
7 case pending = "PENDING"
8 case queued = "QUEUED"
9 case running = "RUNNING"
10 case success = "SUCCESS"
11 case failed = "FAILED"
12 case cancelled = "CANCELLED"
13 case timeout = "TIMEOUT"
14
15 /// Whether the job can be cancelled.
16 var isCancellable: Bool {
17 switch self {
18 case .pending, .queued, .running: true
19 default: false
20 }
21 }
22
23 /// Whether the job has reached a terminal state and no longer changes.
24 var isTerminal: Bool {
25 switch self {
26 case .success, .failed, .cancelled, .timeout: true
27 case .pending, .queued, .running: false
28 }
29 }
30}
31
32/// Status of a single build task within a job.
33enum TaskStatus: String, Codable, Sendable {
34 case pending = "PENDING"
35 case running = "RUNNING"
36 case success = "SUCCESS"
37 case failed = "FAILED"
38 case skipped = "SKIPPED"
39}
40
41// MARK: - Build Task
42
43/// A single task within a build job.
44struct BuildTask: Codable, Sendable, Identifiable, Equatable {
45 private(set) var ordinal: Int?
46 let name: String
47 let status: TaskStatus
48 let log: BuildLog?
49
50 var id: String {
51 if let ordinal {
52 return "\(ordinal):\(name)"
53 }
54 return [name, log?.fullURL, status.rawValue]
55 .compactMap { $0 }
56 .joined(separator: "::")
57 }
58
59 var logCacheKey: String {
60 id
61 }
62
63 func withOrdinal(_ ordinal: Int) -> BuildTask {
64 var task = self
65 task.ordinal = ordinal
66 return task
67 }
68
69 private enum CodingKeys: String, CodingKey {
70 case name, status, log
71 }
72}
73
74// MARK: - Job Summary (for list view)
75
76/// Lightweight job model matching the fields returned by the jobs list query.
77struct JobSummary: Codable, Sendable, Identifiable, Hashable {
78 let id: Int
79 let created: Date
80 let updated: Date
81 let status: JobStatus
82 let note: String?
83 let tags: [String]
84 let visibility: Visibility?
85 let image: String?
86 let tasks: [JobTaskSummary]
87
88 /// Number of completed (success) tasks.
89 var completedTaskCount: Int {
90 tasks.filter { $0.status == .success }.count
91 }
92
93 /// Display label: note if available, otherwise tags joined.
94 var displayLabel: String {
95 if let note, !note.isEmpty {
96 return note
97 }
98 if !tags.isEmpty {
99 return tags.joined(separator: ", ")
100 }
101 return "Job #\(id)"
102 }
103}
104
105/// Minimal task info for the list query.
106struct JobTaskSummary: Codable, Sendable, Hashable {
107 let name: String
108 let status: TaskStatus
109}
110
111// MARK: - Job Detail (for detail view)
112
113/// Full job model with all fields for the detail view.
114struct JobDetail: Codable, Sendable, Equatable {
115 let id: Int
116 let created: Date
117 let updated: Date
118 let status: JobStatus
119 let note: String?
120 let tags: [String]
121 let visibility: Visibility?
122 let image: String?
123 let manifest: String?
124 var tasks: [BuildTask]
125 let log: BuildLog?
126 let owner: Entity
127}
128
129/// The log associated with a build job.
130struct BuildLog: Codable, Sendable, Equatable {
131 let fullURL: String
132}
133
134// MARK: - Job Group
135
136/// A group of related build jobs.
137struct JobGroup: Codable, Sendable, Identifiable {
138 let id: Int
139 let created: Date
140 let note: String?
141 let jobs: [JobSummary]
142}