diff --git a/Sources/JSONAPI/Document/Includes.swift b/Sources/JSONAPI/Document/Includes.swift index 4d856fb..b02a3b2 100644 --- a/Sources/JSONAPI/Document/Includes.swift +++ b/Sources/JSONAPI/Document/Includes.swift @@ -115,4 +115,11 @@ extension Includes where I: _Poly6 { } // MARK: - 7 includes +public typealias Include7 = Poly7 +extension Includes where I: _Poly7 { + public subscript(_ lookup: I.G.Type) -> [I.G] { + return values.compactMap { $0.g } + } +} + // MARK: - 8 includes diff --git a/Sources/JSONAPI/Resource/Poly.swift b/Sources/JSONAPI/Resource/Poly.swift index de506a4..6abe545 100644 --- a/Sources/JSONAPI/Resource/Poly.swift +++ b/Sources/JSONAPI/Resource/Poly.swift @@ -644,3 +644,158 @@ extension Poly6: CustomStringConvertible { return "Poly(\(str))" } } + +// MARK: - 7 types +public protocol _Poly7: _Poly6 { + associatedtype G: EntityType + var g: G? { get } + + init(_ g: G) +} + +public extension _Poly7 { + subscript(_ lookup: G.Type) -> G? { + return g + } +} + +public enum Poly7: _Poly7 { + case a(A) + case b(B) + case c(C) + case d(D) + case e(E) + case f(F) + case g(G) + + public var a: A? { + guard case let .a(ret) = self else { return nil } + return ret + } + + public init(_ a: A) { + self = .a(a) + } + + public var b: B? { + guard case let .b(ret) = self else { return nil } + return ret + } + + public init(_ b: B) { + self = .b(b) + } + + public var c: C? { + guard case let .c(ret) = self else { return nil } + return ret + } + + public init(_ c: C) { + self = .c(c) + } + + public var d: D? { + guard case let .d(ret) = self else { return nil } + return ret + } + + public init(_ d: D) { + self = .d(d) + } + + public var e: E? { + guard case let .e(ret) = self else { return nil } + return ret + } + + public init(_ e: E) { + self = .e(e) + } + + public var f: F? { + guard case let .f(ret) = self else { return nil } + return ret + } + + public init(_ f: F) { + self = .f(f) + } + + public var g: G? { + guard case let .g(ret) = self else { return nil } + return ret + } + + public init(_ g: G) { + self = .g(g) + } + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + + let attempts = [ + try decode(A.self, from: container).map { Poly7.a($0) }, + try decode(B.self, from: container).map { Poly7.b($0) }, + try decode(C.self, from: container).map { Poly7.c($0) }, + try decode(D.self, from: container).map { Poly7.d($0) }, + try decode(E.self, from: container).map { Poly7.e($0) }, + try decode(F.self, from: container).map { Poly7.f($0) }, + try decode(G.self, from: container).map { Poly7.g($0) }] + + let maybeVal: Poly7? = attempts + .compactMap { $0.value } + .first + + guard let val = maybeVal else { + throw EncodingError.invalidValue(Poly7.self, .init(codingPath: decoder.codingPath, debugDescription: "Failed to find an include of the expected type. Attempts: \(attempts.map { $0.error }.compactMap { $0 })")) + } + + self = val + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + switch self { + case .a(let a): + try container.encode(a) + case .b(let b): + try container.encode(b) + case .c(let c): + try container.encode(c) + case .d(let d): + try container.encode(d) + case .e(let e): + try container.encode(e) + case .f(let f): + try container.encode(f) + case .g(let g): + try container.encode(g) + } + } +} + +extension Poly7: CustomStringConvertible { + public var description: String { + let str: String + switch self { + case .a(let a): + str = String(describing: a) + case .b(let b): + str = String(describing: b) + case .c(let c): + str = String(describing: c) + case .d(let d): + str = String(describing: d) + case .e(let e): + str = String(describing: e) + case .f(let f): + str = String(describing: f) + case .g(let g): + str = String(describing: g) + } + + return "Poly(\(str))" + } +} diff --git a/Tests/JSONAPITests/Includes/IncludeTests.swift b/Tests/JSONAPITests/Includes/IncludeTests.swift index 92fd09b..9028f14 100644 --- a/Tests/JSONAPITests/Includes/IncludeTests.swift +++ b/Tests/JSONAPITests/Includes/IncludeTests.swift @@ -121,6 +121,24 @@ class IncludedTests: XCTestCase { test_DecodeEncodeEquality(type: Includes>.self, data: six_different_type_includes) } + + func test_SevenDifferentIncludes() { + let includes = decoded(type: Includes>.self, + data: seven_different_type_includes) + + XCTAssertEqual(includes[TestEntity.self].count, 1) + XCTAssertEqual(includes[TestEntity2.self].count, 1) + XCTAssertEqual(includes[TestEntity3.self].count, 1) + XCTAssertEqual(includes[TestEntity4.self].count, 1) + XCTAssertEqual(includes[TestEntity5.self].count, 1) + XCTAssertEqual(includes[TestEntity6.self].count, 1) + XCTAssertEqual(includes[TestEntity7.self].count, 1) + } + + func test_SevenDifferentIncludes_encode() { + test_DecodeEncodeEquality(type: Includes>.self, + data: seven_different_type_includes) + } } // MARK: - Test types @@ -203,4 +221,15 @@ extension IncludedTests { } typealias TestEntity6 = BasicEntity + + enum TestEntityType7: EntityDescription { + + typealias Attributes = NoAttributes + + public static var type: String { return "test_entity7" } + + typealias Relationships = NoRelationships + } + + typealias TestEntity7 = BasicEntity } diff --git a/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift b/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift index 11014f0..4fa835c 100644 --- a/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift +++ b/Tests/JSONAPITests/Includes/stubs/IncludeStubs.swift @@ -281,3 +281,76 @@ let six_different_type_includes = """ } ] """.data(using: .utf8)! + +let seven_different_type_includes = """ +[ + { + "type": "test_entity1", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF", + "attributes": { + "foo": "Hello", + "bar": 123 + } + }, + { + "type": "test_entity2", + "id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333", + "attributes": { + "foo": "World", + "bar": 456 + }, + "relationships": { + "entity1": { + "data": { + "type": "test_entity1", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF" + } + } + } + }, + { + "type": "test_entity3", + "id": "11223B69-4DF1-467F-B52E-B0C9E44FC443", + "relationships": { + "entity1": { + "data": { + "type": "test_entity1", + "id": "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF" + } + }, + "entity2": { + "data": [ + { + "type": "test_entity2", + "id": "90F03B69-4DF1-467F-B52E-B0C9E44FC333" + } + ] + } + } + }, + { + "type": "test_entity6", + "id": "11113B69-4DF1-467F-B52E-B0C9E44FC444", + "relationships": { + "entity4": { + "data": { + "type": "test_entity4", + "id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E" + } + } + } + }, + { + "type": "test_entity5", + "id": "A24B3B69-4DF1-467F-B52E-B0C9E44F436A" + }, + { + "type": "test_entity4", + "id": "364B3B69-4DF1-467F-B52E-B0C9E44F666E" + }, + { + "type": "test_entity7", + "id": "364B3B69-4DF1-222F-B52E-B0C9E44F666E" + } +] +""".data(using: .utf8)! diff --git a/Tests/JSONAPITests/Poly/PolyTests.swift b/Tests/JSONAPITests/Poly/PolyTests.swift index 5aaa1d2..5f508d1 100644 --- a/Tests/JSONAPITests/Poly/PolyTests.swift +++ b/Tests/JSONAPITests/Poly/PolyTests.swift @@ -164,6 +164,72 @@ class PolyTests: XCTestCase { XCTAssertNil(poly6.d) XCTAssertNil(poly6.e) } + + func test_init_Poly7() { + let entity = TestEntity5() + let poly = Poly7(entity) + XCTAssertEqual(poly.a, entity) + XCTAssertNil(poly.b) + XCTAssertNil(poly.c) + XCTAssertNil(poly.d) + XCTAssertNil(poly.e) + XCTAssertNil(poly.f) + XCTAssertNil(poly.g) + + let poly2 = Poly7(entity) + XCTAssertEqual(poly2.b, entity) + XCTAssertNil(poly2.a) + XCTAssertNil(poly2.c) + XCTAssertNil(poly2.d) + XCTAssertNil(poly2.e) + XCTAssertNil(poly2.f) + XCTAssertNil(poly2.g) + + let poly3 = Poly7(entity) + XCTAssertEqual(poly3.c, entity) + XCTAssertNil(poly3.a) + XCTAssertNil(poly3.b) + XCTAssertNil(poly3.d) + XCTAssertNil(poly3.e) + XCTAssertNil(poly3.f) + XCTAssertNil(poly3.g) + + let poly4 = Poly7(entity) + XCTAssertEqual(poly4.d, entity) + XCTAssertNil(poly4.a) + XCTAssertNil(poly4.b) + XCTAssertNil(poly4.c) + XCTAssertNil(poly4.e) + XCTAssertNil(poly4.f) + XCTAssertNil(poly4.g) + + let poly5 = Poly7(entity) + XCTAssertEqual(poly5.e, entity) + XCTAssertNil(poly5.a) + XCTAssertNil(poly5.b) + XCTAssertNil(poly5.c) + XCTAssertNil(poly5.d) + XCTAssertNil(poly5.f) + XCTAssertNil(poly5.g) + + let poly6 = Poly7(entity) + XCTAssertEqual(poly6.f, entity) + XCTAssertNil(poly6.a) + XCTAssertNil(poly6.b) + XCTAssertNil(poly6.c) + XCTAssertNil(poly6.d) + XCTAssertNil(poly6.e) + XCTAssertNil(poly6.g) + + let poly7 = Poly7(entity) + XCTAssertEqual(poly7.g, entity) + XCTAssertNil(poly7.a) + XCTAssertNil(poly7.b) + XCTAssertNil(poly7.c) + XCTAssertNil(poly7.d) + XCTAssertNil(poly7.e) + XCTAssertNil(poly7.f) + } } // MARK: - subscript lookup @@ -224,6 +290,19 @@ extension PolyTests { XCTAssertNil(poly[TestEntity5.self]) XCTAssertEqual(entity, poly[TestEntity6.self]) } + + func test_Poly7_lookup() { + let entity = decoded(type: TestEntity7.self, data: poly_entity7) + let poly = decoded(type: Poly7.self, data: poly_entity7) + + XCTAssertNil(poly[TestEntity.self]) + XCTAssertNil(poly[TestEntity2.self]) + XCTAssertNil(poly[TestEntity3.self]) + XCTAssertNil(poly[TestEntity4.self]) + XCTAssertNil(poly[TestEntity5.self]) + XCTAssertNil(poly[TestEntity6.self]) + XCTAssertEqual(entity, poly[TestEntity7.self]) + } } // MARK: - failures @@ -259,6 +338,10 @@ extension PolyTests { func test_Poly6_decode_throws_typeNotFound() { XCTAssertThrowsError(try JSONDecoder().decode(Poly6.self, from: poly_entity7)) } + + func test_Poly7_decode_throws_typeNotFound() { + XCTAssertThrowsError(try JSONDecoder().decode(Poly7.self, from: poly_entity8)) + } } // MARK: - Test types @@ -341,4 +424,15 @@ extension PolyTests { } typealias TestEntity6 = BasicEntity + + enum TestEntityType7: EntityDescription { + + typealias Attributes = NoAttributes + + public static var type: String { return "test_entity7" } + + typealias Relationships = NoRelationships + } + + typealias TestEntity7 = BasicEntity } diff --git a/Tests/JSONAPITests/Poly/stubs/PolyStubs.swift b/Tests/JSONAPITests/Poly/stubs/PolyStubs.swift index ae34f2a..b45c029 100644 --- a/Tests/JSONAPITests/Poly/stubs/PolyStubs.swift +++ b/Tests/JSONAPITests/Poly/stubs/PolyStubs.swift @@ -93,3 +93,10 @@ let poly_entity7 = """ "id": "A24B3444-4DF1-467F-B52E-B0C9E44F436A" } """.data(using: .utf8)! + +let poly_entity8 = """ +{ + "type": "test_entity8", + "id": "A24B3444-4DF1-467F-B52E-B0C9E44F436A" +} +""".data(using: .utf8)!