106 lines
5.6 KiB
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`
|
|
}
|
|
}
|