import Foundation public struct DeviceInfo: Sendable, Equatable { public let allspace: UInt64 public let freespace: UInt64 public let name: String public let size: String public let brand: String public let powerlevel: Int } extension DeviceInfo: Codable { enum CodingKeys: String, CodingKey { case allspace, freespace, name, size, brand, powerlevel } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // All string fields: fallback to "" if missing name = (try? container.decode(String.self, forKey: .name)) ?? "" size = (try? container.decode(String.self, forKey: .size)) ?? "" brand = (try? container.decode(String.self, forKey: .brand)) ?? "" // powerlevel: Int, may come as Int or String if let num = try? container.decode(Int.self, forKey: .powerlevel) { powerlevel = num } else if let str = try? container.decode(String.self, forKey: .powerlevel), let num = Int(str) { powerlevel = num } else { powerlevel = 0 } // allspace/freespace: may come as number, string, or be missing allspace = Self.decodeFlexibleUInt64(container: container, key: .allspace) freespace = Self.decodeFlexibleUInt64(container: container, key: .freespace) } private static func decodeFlexibleUInt64( container: KeyedDecodingContainer, key: CodingKeys ) -> UInt64 { if let value = try? container.decode(UInt64.self, forKey: key) { return value } if let value = try? container.decode(Int.self, forKey: key) { return UInt64(max(0, value)) } if let str = try? container.decode(String.self, forKey: key), let value = UInt64(str) { return value } return 0 } }