A machine named after its template showed no subheading at all, and with one Linux template the architecture is half the story. Both panels now say template and architecture, whenever that adds to the name.
218 lines
6.2 KiB
Swift
218 lines
6.2 KiB
Swift
import Foundation
|
|
import Frida
|
|
|
|
public struct VirtualMachineTemplate: Identifiable, Sendable, Equatable {
|
|
public let id: String
|
|
public let backendID: String
|
|
public let name: String
|
|
public let summary: String
|
|
public let iconName: String
|
|
public let operatingSystem: VirtualMachineOperatingSystem
|
|
public let variants: [VirtualMachineTemplateVariant]
|
|
public let parameters: [VirtualMachineParameter]
|
|
|
|
public static let architectureParameterID = "architecture"
|
|
|
|
public init(
|
|
id: String,
|
|
backendID: String,
|
|
name: String,
|
|
summary: String,
|
|
iconName: String,
|
|
operatingSystem: VirtualMachineOperatingSystem,
|
|
variants: [VirtualMachineTemplateVariant],
|
|
parameters: [VirtualMachineParameter]
|
|
) {
|
|
self.id = id
|
|
self.backendID = backendID
|
|
self.name = name
|
|
self.summary = summary
|
|
self.iconName = iconName
|
|
self.operatingSystem = operatingSystem
|
|
self.variants = variants
|
|
self.parameters = parameters
|
|
}
|
|
|
|
public init(
|
|
id: String,
|
|
backendID: String,
|
|
name: String,
|
|
summary: String,
|
|
iconName: String,
|
|
operatingSystem: VirtualMachineOperatingSystem,
|
|
architecture: VirtualMachineArchitecture,
|
|
agentFlavor: BareboneAgentFlavor?,
|
|
parameters: [VirtualMachineParameter],
|
|
starterImages: StarterImages? = nil
|
|
) {
|
|
self.init(
|
|
id: id,
|
|
backendID: backendID,
|
|
name: name,
|
|
summary: summary,
|
|
iconName: iconName,
|
|
operatingSystem: operatingSystem,
|
|
variants: [
|
|
VirtualMachineTemplateVariant(
|
|
architecture: architecture,
|
|
agentFlavor: agentFlavor,
|
|
starterImages: starterImages
|
|
)
|
|
],
|
|
parameters: parameters
|
|
)
|
|
}
|
|
|
|
public var architecture: VirtualMachineArchitecture {
|
|
variants[0].architecture
|
|
}
|
|
|
|
public func variant(for parameters: [String: VirtualMachineParameterValue]) -> VirtualMachineTemplateVariant {
|
|
guard let chosen = parameters[Self.architectureParameterID]?.text,
|
|
let match = variants.first(where: { $0.architecture.rawValue == chosen })
|
|
else { return variants[0] }
|
|
return match
|
|
}
|
|
|
|
public func detailLabel(for parameters: [String: VirtualMachineParameterValue]) -> String {
|
|
guard variants.count > 1 else { return name }
|
|
return "\(name) \u{00b7} \(variant(for: parameters).architecture.displayName)"
|
|
}
|
|
|
|
public var icon: Icon? {
|
|
MachineIcon.named(iconName)
|
|
}
|
|
|
|
public var defaultParameterValues: [String: VirtualMachineParameterValue] {
|
|
Dictionary(uniqueKeysWithValues: parameters.map { ($0.id, $0.kind.defaultValue) })
|
|
}
|
|
}
|
|
|
|
public struct VirtualMachineTemplateVariant: Sendable, Equatable {
|
|
public let architecture: VirtualMachineArchitecture
|
|
public let agentFlavor: BareboneAgentFlavor?
|
|
public let starterImages: StarterImages?
|
|
|
|
public init(
|
|
architecture: VirtualMachineArchitecture,
|
|
agentFlavor: BareboneAgentFlavor?,
|
|
starterImages: StarterImages? = nil
|
|
) {
|
|
self.architecture = architecture
|
|
self.agentFlavor = agentFlavor
|
|
self.starterImages = starterImages
|
|
}
|
|
}
|
|
|
|
/// Declared in the order Frida names them, which is the order they are listed
|
|
/// in.
|
|
public enum VirtualMachineOperatingSystem: String, Sendable, Codable, Comparable {
|
|
case windows
|
|
case macOS
|
|
case linux
|
|
case iOS
|
|
case android
|
|
|
|
public static func < (lhs: Self, rhs: Self) -> Bool {
|
|
lhs.rank < rhs.rank
|
|
}
|
|
|
|
private var rank: Int {
|
|
Self.allCases.firstIndex(of: self) ?? 0
|
|
}
|
|
|
|
private static let allCases: [Self] = [.windows, .macOS, .linux, .iOS, .android]
|
|
}
|
|
|
|
public enum VirtualMachineArchitecture: String, Sendable, Codable, Comparable {
|
|
case x86
|
|
case x86_64
|
|
case arm
|
|
case arm64
|
|
|
|
public static func < (lhs: Self, rhs: Self) -> Bool {
|
|
lhs.rank < rhs.rank
|
|
}
|
|
|
|
public var displayName: String {
|
|
switch self {
|
|
case .x86: return "x86"
|
|
case .x86_64: return "x86-64"
|
|
case .arm: return "arm"
|
|
case .arm64: return "arm64"
|
|
}
|
|
}
|
|
|
|
private var rank: Int {
|
|
Self.allCases.firstIndex(of: self) ?? 0
|
|
}
|
|
|
|
private static let allCases: [Self] = [.x86, .x86_64, .arm, .arm64]
|
|
}
|
|
|
|
public struct VirtualMachineParameter: Identifiable, Sendable, Equatable {
|
|
public let id: String
|
|
public let name: String
|
|
public let kind: VirtualMachineParameterKind
|
|
|
|
public init(id: String, name: String, kind: VirtualMachineParameterKind) {
|
|
self.id = id
|
|
self.name = name
|
|
self.kind = kind
|
|
}
|
|
}
|
|
|
|
public enum VirtualMachineParameterKind: Sendable, Equatable {
|
|
case text(default: String)
|
|
case number(default: Int, min: Int, max: Int, unit: String?)
|
|
case filePath(extensions: [String])
|
|
case choice(options: [VirtualMachineParameterOption], default: String)
|
|
case toggle(default: Bool)
|
|
|
|
var defaultValue: VirtualMachineParameterValue {
|
|
switch self {
|
|
case .text(let value):
|
|
return .text(value)
|
|
case .number(let value, _, _, _):
|
|
return .number(value)
|
|
case .filePath:
|
|
return .text("")
|
|
case .choice(_, let value):
|
|
return .text(value)
|
|
case .toggle(let value):
|
|
return .toggle(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct VirtualMachineParameterOption: Identifiable, Sendable, Equatable {
|
|
public let id: String
|
|
public let name: String
|
|
|
|
public init(id: String, name: String) {
|
|
self.id = id
|
|
self.name = name
|
|
}
|
|
}
|
|
|
|
public enum VirtualMachineParameterValue: Sendable, Equatable, Codable {
|
|
case text(String)
|
|
case number(Int)
|
|
case toggle(Bool)
|
|
|
|
public var text: String? {
|
|
guard case .text(let value) = self else { return nil }
|
|
return value
|
|
}
|
|
|
|
public var number: Int? {
|
|
guard case .number(let value) = self else { return nil }
|
|
return value
|
|
}
|
|
|
|
public var toggle: Bool? {
|
|
guard case .toggle(let value) = self else { return nil }
|
|
return value
|
|
}
|
|
}
|