Files
JSONAPI/Sources/JSONAPI/Resource/Poly.swift
T
2018-12-06 18:38:20 -08:00

802 lines
16 KiB
Swift

//
// Poly.swift
// JSONAPI
//
// Created by Mathew Polzin on 11/22/18.
//
/// Poly is a protocol to which types that
/// are polymorphic belong to. Specifically,
/// Poly1, Poly2, Poly3, etc. types conform
/// to the Poly protocol. These types allow
/// typesafe grouping of a number of
/// disparate types under one roof for
/// the purposes of JSON API compliant
/// encoding or decoding.
public protocol Poly: PrimaryResource {}
// MARK: - Generic Decoding
private func decode<Entity: JSONAPI.EntityType>(_ type: Entity.Type, from container: SingleValueDecodingContainer) throws -> Result<Entity, DecodingError> {
let ret: Result<Entity, DecodingError>
do {
ret = try .success(container.decode(Entity.self))
} catch (let err as DecodingError) {
ret = .failure(err)
} catch (let err) {
ret = .failure(DecodingError.typeMismatch(Entity.Description.self,
.init(codingPath: container.codingPath,
debugDescription: String(describing: err),
underlyingError: err)))
}
return ret
}
// MARK: - 0 types
public protocol _Poly0: Poly { }
public struct Poly0: _Poly0 {
public init() {}
public init(from decoder: Decoder) throws {
throw JSONAPIEncodingError.illegalDecoding("Attempted to decode Poly0, which should represent a thing that is not expected to be found in a document.")
}
public func encode(to encoder: Encoder) throws {
throw JSONAPIEncodingError.illegalEncoding("Attempted to encode Poly0, which should represent a thing that is not expected to be found in a document.")
}
}
// MARK: - 1 type
public protocol _Poly1: _Poly0 {
associatedtype A: EntityType
var a: A? { get }
init(_ a: A)
}
public extension _Poly1 {
subscript(_ lookup: A.Type) -> A? {
return a
}
}
public enum Poly1<A: EntityType>: _Poly1 {
case a(A)
public var a: A? {
guard case let .a(ret) = self else { return nil }
return ret
}
public init(_ a: A) {
self = .a(a)
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self = .a(try container.decode(A.self))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .a(let a):
try container.encode(a)
}
}
}
extension Poly1: CustomStringConvertible {
public var description: String {
let str: String
switch self {
case .a(let a):
str = String(describing: a)
}
return "Poly(\(str))"
}
}
// MARK: - 2 types
public protocol _Poly2: _Poly1 {
associatedtype B: EntityType
var b: B? { get }
init(_ b: B)
}
public extension _Poly2 {
subscript(_ lookup: B.Type) -> B? {
return b
}
}
public enum Poly2<A: EntityType, B: EntityType>: _Poly2 {
case a(A)
case b(B)
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 init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let attempts = [
try decode(A.self, from: container).map { Poly2.a($0) },
try decode(B.self, from: container).map { Poly2.b($0) } ]
let maybeVal: Poly2<A, B>? = attempts
.compactMap { $0.value }
.first
guard let val = maybeVal else {
throw EncodingError.invalidValue(Poly2<A, B>.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)
}
}
}
extension Poly2: 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)
}
return "Poly(\(str))"
}
}
// MARK: - 3 types
public protocol _Poly3: _Poly2 {
associatedtype C: EntityType
var c: C? { get }
init(_ c: C)
}
public extension _Poly3 {
subscript(_ lookup: C.Type) -> C? {
return c
}
}
public enum Poly3<A: EntityType, B: EntityType, C: EntityType>: _Poly3 {
case a(A)
case b(B)
case c(C)
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 init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let attempts = [
try decode(A.self, from: container).map { Poly3.a($0) },
try decode(B.self, from: container).map { Poly3.b($0) },
try decode(C.self, from: container).map { Poly3.c($0) }]
let maybeVal: Poly3<A, B, C>? = attempts
.compactMap { $0.value }
.first
guard let val = maybeVal else {
throw EncodingError.invalidValue(Poly3<A, B, C>.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)
}
}
}
extension Poly3: 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)
}
return "Poly(\(str))"
}
}
// MARK: - 4 types
public protocol _Poly4: _Poly3 {
associatedtype D: EntityType
var d: D? { get }
init(_ d: D)
}
public extension _Poly4 {
subscript(_ lookup: D.Type) -> D? {
return d
}
}
public enum Poly4<A: EntityType, B: EntityType, C: EntityType, D: EntityType>: _Poly4 {
case a(A)
case b(B)
case c(C)
case d(D)
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 init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let attempts = [
try decode(A.self, from: container).map { Poly4.a($0) },
try decode(B.self, from: container).map { Poly4.b($0) },
try decode(C.self, from: container).map { Poly4.c($0) },
try decode(D.self, from: container).map { Poly4.d($0) }]
let maybeVal: Poly4<A, B, C, D>? = attempts
.compactMap { $0.value }
.first
guard let val = maybeVal else {
throw EncodingError.invalidValue(Poly4<A, B, C, D>.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)
}
}
}
extension Poly4: 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)
}
return "Poly(\(str))"
}
}
// MARK: - 5 types
public protocol _Poly5: _Poly4 {
associatedtype E: EntityType
var e: E? { get }
init(_ e: E)
}
public extension _Poly5 {
subscript(_ lookup: E.Type) -> E? {
return e
}
}
public enum Poly5<A: EntityType, B: EntityType, C: EntityType, D: EntityType, E: EntityType>: _Poly5 {
case a(A)
case b(B)
case c(C)
case d(D)
case e(E)
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 init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let attempts = [
try decode(A.self, from: container).map { Poly5.a($0) },
try decode(B.self, from: container).map { Poly5.b($0) },
try decode(C.self, from: container).map { Poly5.c($0) },
try decode(D.self, from: container).map { Poly5.d($0) },
try decode(E.self, from: container).map { Poly5.e($0) }]
let maybeVal: Poly5<A, B, C, D, E>? = attempts
.compactMap { $0.value }
.first
guard let val = maybeVal else {
throw EncodingError.invalidValue(Poly5<A, B, C, D, E>.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)
}
}
}
extension Poly5: 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)
}
return "Poly(\(str))"
}
}
// MARK: - 6 types
public protocol _Poly6: _Poly5 {
associatedtype F: EntityType
var f: F? { get }
init(_ f: F)
}
public extension _Poly6 {
subscript(_ lookup: F.Type) -> F? {
return f
}
}
public enum Poly6<A: EntityType, B: EntityType, C: EntityType, D: EntityType, E: EntityType, F: EntityType>: _Poly6 {
case a(A)
case b(B)
case c(C)
case d(D)
case e(E)
case f(F)
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 init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let attempts = [
try decode(A.self, from: container).map { Poly6.a($0) },
try decode(B.self, from: container).map { Poly6.b($0) },
try decode(C.self, from: container).map { Poly6.c($0) },
try decode(D.self, from: container).map { Poly6.d($0) },
try decode(E.self, from: container).map { Poly6.e($0) },
try decode(F.self, from: container).map { Poly6.f($0) }]
let maybeVal: Poly6<A, B, C, D, E, F>? = attempts
.compactMap { $0.value }
.first
guard let val = maybeVal else {
throw EncodingError.invalidValue(Poly6<A, B, C, D, E, F>.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)
}
}
}
extension Poly6: 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)
}
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<A: EntityType, B: EntityType, C: EntityType, D: EntityType, E: EntityType, F: EntityType, G: EntityType>: _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<A, B, C, D, E, F, G>? = attempts
.compactMap { $0.value }
.first
guard let val = maybeVal else {
throw EncodingError.invalidValue(Poly7<A, B, C, D, E, F, G>.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))"
}
}