krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

v2.1: 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
 24/// Status of a single build task within a job.
 25enum TaskStatus: String, Codable, Sendable {
 26    case pending = "PENDING"
 27    case running = "RUNNING"
 28    case success = "SUCCESS"
 29    case failed = "FAILED"
 30    case skipped = "SKIPPED"
 31}
 32
 33// MARK: - Build Task
 34
 35/// A single task within a build job.
 36struct BuildTask: Codable, Sendable, Identifiable {
 37    private(set) var ordinal: Int?
 38    let name: String
 39    let status: TaskStatus
 40    let log: BuildLog?
 41
 42    var id: String {
 43        if let ordinal {
 44            return "\(ordinal):\(name)"
 45        }
 46        return [name, log?.fullURL, status.rawValue]
 47            .compactMap { $0 }
 48            .joined(separator: "::")
 49    }
 50
 51    var logCacheKey: String {
 52        id
 53    }
 54
 55    func withOrdinal(_ ordinal: Int) -> BuildTask {
 56        var task = self
 57        task.ordinal = ordinal
 58        return task
 59    }
 60
 61    private enum CodingKeys: String, CodingKey {
 62        case name, status, log
 63    }
 64}
 65
 66// MARK: - Job Summary (for list view)
 67
 68/// Lightweight job model matching the fields returned by the jobs list query.
 69struct JobSummary: Codable, Sendable, Identifiable, Hashable {
 70    let id: Int
 71    let created: Date
 72    let updated: Date
 73    let status: JobStatus
 74    let note: String?
 75    let tags: [String]
 76    let visibility: Visibility?
 77    let image: String?
 78    let tasks: [JobTaskSummary]
 79
 80    /// Number of completed (success) tasks.
 81    var completedTaskCount: Int {
 82        tasks.filter { $0.status == .success }.count
 83    }
 84
 85    /// Display label: note if available, otherwise tags joined.
 86    var displayLabel: String {
 87        if let note, !note.isEmpty {
 88            return note
 89        }
 90        if !tags.isEmpty {
 91            return tags.joined(separator: ", ")
 92        }
 93        return "Job #\(id)"
 94    }
 95}
 96
 97/// Minimal task info for the list query.
 98struct JobTaskSummary: Codable, Sendable, Hashable {
 99    let name: String
100    let status: TaskStatus
101}
102
103// MARK: - Job Detail (for detail view)
104
105/// Full job model with all fields for the detail view.
106struct JobDetail: Codable, Sendable {
107    let id: Int
108    let created: Date
109    let updated: Date
110    let status: JobStatus
111    let note: String?
112    let tags: [String]
113    let visibility: Visibility?
114    let image: String?
115    let manifest: String?
116    var tasks: [BuildTask]
117    let log: BuildLog?
118    let owner: Entity
119}
120
121/// The log associated with a build job.
122struct BuildLog: Codable, Sendable {
123    let fullURL: String
124}
125
126// MARK: - Job Group
127
128/// A group of related build jobs.
129struct JobGroup: Codable, Sendable, Identifiable {
130    let id: Int
131    let created: Date
132    let note: String?
133    let jobs: [JobSummary]
134}