Files
luma/Sources/LumaCore/Models/InstrumentIcon.swift

106 lines
5.6 KiB
Swift

import Foundation
public enum InstrumentIcon: Hashable, Sendable {
case symbolic(String)
case pixels(Data)
}
extension InstrumentIcon: Codable {
private enum CodingKeys: String, CodingKey { case kind, value }
private enum Kind: String, Codable { case symbolic, pixels }
public init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
switch try c.decode(Kind.self, forKey: .kind) {
case .symbolic:
self = .symbolic(try c.decode(String.self, forKey: .value))
case .pixels:
let b64 = try c.decode(String.self, forKey: .value)
guard let data = Data(base64Encoded: b64) else {
throw DecodingError.dataCorruptedError(forKey: .value, in: c, debugDescription: "invalid base64")
}
self = .pixels(data)
}
}
public func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .symbolic(let id):
try c.encode(Kind.symbolic, forKey: .kind)
try c.encode(id, forKey: .value)
case .pixels(let data):
try c.encode(Kind.pixels, forKey: .kind)
try c.encode(data.base64EncodedString(), forKey: .value)
}
}
}
extension InstrumentIcon {
public func toJSON() -> [String: Any] {
switch self {
case .symbolic(let id): return ["kind": "symbolic", "value": id]
case .pixels(let data): return ["kind": "pixels", "value": data.base64EncodedString()]
}
}
public static func fromJSON(_ raw: Any?) -> InstrumentIcon? {
guard let obj = raw as? [String: Any],
let kind = obj["kind"] as? String,
let value = obj["value"] as? String
else { return nil }
switch kind {
case "symbolic": return .symbolic(value)
case "pixels": return Data(base64Encoded: value).map(InstrumentIcon.pixels)
default: return nil
}
}
public func encodedJSONString() -> String {
let data = try! JSONEncoder().encode(self)
return String(decoding: data, as: UTF8.self)
}
public static func decodedJSONString(_ s: String) -> InstrumentIcon {
try! JSONDecoder().decode(InstrumentIcon.self, from: Data(s.utf8))
}
}
public struct InstrumentIconConcept: Sendable, Equatable {
public let id: String
public let displayName: String
public let sfSymbol: String
public let symbolicIcon: String
}
public enum InstrumentIconCatalog {
public static let all: [InstrumentIconConcept] = [
.init(id: "wand-stars", displayName: "Magic Wand", sfSymbol: "wand.and.stars", symbolicIcon: "applications-utilities-symbolic"),
.init(id: "puzzle", displayName: "Puzzle", sfSymbol: "puzzlepiece.extension", symbolicIcon: "preferences-desktop-apps-symbolic"),
.init(id: "bug", displayName: "Bug", sfSymbol: "ladybug", symbolicIcon: "tools-check-spelling-symbolic"),
.init(id: "magnifyingglass", displayName: "Search", sfSymbol: "magnifyingglass", symbolicIcon: "system-search-symbolic"),
.init(id: "gauge", displayName: "Gauge", sfSymbol: "gauge.with.dots.needle.50percent", symbolicIcon: "speedometer-symbolic"),
.init(id: "antenna", displayName: "Antenna", sfSymbol: "antenna.radiowaves.left.and.right", symbolicIcon: "network-wireless-symbolic"),
.init(id: "shield", displayName: "Shield", sfSymbol: "shield", symbolicIcon: "security-high-symbolic"),
.init(id: "bolt", displayName: "Bolt", sfSymbol: "bolt", symbolicIcon: "weather-storm-symbolic"),
.init(id: "key", displayName: "Key", sfSymbol: "key", symbolicIcon: "dialog-password-symbolic"),
.init(id: "lock", displayName: "Lock", sfSymbol: "lock", symbolicIcon: "system-lock-screen-symbolic"),
.init(id: "network", displayName: "Network", sfSymbol: "network", symbolicIcon: "network-workgroup-symbolic"),
.init(id: "cpu", displayName: "CPU", sfSymbol: "cpu", symbolicIcon: "computer-symbolic"),
.init(id: "memory", displayName: "Memory", sfSymbol: "memorychip", symbolicIcon: "drive-harddisk-symbolic"),
.init(id: "doc-search", displayName: "Doc Search", sfSymbol: "doc.text.magnifyingglass", symbolicIcon: "system-search-symbolic"),
.init(id: "pin", displayName: "Pin", sfSymbol: "pin", symbolicIcon: "starred-symbolic"),
.init(id: "scope", displayName: "Scope", sfSymbol: "scope", symbolicIcon: "view-fullscreen-symbolic"),
.init(id: "cloud", displayName: "Cloud", sfSymbol: "cloud", symbolicIcon: "weather-overcast-symbolic"),
.init(id: "branch", displayName: "Branch", sfSymbol: "arrow.triangle.branch", symbolicIcon: "view-list-symbolic"),
]
public static let userPickable: [InstrumentIconConcept] = Array(all.prefix(16))
public static let `default`: InstrumentIconConcept = all[0]
public static func concept(forID id: String) -> InstrumentIconConcept {
all.first { $0.id == id } ?? `default`
}
}