gh-somegeekintn-SimDirs/SimDirs/Model/SimRuntime.swift

154 lines
5.1 KiB
Swift

//
// SimRuntime.swift
// SimDirs
//
// Created by Casey Fleser on 5/24/22.
//
import SwiftUI
struct SimRuntime: Comparable, Decodable {
enum CodingKeys: String, CodingKey {
case availabilityError
case bundlePath
case buildversion
case identifier
case isAvailable
case isInternal
case name
case platform
case runtimeRoot
case supportedDeviceTypes
case version
}
struct DeviceType: Decodable {
let name : String
let bundlePath : String
let identifier : String
let productFamily : SimProductFamily
init(canonical: SimDeviceType) {
name = canonical.name
bundlePath = canonical.bundlePath
identifier = canonical.identifier
productFamily = canonical.productFamily
}
}
let name : String
let version : String
let identifier : String
let platform : SimPlatform
let bundlePath : String
let buildversion : String
let runtimeRoot : String
let isInternal : Bool
let isAvailable : Bool
var supportedDeviceTypes : [DeviceType]
let availabilityError : String?
var devices = [SimDevice]()
var isPlaceholder = false
static func < (lhs: SimRuntime, rhs: SimRuntime) -> Bool {
return lhs.name < rhs.name
}
static func == (lhs: SimRuntime, rhs: SimRuntime) -> Bool {
return lhs.identifier == rhs.identifier
}
init(platformID: String) throws {
guard let lastComponent = platformID.split(separator: ".").last else { throw SimError.deviceParsingFailure }
let vComps = lastComponent.split(separator: "-")
if vComps.count == 3 {
guard let compPlatform = SimPlatform(rawValue: String(vComps[0])) else { throw SimError.deviceParsingFailure }
guard let major = Int(vComps[1]) else { throw SimError.deviceParsingFailure }
guard let minor = Int(vComps[2]) else { throw SimError.deviceParsingFailure }
platform = compPlatform
version = "\(major).\(minor)"
name = "\(platform) \(version)"
identifier = platformID
bundlePath = ""
buildversion = ""
runtimeRoot = ""
isInternal = false
isAvailable = false
supportedDeviceTypes = []
availabilityError = "Missing runtime"
isPlaceholder = true
}
else {
throw SimError.deviceParsingFailure
}
}
func supports(deviceType: SimDeviceType) -> Bool {
return supportedDeviceTypes.contains { $0.identifier == deviceType.identifier }
}
func supports(platform: SimPlatform) -> Bool {
return self.platform == platform
}
mutating func setDevices(_ devices: [SimDevice], from devTypes: [SimDeviceType]) {
self.devices = devices.map { $0.scannedDevice }
// If this runtime is a placeholder it will be missing supported device types
// create device type stubs based on the devices being added using supplied
// fully described device types
if isPlaceholder {
let devTypeIDs = Set(devices.map({ $0.deviceTypeIdentifier }))
self.supportedDeviceTypes = devTypeIDs.compactMap { devTypeID in
devTypes.first(where: { $0.identifier == devTypeID }).map({ SimRuntime.DeviceType(canonical: $0) })
}
}
}
func updatedDevices(from devices: [SimDevice]) -> [SimDevice] {
return devices.compactMap { device in
self.devices.first(where: { $0.udid == device.udid })?.updatedDevice(from: device)
}
}
mutating func applyDeviceUpdates(_ devices: [SimDevice]) {
for device in devices {
guard let devIdx = self.devices.firstIndex(where: { $0.udid == device.udid }) else { continue }
self.devices[devIdx] = device
}
}
}
extension SimRuntime: SourceItemData {
var title : String { return name }
var headerTitle : String { "Runtime: \(title)" }
var imageDesc : SourceImageDesc { .symbol(systemName: "shippingbox", color: isAvailable ? .green : .red) }
var optionTrait : SourceFilter.Options { isAvailable ? .runtimeInstalled : [] }
}
extension Array where Element == SimRuntime {
mutating func indexOfMatchedOrCreated(identifier: String) throws -> Index {
return try firstIndex { $0.identifier == identifier } ?? {
try self.append(SimRuntime(platformID: identifier))
return self.endIndex - 1
}()
}
func supporting(deviceType: SimDeviceType) -> Self {
filter { $0.supports(deviceType: deviceType) }
}
func supporting(platform: SimPlatform) -> Self {
filter { $0.supports(platform: platform) }
}
}