import Foundation struct Cap { /// The unique number of the cap let id: Int /// The name of the cap var name: String /// The name of the cap without special characters var cleanName: String /// The number of images existing for the cap var imageCount: Int /// The index of the main image for the cap var mainImage: Int /// The version of the first classifier capable of recognizing the cap var classifierVersion: Int? var color: Color? /// The subpath to the main image on the server var mainImagePath: String { String(format: "images/%04d/%04d-%02d.jpg", id, id, mainImage) } var image: CapImage { .init(cap: id, version: mainImage) } /** Create a new cap. - Parameter id: The unique id of the cap - Parameter name: The name associated with the cap */ init(id: Int, name: String, classifier: Int? = nil) { self.id = id self.name = name self.cleanName = name.clean self.imageCount = 1 self.mainImage = 0 self.classifierVersion = classifier } init(data: CapData) { self.id = data.id self.name = data.name self.cleanName = data.name.clean self.imageCount = data.count self.mainImage = data.mainImage self.classifierVersion = data.classifierVersion } var data: CapData { .init(id: id, name: name, count: imageCount, mainImage: mainImage, classifierVersion: classifierVersion, color: color) } mutating func update(with data: CapData) { self.name = data.name self.cleanName = data.name.clean self.imageCount = data.count self.mainImage = data.mainImage self.classifierVersion = data.classifierVersion } static func ==(lhs: Cap, rhs: CapData) -> Bool { lhs.id == rhs.id && lhs.name == rhs.name && lhs.imageCount == rhs.count && lhs.mainImage == rhs.mainImage && lhs.classifierVersion == rhs.classifierVersion } static func !=(lhs: Cap, rhs: CapData) -> Bool { !(lhs == rhs) } func classifiable(by classifierVersion: Int?) -> Bool { guard let version = classifierVersion else { return false } guard let own = self.classifierVersion else { return false } return version >= own } } extension Cap { struct Color: Codable, Equatable { let r: Int let g: Int let b: Int } } // MARK: Protocol Identifiable extension Cap: Identifiable { } // MARK: Protocol Comparable extension Cap: Codable { enum CodingKeys: String, CodingKey { case id = "u" case name = "n" case cleanName = "c" case imageCount = "i" case mainImage = "m" case classifierVersion = "v" case color = "f" } } // MARK: Protocol Comparable extension Cap: Comparable { static func < (lhs: Cap, rhs: Cap) -> Bool { lhs.id < rhs.id } } // MARK: Protocol Equatable extension Cap: Equatable { static func == (lhs: Cap, rhs: Cap) -> Bool { return lhs.id == rhs.id } } // MARK: Protocol Hashable extension Cap: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(id) } } // MARK: String extension private extension String { var clean: String { return lowercased().replacingOccurrences(of: "[^a-z0-9 ]", with: "", options: .regularExpression) } } // MARK: Int extension private extension Int { var isEven: Bool { return self % 2 == 0 } }