diff --git a/Sources/XXClient/Models/Contact.swift b/Sources/XXClient/Models/Contact.swift index 03e08f2a517d98571ee0ad9efc497c9ce930460d..6bc9b769841482eadf0f7544869769361254c860 100644 --- a/Sources/XXClient/Models/Contact.swift +++ b/Sources/XXClient/Models/Contact.swift @@ -44,6 +44,18 @@ extension Contact: Equatable { } } +extension Contact { + public func getFact(_ type: FactType) throws -> Fact? { + try getFacts().get(type) + } + + public mutating func setFact(_ type: FactType, _ value: String?) throws { + var facts = try getFacts() + facts.set(type, value) + try setFacts(facts) + } +} + extension Contact { public static func live( _ data: Data, diff --git a/Sources/XXClient/Models/Fact.swift b/Sources/XXClient/Models/Fact.swift index 2ee8a6b3a87720869aafc10e4c746ebcc2565c55..8b2de9f79e5aaa88bb6eb3f3e94ae592c6d50930 100644 --- a/Sources/XXClient/Models/Fact.swift +++ b/Sources/XXClient/Models/Fact.swift @@ -2,21 +2,21 @@ import Foundation public struct Fact: Equatable { public init( - fact: String, - type: Int + type: FactType, + value: String ) { - self.fact = fact self.type = type + self.value = value } - public var fact: String - public var type: Int + public var type: FactType + public var value: String } extension Fact: Codable { enum CodingKeys: String, CodingKey { - case fact = "Fact" case type = "T" + case value = "Fact" } public static func decode(_ data: Data) throws -> Self { @@ -28,6 +28,21 @@ extension Fact: Codable { } } +extension Fact { + @available(iOS, deprecated: 9999.0, message: "This API has been soft-deprecated in favor of `Fact.init(type:value:)`.") + @available(macOS, deprecated: 9999.0, message: "This API has been soft-deprecated in favor of `Fact.init(type:value:)`.") + public init(fact: String, type: Int) { + self.init(type: .init(rawValue: type), value: fact) + } + + @available(iOS, deprecated: 9999.0, message: "This API has been soft-deprecated in favor of `Fact.value`.") + @available(macOS, deprecated: 9999.0, message: "This API has been soft-deprecated in favor of `Fact.value`.") + public var fact: String { + get { value } + set { value = newValue } + } +} + extension Array where Element == Fact { public static func decode(_ data: Data) throws -> Self { if let string = String(data: data, encoding: .utf8), string == "null" { @@ -43,3 +58,17 @@ extension Array where Element == Fact { return try JSONEncoder().encode(self) } } + +extension Array where Element == Fact { + public func get(_ type: FactType) -> Fact? { + first(where: { $0.type == type }) + } + + public mutating func set(_ type: FactType, _ value: String?) { + removeAll(where: { $0.type == type }) + if let value = value { + append(Fact(type: type, value: value)) + sort(by: { $0.type < $1.type }) + } + } +} diff --git a/Sources/XXClient/Models/FactType.swift b/Sources/XXClient/Models/FactType.swift new file mode 100644 index 0000000000000000000000000000000000000000..cbae1a7ebdeaf510440dfcffb631848f3fbe4353 --- /dev/null +++ b/Sources/XXClient/Models/FactType.swift @@ -0,0 +1,49 @@ +import Foundation + +public enum FactType: Equatable { + public static let knownTypes: [FactType] = [.username, .email, .phone] + + case username + case email + case phone + case other(Int) +} + +extension FactType: RawRepresentable { + public init(rawValue: Int) { + if let known = FactType.knownTypes.first(where: { $0.rawValue == rawValue }) { + self = known + } else { + self = .other(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .username: return 0 + case .email: return 1 + case .phone: return 2 + case .other(let rawValue): return rawValue + } + } +} + +extension FactType: ExpressibleByIntegerLiteral { + public init(integerLiteral value: IntegerLiteralType) { + self.init(rawValue: value) + } +} + +extension FactType: Comparable {} + +extension FactType: Codable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + self.init(rawValue: try container.decode(Int.self)) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(rawValue) + } +} diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift index a24a7652de827130a01f145c7df99dcdf7d172dd..5acd76d7c9b05d2c4eee1c01effdbefbcd4be3a7 100644 --- a/Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift +++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift @@ -74,13 +74,13 @@ extension MessengerSearchUsers.Query { var facts: [Fact] { var facts: [Fact] = [] if let username = username, username.isEmpty == false { - facts.append(Fact(fact: username, type: 0)) + facts.set(.username, username) } if let email = email, email.isEmpty == false { - facts.append(Fact(fact: email, type: 1)) + facts.set(.email, email) } if let phone = phone, phone.isEmpty == false { - facts.append(Fact(fact: phone, type: 2)) + facts.set(.phone, phone) } return facts } diff --git a/Tests/XXClientTests/Models/ContactTests.swift b/Tests/XXClientTests/Models/ContactTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..91dc41491650e15caa5b19bbbcd4b03ed26b37c4 --- /dev/null +++ b/Tests/XXClientTests/Models/ContactTests.swift @@ -0,0 +1,61 @@ +import CustomDump +import XCTest +@testable import XXClient + +final class ContactTests: XCTestCase { + func testGetFact() throws { + var contact = Contact.unimplemented("contact-data".data(using: .utf8)!) + contact.getFactsFromContact.run = { _ in + [ + Fact(type: .username, value: "username"), + Fact(type: .email, value: "email"), + Fact(type: .phone, value: "phone"), + Fact(type: .other(3), value: "other"), + ] + } + + XCTAssertNoDifference( + [ + try contact.getFact(.username), + try contact.getFact(.email), + try contact.getFact(.phone), + try contact.getFact(.other(3)), + try contact.getFact(.other(4)), + ], + [ + Fact(type: .username, value: "username"), + Fact(type: .email, value: "email"), + Fact(type: .phone, value: "phone"), + Fact(type: .other(3), value: "other"), + nil + ] + ) + } + + func testSetFact() throws { + var contact = Contact.unimplemented("contact-data".data(using: .utf8)!) + var facts: [Fact] = [ + Fact(type: .username, value: "username"), + Fact(type: .email, value: "email"), + Fact(type: .phone, value: "phone"), + Fact(type: .other(3), value: "other-3"), + ] + contact.getFactsFromContact.run = { _ in facts } + contact.setFactsOnContact.run = { data, newFacts in + facts = newFacts + return data + } + + try contact.setFact(.username, "new-username") + try contact.setFact(.other(4), "new-other-4") + try contact.setFact(.other(3), "new-other-3") + try contact.setFact(.email, nil) + + XCTAssertNoDifference(facts, [ + Fact(type: .username, value: "new-username"), + Fact(type: .phone, value: "phone"), + Fact(type: .other(3), value: "new-other-3"), + Fact(type: .other(4), value: "new-other-4"), + ]) + } +} diff --git a/Tests/XXClientTests/Models/FactTests.swift b/Tests/XXClientTests/Models/FactTests.swift index c844af66f73b6e4686e51436f49d8a28a1c79568..38b6c560f73b049fa7ac33b7f7a4716cc02eee18 100644 --- a/Tests/XXClientTests/Models/FactTests.swift +++ b/Tests/XXClientTests/Models/FactTests.swift @@ -5,7 +5,7 @@ import XCTest final class FactTests: XCTestCase { func testCoding() throws { let factValue = "Zezima" - let factType: Int = 0 + let factType: Int = 123 let jsonString = """ { "Fact": "\(factValue)", @@ -16,8 +16,8 @@ final class FactTests: XCTestCase { let model = try Fact.decode(jsonData) XCTAssertNoDifference(model, Fact( - fact: factValue, - type: factType + type: .other(123), + value: factValue )) let encodedModel = try model.encode() @@ -28,9 +28,9 @@ final class FactTests: XCTestCase { func testCodingArray() throws { let models = [ - Fact(fact: "abcd", type: 0), - Fact(fact: "efgh", type: 1), - Fact(fact: "ijkl", type: 2), + Fact(type: .username, value: "abcd"), + Fact(type: .email, value: "efgh"), + Fact(type: .phone, value: "ijkl"), ] let encodedModels = try models.encode() @@ -51,4 +51,49 @@ final class FactTests: XCTestCase { XCTAssertNoDifference(encodedModels, jsonData) } + + func testArrayGetter() { + let facts = [ + Fact(type: .username, value: "username"), + Fact(type: .email, value: "email"), + Fact(type: .phone, value: "phone"), + Fact(type: .other(3), value: "other"), + ] + + XCTAssertNoDifference( + [ + facts.get(.username), + facts.get(.email), + facts.get(.phone), + facts.get(.other(3)), + facts.get(.other(4)), + ], + [ + Fact(type: .username, value: "username"), + Fact(type: .email, value: "email"), + Fact(type: .phone, value: "phone"), + Fact(type: .other(3), value: "other"), + nil + ] + ) + } + + func testArraySetter() { + var facts: [Fact] = [] + + facts.set(.email, "email") + facts.set(.phone, "phone") + facts.set(.other(3), "other") + facts.set(.username, "username") + + XCTAssertNoDifference( + facts, + [ + Fact(type: .username, value: "username"), + Fact(type: .email, value: "email"), + Fact(type: .phone, value: "phone"), + Fact(type: .other(3), value: "other"), + ] + ) + } } diff --git a/Tests/XXClientTests/Models/FactTypeTests.swift b/Tests/XXClientTests/Models/FactTypeTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..f8c3347be43605cff8d2b240ae9d3bcbcd4631ed --- /dev/null +++ b/Tests/XXClientTests/Models/FactTypeTests.swift @@ -0,0 +1,44 @@ +import CustomDump +import Foundation +import XCTest +@testable import XXClient + +final class FactTypeTests: XCTestCase { + func testDecoding() throws { + let decoder = Foundation.JSONDecoder() + + XCTAssertNoDifference( + [ + try decoder.decode(FactType.self, from: "0".data(using: .utf8)!), + try decoder.decode(FactType.self, from: "1".data(using: .utf8)!), + try decoder.decode(FactType.self, from: "2".data(using: .utf8)!), + try decoder.decode(FactType.self, from: "3".data(using: .utf8)!), + ], + [ + FactType.username, + FactType.email, + FactType.phone, + FactType.other(3), + ] + ) + } + + func testEncoding() throws { + let encoder = Foundation.JSONEncoder() + + XCTAssertNoDifference( + [ + try encoder.encode(FactType.username), + try encoder.encode(FactType.email), + try encoder.encode(FactType.phone), + try encoder.encode(FactType.other(3)), + ], + [ + "0".data(using: .utf8)!, + "1".data(using: .utf8)!, + "2".data(using: .utf8)!, + "3".data(using: .utf8)!, + ] + ) + } +} diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift index b6eaac39a1d79cfbf665e7b9a8dc90fab2d161af..9ffa1c82bdc0fec3fc7b089c2ef25a60f634d86d 100644 --- a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift +++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift @@ -181,9 +181,9 @@ final class MessengerSearchUsersTests: XCTestCase { phone: "phone" ).facts, [ - Fact(fact: "username", type: 0), - Fact(fact: "email", type: 1), - Fact(fact: "phone", type: 2), + Fact(type: .username, value: "username"), + Fact(type: .email, value: "email"), + Fact(type: .phone, value: "phone"), ] ) @@ -194,7 +194,7 @@ final class MessengerSearchUsersTests: XCTestCase { phone: nil ).facts, [ - Fact(fact: "username", type: 0), + Fact(type: .username, value: "username"), ] ) }