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 } }