/* * darwin-apk © 2024 Gay Pizza Specifications * SPDX-License-Identifier: Apache-2.0 */ import Foundation public struct ApkIndexPackage: Hashable, Sendable { public let indexChecksum: ApkIndexDigest public let name: String public let version: String public let architecture: String? public let packageSize: UInt64 public let installedSize: UInt64 public let packageDescription: String public let url: String public let license: String public let origin: String? public let maintainer: String? public let buildTime: Date? public let commit: String? public let providerPriority: UInt16? public let dependencies: [ApkIndexDependency] public let provides: [ApkIndexProvides] public let installIf: [ApkIndexInstallIf] public var downloadFilename: String { "\(self.name)-\(version).apk" } init(name: String, version spec: ApkVersionSpecification) { fatalError("Cannot construct an ApkIndexPackage this way") } } extension ApkIndexPackage { init(raw rawEntry: ApkRawIndexEntry) throws(Self.ParseError) { // Required fields var indexChecksum: ApkIndexDigest? = nil var name: String? = nil var version: String? = nil var description: String? = nil var url: String? = nil var license: String? = nil var packageSize: UInt64? = nil var installedSize: UInt64? = nil var dependencies = [ApkIndexDependency]() var provides = [ApkIndexProvides]() var installIf = [ApkIndexInstallIf]() // Optional fields var architecture: String? = nil var origin: String? = nil var maintainer: String? = nil var buildTime: Date? = nil var commit: String? = nil var providerPriority: UInt16? = nil // Read all the raw records for this entry for record in rawEntry.fields { switch record.key { case "P": name = record.value case "V": version = record.value case "T": description = record.value case "U": url = record.value case "L": license = record.value case "A": architecture = record.value case "D": do { dependencies = try record.value.split(separator: " ") .map { .init(requirement: try .init(extract: $0)) } } catch { throw .badValue(key: record.key, cause: error.localizedDescription) } case "C": guard let digest = ApkIndexDigest(decode: record.value) else { throw .badValue(key: record.key, cause: "Invalid SHA digest") } indexChecksum = digest case "S": guard let value = UInt64(record.value, radix: 10) else { throw .badValue(key: record.key, cause: "Invalid size value") } packageSize = value case "I": guard let value = UInt64(record.value, radix: 10) else { throw .badValue(key: record.key, cause: "Invalid installed size value") } installedSize = value case "p": do { provides = try record.value.split(separator: " ") .map { .init(requirement: try .init(extract: $0)) } } catch { throw .badValue(key: record.key, cause: error.localizedDescription) } case "i": do { installIf = try record.value.split(separator: " ") .map { .init(requirement: try .init(extract: $0)) } } catch { throw .badValue(key: record.key, cause: error.localizedDescription) } case "o": origin = record.value case "m": maintainer = record.value case "t": guard let timet = UInt64(record.value, radix: 10), let timetInterval = TimeInterval(exactly: timet) else { throw .badValue(key: record.key, cause: "Invalid build time value") } buildTime = Date(timeIntervalSince1970: timetInterval) case "c": commit = record.value case "k": guard let value = UInt64(record.value, radix: 10), (0..(or error: @autoclosure () -> E) throws(E) -> Wrapped { switch self { case .some(let v): return v case .none: throw error() } } }