gitbay/Repos/RepoModels.swift

5411753023a5e2958f17b523903abce380941107
gitbay-ios/gitbay/Repos/RepoModels.swift history · blame · raw

140 lines · 4316 bytes

  1import Foundation
  2
  3/// One row of `repo list`.
  4nonisolated struct RepoSummary: Decodable, Sendable, Hashable, Identifiable {
  5    let path: String
  6    let visibility: String
  7    let description: String?
  8    let archived: Bool?
  9    /// Present on `repo search` rows, absent on `repo list`.
 10    let topics: [String]?
 11
 12    var id: String { path }
 13    var isArchived: Bool { archived ?? false }
 14    var isPrivate: Bool { visibility == "private" }
 15
 16    /// "krz/gitbay" → ("krz", "gitbay")
 17    var owner: String { String(path.split(separator: "/").first ?? "") }
 18    var name: String { String(path.split(separator: "/").last ?? "") }
 19}
 20
 21/// `repo show <owner/name>`.
 22nonisolated struct RepoDetail: Decodable, Sendable, Hashable {
 23    let path: String
 24    let description: String?
 25    let website: String?
 26    let visibility: String
 27    let defaultBranch: String
 28    let protectedBranches: [String]?
 29    let archived: Bool?
 30    let topics: [String]?
 31
 32    enum CodingKeys: String, CodingKey {
 33        case path, description, website, visibility, archived, topics
 34        case defaultBranch = "default_branch"
 35        case protectedBranches = "protected_branches"
 36    }
 37
 38    var isArchived: Bool { archived ?? false }
 39}
 40
 41/// `repo tree` — one directory listing.
 42nonisolated struct TreeListing: Decodable, Sendable, Hashable {
 43    let path: String
 44    let ref: String
 45    let dir: String
 46    let entries: [TreeEntry]
 47}
 48
 49/// One entry of a tree. The sha is the git object id — content at a sha
 50/// never changes, which is what makes caching on it sound.
 51nonisolated struct TreeEntry: Decodable, Sendable, Hashable, Identifiable {
 52    let name: String
 53    let type: String // blob | tree
 54    let mode: String
 55    let sha: String
 56    let size: Int64?
 57
 58    var id: String { sha + name }
 59    var isDirectory: Bool { type == "tree" }
 60    var isSymlink: Bool { mode == "120000" }
 61}
 62
 63/// `repo cat` — one file. Exactly one of `content`/`base64` is set.
 64nonisolated struct FileContent: Decodable, Sendable, Hashable {
 65    let path: String
 66    let ref: String
 67    let file: String
 68    let size: Int
 69    let binary: Bool
 70    let truncated: Bool?
 71    let content: String?
 72    let base64: String?
 73
 74    var isTruncated: Bool { truncated ?? false }
 75
 76    var data: Data? {
 77        if let content { return Data(content.utf8) }
 78        if let base64 { return Data(base64Encoded: base64) }
 79        return nil
 80    }
 81}
 82
 83/// `repo log` — one commit, with its signature verdict.
 84nonisolated struct Commit: Decodable, Sendable, Hashable, Identifiable {
 85    let sha: String
 86    let subject: String
 87    let authorName: String
 88    let authorEmail: String
 89    let committerEmail: String?
 90    let date: Date
 91    let signature: Signature
 92
 93    enum CodingKeys: String, CodingKey {
 94        case sha, subject, date, signature
 95        case authorName = "author_name"
 96        case authorEmail = "author_email"
 97        case committerEmail = "committer_email"
 98    }
 99
100    var id: String { sha }
101    var shortSHA: String { String(sha.prefix(10)) }
102
103    nonisolated struct Signature: Decodable, Sendable, Hashable {
104        let state: SignatureState
105        let signer: String?
106        let keyFingerprint: String?
107
108        enum CodingKeys: String, CodingKey {
109            case state, signer
110            case keyFingerprint = "key_fingerprint"
111        }
112    }
113}
114
115/// The server's verdict on a commit signature; `internal/sig/verify.go`.
116/// Unknown future states decode as `.unrecognized` rather than failing the
117/// whole log.
118nonisolated enum SignatureState: Sendable, Hashable, Decodable {
119    case verified
120    case signedUnknownKey
121    case signedEmailMismatch
122    case signedKeyExpired
123    case signedKeyRevoked
124    case badSignature
125    case unsigned
126    case unrecognized(String)
127
128    init(from decoder: any Decoder) throws {
129        switch try decoder.singleValueContainer().decode(String.self) {
130        case "verified": self = .verified
131        case "signed_unknown_key": self = .signedUnknownKey
132        case "signed_email_mismatch": self = .signedEmailMismatch
133        case "signed_key_expired": self = .signedKeyExpired
134        case "signed_key_revoked": self = .signedKeyRevoked
135        case "bad_signature": self = .badSignature
136        case "unsigned": self = .unsigned
137        case let other: self = .unrecognized(other)
138        }
139    }
140}