142 lines
4.9 KiB
Swift
142 lines
4.9 KiB
Swift
//
|
|
// Copyright © 2022 osy. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Basic information about the VM only used in listing and presenting.
|
|
struct UTMConfigurationInfo: Codable {
|
|
/// VM name displayed to user.
|
|
var name: String = NSLocalizedString("Virtual Machine", comment: "UTMConfigurationInfo")
|
|
|
|
/// Path to the icon.
|
|
var iconURL: URL?
|
|
|
|
/// If true, the icon is stored in the bundle. Otherwise, the icon is built-in.
|
|
var isIconCustom: Bool = false
|
|
|
|
/// User specified notes to be displayed when the VM is selected.
|
|
var notes: String?
|
|
|
|
/// Random identifier not accessible by the user.
|
|
var uuid: UUID = UUID()
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case name = "Name"
|
|
case icon = "Icon"
|
|
case isIconCustom = "IconCustom"
|
|
case notes = "Notes"
|
|
case uuid = "UUID"
|
|
}
|
|
|
|
init() {
|
|
}
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let values = try decoder.container(keyedBy: CodingKeys.self)
|
|
name = try values.decode(String.self, forKey: .name)
|
|
isIconCustom = try values.decode(Bool.self, forKey: .isIconCustom)
|
|
if isIconCustom {
|
|
guard let dataURL = decoder.userInfo[.dataURL] as? URL else {
|
|
throw UTMConfigurationError.invalidDataURL
|
|
}
|
|
let iconName = try values.decode(String.self, forKey: .icon)
|
|
iconURL = dataURL.appendingPathComponent(iconName)
|
|
} else if let iconName = try values.decodeIfPresent(String.self, forKey: .icon) {
|
|
iconURL = Self.builtinIcon(named: iconName)
|
|
}
|
|
notes = try values.decodeIfPresent(String.self, forKey: .notes)
|
|
uuid = try values.decode(UUID.self, forKey: .uuid)
|
|
}
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(name, forKey: .name)
|
|
if isIconCustom, let iconURL = iconURL {
|
|
try container.encode(true, forKey: .isIconCustom)
|
|
try container.encode(iconURL.lastPathComponent, forKey: .icon)
|
|
} else if !isIconCustom, let name = iconURL?.deletingPathExtension().lastPathComponent {
|
|
try container.encode(false, forKey: .isIconCustom)
|
|
try container.encode(name, forKey: .icon)
|
|
} else {
|
|
try container.encode(false, forKey: .isIconCustom)
|
|
}
|
|
try container.encodeIfPresent(notes, forKey: .notes)
|
|
try container.encode(uuid, forKey: .uuid)
|
|
}
|
|
|
|
static func builtinIcon(named name: String) -> URL? {
|
|
Bundle.main.url(forResource: name, withExtension: "png", subdirectory: "Icons")
|
|
}
|
|
}
|
|
|
|
// MARK: - Conversion of old config format
|
|
|
|
extension UTMConfigurationInfo {
|
|
init(migrating oldConfig: UTMLegacyQemuConfiguration) {
|
|
self.init()
|
|
name = oldConfig.name
|
|
notes = oldConfig.notes
|
|
if let uuidString = oldConfig.systemUUID, let uuid = UUID(uuidString: uuidString) {
|
|
self.uuid = uuid
|
|
}
|
|
isIconCustom = oldConfig.iconCustom
|
|
if isIconCustom {
|
|
if let name = oldConfig.icon, let dataURL = oldConfig.existingPath {
|
|
iconURL = dataURL.appendingPathComponent(name)
|
|
} else {
|
|
isIconCustom = false
|
|
}
|
|
}
|
|
if !isIconCustom, let name = oldConfig.icon {
|
|
iconURL = Self.builtinIcon(named: name)
|
|
}
|
|
}
|
|
|
|
#if os(macOS)
|
|
init(migrating oldConfig: UTMLegacyAppleConfiguration, dataURL: URL) {
|
|
self.init()
|
|
name = oldConfig.name
|
|
notes = oldConfig.notes
|
|
uuid = UUID()
|
|
isIconCustom = oldConfig.iconCustom
|
|
if isIconCustom {
|
|
if let name = oldConfig.icon {
|
|
iconURL = dataURL.appendingPathComponent(name)
|
|
} else {
|
|
isIconCustom = false
|
|
}
|
|
}
|
|
if let name = oldConfig.icon {
|
|
iconURL = Self.builtinIcon(named: name)
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// MARK: - Saving data
|
|
|
|
extension UTMConfigurationInfo {
|
|
@MainActor mutating func saveData(to dataURL: URL) async throws -> [URL] {
|
|
// save new icon
|
|
if isIconCustom, let iconURL = iconURL {
|
|
let newIconURL = try await UTMQemuConfiguration.copyItemIfChanged(from: iconURL, to: dataURL)
|
|
self.iconURL = newIconURL
|
|
return [newIconURL]
|
|
}
|
|
return []
|
|
}
|
|
}
|