Files
luma/Sources/LumaCore/Models/InstrumentCompatibility.swift
Ole André Vadla Ravnås 7c780f8c43 core: Replace incompatibility reason with status
Promote the runtime per-instrument flag from a bare reason
string to an InstrumentStatus enum so later kinds (load,
reload, compile failures) can share the surface. Behaviour
is unchanged: skipIfIncompatible sets .incompatible and the
sidebar still renders a single warning icon.
2026-05-16 02:51:01 +02:00

125 lines
3.8 KiB
Swift

import Foundation
public struct InstrumentCompatibility: Codable, Hashable, Sendable {
public var platforms: Set<String>?
public var osIDs: Set<String>?
public var archs: Set<String>?
public static let universal = InstrumentCompatibility()
public init(
platforms: Set<String>? = nil,
osIDs: Set<String>? = nil,
archs: Set<String>? = nil
) {
self.platforms = platforms?.nilIfEmpty
self.osIDs = osIDs?.nilIfEmpty
self.archs = archs?.nilIfEmpty
}
public var isUniversal: Bool {
platforms == nil && osIDs == nil && archs == nil
}
public func matches(_ params: SystemParameters) -> Bool {
incompatibilityReason(for: params) == nil
}
public func incompatibilityReason(for params: SystemParameters) -> String? {
var requirements: [String] = []
if let platforms, !platforms.contains(params.platform) {
requirements.append("platform \(formatRequirement(platforms, displayedBy: InstrumentCompatibility.platformDisplayName))")
}
if let osIDs, !osIDs.contains(params.osID) {
requirements.append("OS \(formatRequirement(osIDs, displayedBy: InstrumentCompatibility.osDisplayName))")
}
if let archs, !archs.contains(params.arch) {
requirements.append("architecture \(formatRequirement(archs, displayedBy: InstrumentCompatibility.archDisplayName))")
}
guard !requirements.isEmpty else { return nil }
return "Requires \(requirements.joined(separator: ", ")); this session is \(describeSession(params))"
}
private func formatRequirement(_ values: Set<String>, displayedBy display: (String) -> String) -> String {
values.map(display).sorted().joined(separator: "/")
}
private func describeSession(_ params: SystemParameters) -> String {
"\(InstrumentCompatibility.osDisplayName(params.osID))/\(InstrumentCompatibility.archDisplayName(params.arch))"
}
public static func platformDisplayName(_ raw: String) -> String {
platformDisplayNames[raw] ?? raw
}
public static func osDisplayName(_ raw: String) -> String {
osDisplayNames[raw] ?? raw
}
public static func archDisplayName(_ raw: String) -> String {
archDisplayNames[raw] ?? raw
}
private static let platformDisplayNames: [String: String] = [
"windows": "Windows",
"darwin": "Darwin",
"linux": "Linux",
"freebsd": "FreeBSD",
"qnx": "QNX",
"barebone": "Barebone",
]
private static let osDisplayNames: [String: String] = [
"windows": "Windows",
"macos": "macOS",
"linux": "Linux",
"ios": "iOS",
"watchos": "watchOS",
"tvos": "tvOS",
"visionos": "visionOS",
"android": "Android",
"freebsd": "FreeBSD",
"qnx": "QNX",
]
private static let archDisplayNames: [String: String] = [
"ia32": "x86",
"x64": "x86_64",
"arm": "arm",
"arm64": "arm64",
"mips": "MIPS",
]
}
public struct SystemParameters: Hashable, Sendable {
public let platform: String
public let osID: String
public let arch: String
public init(platform: String, osID: String, arch: String) {
self.platform = platform
self.osID = osID
self.arch = arch
}
public init?(raw: [String: Any]) {
guard
let platform = raw["platform"] as? String,
let arch = raw["arch"] as? String,
let os = raw["os"] as? [String: Any],
let osID = os["id"] as? String
else {
return nil
}
self.platform = platform
self.osID = osID
self.arch = arch
}
}
extension Set {
fileprivate var nilIfEmpty: Self? {
isEmpty ? nil : self
}
}