Skip to content

Commit

Permalink
Merge pull request #51 from mattpolzin/feature/primary-resource-body-…
Browse files Browse the repository at this point in the history
…improvements

Feature/primary resource body improvements
  • Loading branch information
mattpolzin authored Nov 7, 2019
2 parents ae7e0f5 + 0538de4 commit 19636a4
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 179 deletions.
32 changes: 18 additions & 14 deletions Sources/JSONAPI/Document/ResourceBody.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ extension Optional: OptionalCodablePrimaryResource where Wrapped: CodablePrimary
/// An `EncodableResourceBody` is a `ResourceBody` that only supports being
/// encoded. It is actually weaker than `ResourceBody`, which supports both encoding
/// and decoding.
public protocol EncodableResourceBody: Equatable, Encodable {}
public protocol EncodableResourceBody: Equatable, Encodable {
associatedtype PrimaryResource
}

/// A `CodableResourceBody` is a representation of the body of the JSON:API Document.
/// It can either be one resource (which can be specified as optional or not)
Expand All @@ -49,19 +51,19 @@ public func +<R: ResourceBodyAppendable>(_ left: R, right: R) -> R {
/// A type allowing for a document body containing 1 primary resource.
/// If the `Entity` specialization is an `Optional` type, the body can contain
/// 0 or 1 primary resources.
public struct SingleResourceBody<Entity: JSONAPI.OptionalEncodablePrimaryResource>: EncodableResourceBody {
public let value: Entity
public struct SingleResourceBody<PrimaryResource: JSONAPI.OptionalEncodablePrimaryResource>: EncodableResourceBody {
public let value: PrimaryResource

public init(resourceObject: Entity) {
public init(resourceObject: PrimaryResource) {
self.value = resourceObject
}
}

/// A type allowing for a document body containing 0 or more primary resources.
public struct ManyResourceBody<Entity: JSONAPI.EncodablePrimaryResource>: EncodableResourceBody, ResourceBodyAppendable {
public let values: [Entity]
public struct ManyResourceBody<PrimaryResource: JSONAPI.EncodablePrimaryResource>: EncodableResourceBody, ResourceBodyAppendable {
public let values: [PrimaryResource]

public init(resourceObjects: [Entity]) {
public init(resourceObjects: [PrimaryResource]) {
values = resourceObjects
}

Expand All @@ -73,6 +75,8 @@ public struct ManyResourceBody<Entity: JSONAPI.EncodablePrimaryResource>: Encoda
/// Use NoResourceBody to indicate you expect a JSON API document to not
/// contain a "data" top-level key.
public struct NoResourceBody: CodableResourceBody {
public typealias PrimaryResource = Void

public static var none: NoResourceBody { return NoResourceBody() }
}

Expand All @@ -82,7 +86,7 @@ extension SingleResourceBody {
var container = encoder.singleValueContainer()

let anyNil: Any? = nil
let nilValue = anyNil as? Entity
let nilValue = anyNil as? PrimaryResource
guard value != nilValue else {
try container.encodeNil()
return
Expand All @@ -92,18 +96,18 @@ extension SingleResourceBody {
}
}

extension SingleResourceBody: Decodable, CodableResourceBody where Entity: OptionalCodablePrimaryResource {
extension SingleResourceBody: Decodable, CodableResourceBody where PrimaryResource: OptionalCodablePrimaryResource {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()

let anyNil: Any? = nil
if container.decodeNil(),
let val = anyNil as? Entity {
let val = anyNil as? PrimaryResource {
value = val
return
}

value = try container.decode(Entity.self)
value = try container.decode(PrimaryResource.self)
}
}

Expand All @@ -117,12 +121,12 @@ extension ManyResourceBody {
}
}

extension ManyResourceBody: Decodable, CodableResourceBody where Entity: CodablePrimaryResource {
extension ManyResourceBody: Decodable, CodableResourceBody where PrimaryResource: CodablePrimaryResource {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var valueAggregator = [Entity]()
var valueAggregator = [PrimaryResource]()
while !container.isAtEnd {
valueAggregator.append(try container.decode(Entity.self))
valueAggregator.append(try container.decode(PrimaryResource.self))
}
values = valueAggregator
}
Expand Down
44 changes: 22 additions & 22 deletions Sources/JSONAPI/Resource/Poly+PrimaryResource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import Poly
public typealias EncodableJSONPoly = Poly & EncodablePrimaryResource

public typealias EncodablePolyWrapped = Encodable & Equatable
public typealias PolyWrapped = EncodablePolyWrapped & Decodable
public typealias CodablePolyWrapped = EncodablePolyWrapped & Decodable

extension Poly0: CodablePrimaryResource {
public init(from decoder: Decoder) throws {
Expand All @@ -35,42 +35,42 @@ extension Poly1: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped {}

extension Poly1: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped {}
where A: CodablePolyWrapped {}

// MARK: - 2 types
extension Poly2: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped, B: EncodablePolyWrapped {}

extension Poly2: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped {}
where A: CodablePolyWrapped, B: CodablePolyWrapped {}

// MARK: - 3 types
extension Poly3: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped, B: EncodablePolyWrapped, C: EncodablePolyWrapped {}

extension Poly3: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped {}
where A: CodablePolyWrapped, B: CodablePolyWrapped, C: CodablePolyWrapped {}

// MARK: - 4 types
extension Poly4: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped, B: EncodablePolyWrapped, C: EncodablePolyWrapped, D: EncodablePolyWrapped {}

extension Poly4: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped {}
where A: CodablePolyWrapped, B: CodablePolyWrapped, C: CodablePolyWrapped, D: CodablePolyWrapped {}

// MARK: - 5 types
extension Poly5: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped, B: EncodablePolyWrapped, C: EncodablePolyWrapped, D: EncodablePolyWrapped, E: EncodablePolyWrapped {}

extension Poly5: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped {}
where A: CodablePolyWrapped, B: CodablePolyWrapped, C: CodablePolyWrapped, D: CodablePolyWrapped, E: CodablePolyWrapped {}

// MARK: - 6 types
extension Poly6: EncodablePrimaryResource, OptionalEncodablePrimaryResource
where A: EncodablePolyWrapped, B: EncodablePolyWrapped, C: EncodablePolyWrapped, D: EncodablePolyWrapped, E: EncodablePolyWrapped, F: EncodablePolyWrapped {}

extension Poly6: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped, F: PolyWrapped {}
where A: CodablePolyWrapped, B: CodablePolyWrapped, C: CodablePolyWrapped, D: CodablePolyWrapped, E: CodablePolyWrapped, F: CodablePolyWrapped {}

// MARK: - 7 types
extension Poly7: EncodablePrimaryResource, OptionalEncodablePrimaryResource
Expand All @@ -84,7 +84,7 @@ extension Poly7: EncodablePrimaryResource, OptionalEncodablePrimaryResource
G: EncodablePolyWrapped {}

extension Poly7: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped, F: PolyWrapped, G: PolyWrapped {}
where A: CodablePolyWrapped, B: CodablePolyWrapped, C: CodablePolyWrapped, D: CodablePolyWrapped, E: CodablePolyWrapped, F: CodablePolyWrapped, G: CodablePolyWrapped {}

// MARK: - 8 types
extension Poly8: EncodablePrimaryResource, OptionalEncodablePrimaryResource
Expand All @@ -99,7 +99,7 @@ extension Poly8: EncodablePrimaryResource, OptionalEncodablePrimaryResource
H: EncodablePolyWrapped {}

extension Poly8: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped, F: PolyWrapped, G: PolyWrapped, H: PolyWrapped {}
where A: CodablePolyWrapped, B: CodablePolyWrapped, C: CodablePolyWrapped, D: CodablePolyWrapped, E: CodablePolyWrapped, F: CodablePolyWrapped, G: CodablePolyWrapped, H: CodablePolyWrapped {}

// MARK: - 9 types
extension Poly9: EncodablePrimaryResource, OptionalEncodablePrimaryResource
Expand All @@ -115,7 +115,7 @@ extension Poly9: EncodablePrimaryResource, OptionalEncodablePrimaryResource
I: EncodablePolyWrapped {}

extension Poly9: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped, F: PolyWrapped, G: PolyWrapped, H: PolyWrapped, I: PolyWrapped {}
where A: CodablePolyWrapped, B: CodablePolyWrapped, C: CodablePolyWrapped, D: CodablePolyWrapped, E: CodablePolyWrapped, F: CodablePolyWrapped, G: CodablePolyWrapped, H: CodablePolyWrapped, I: CodablePolyWrapped {}

// MARK: - 10 types
extension Poly10: EncodablePrimaryResource, OptionalEncodablePrimaryResource
Expand All @@ -132,7 +132,7 @@ extension Poly10: EncodablePrimaryResource, OptionalEncodablePrimaryResource
J: EncodablePolyWrapped {}

extension Poly10: CodablePrimaryResource, OptionalCodablePrimaryResource
where A: PolyWrapped, B: PolyWrapped, C: PolyWrapped, D: PolyWrapped, E: PolyWrapped, F: PolyWrapped, G: PolyWrapped, H: PolyWrapped, I: PolyWrapped, J: PolyWrapped {}
where A: CodablePolyWrapped, B: CodablePolyWrapped, C: CodablePolyWrapped, D: CodablePolyWrapped, E: CodablePolyWrapped, F: CodablePolyWrapped, G: CodablePolyWrapped, H: CodablePolyWrapped, I: CodablePolyWrapped, J: CodablePolyWrapped {}

// MARK: - 11 types
extension Poly11: EncodablePrimaryResource, OptionalEncodablePrimaryResource
Expand All @@ -151,14 +151,14 @@ extension Poly11: EncodablePrimaryResource, OptionalEncodablePrimaryResource

extension Poly11: CodablePrimaryResource, OptionalCodablePrimaryResource
where
A: PolyWrapped,
B: PolyWrapped,
C: PolyWrapped,
D: PolyWrapped,
E: PolyWrapped,
F: PolyWrapped,
G: PolyWrapped,
H: PolyWrapped,
I: PolyWrapped,
J: PolyWrapped,
K: PolyWrapped {}
A: CodablePolyWrapped,
B: CodablePolyWrapped,
C: CodablePolyWrapped,
D: CodablePolyWrapped,
E: CodablePolyWrapped,
F: CodablePolyWrapped,
G: CodablePolyWrapped,
H: CodablePolyWrapped,
I: CodablePolyWrapped,
J: CodablePolyWrapped,
K: CodablePolyWrapped {}
10 changes: 8 additions & 2 deletions Sources/JSONAPITesting/Comparisons/Comparison.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
// Created by Mathew Polzin on 11/3/19.
//

public enum Comparison: Equatable, CustomStringConvertible {
public protocol Comparable: CustomStringConvertible {
var rawValue: String { get }

var isSame: Bool { get }
}

public enum Comparison: Comparable, Equatable {
case same
case different(String, String)
case prebuilt(String)
Expand Down Expand Up @@ -50,7 +56,7 @@ public enum Comparison: Equatable, CustomStringConvertible {

public typealias NamedDifferences = [String: String]

public protocol PropertyComparable: CustomStringConvertible {
public protocol PropertyComparable: Comparable {
var differences: NamedDifferences { get }
}

Expand Down
84 changes: 4 additions & 80 deletions Sources/JSONAPITesting/Comparisons/DocumentCompare.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,28 +79,8 @@ public enum BodyComparison: Equatable, CustomStringConvertible {
public var rawValue: String { description }
}

extension EncodableJSONAPIDocument where Body: Equatable {
public func compare<T>(to other: Self) -> DocumentComparison where PrimaryResourceBody == SingleResourceBody<T>, T: ResourceObjectType {
return DocumentComparison(
apiDescription: Comparison(
String(describing: apiDescription),
String(describing: other.apiDescription)
),
body: body.compare(to: other.body)
)
}

public func compare<T>(to other: Self) -> DocumentComparison where PrimaryResourceBody == SingleResourceBody<T?>, T: ResourceObjectType {
return DocumentComparison(
apiDescription: Comparison(
String(describing: apiDescription),
String(describing: other.apiDescription)
),
body: body.compare(to: other.body)
)
}

public func compare<T>(to other: Self) -> DocumentComparison where PrimaryResourceBody == ManyResourceBody<T>, T: ResourceObjectType {
extension EncodableJSONAPIDocument where Body: Equatable, PrimaryResourceBody: _OptionalResourceBody {
public func compare(to other: Self) -> DocumentComparison {
return DocumentComparison(
apiDescription: Comparison(
String(describing: apiDescription),
Expand All @@ -111,64 +91,8 @@ extension EncodableJSONAPIDocument where Body: Equatable {
}
}

extension DocumentBody where Self: Equatable {
public func compare<T>(to other: Self) -> BodyComparison where T: ResourceObjectType, PrimaryResourceBody == SingleResourceBody<T> {

// rule out case where they are the same
guard self != other else {
return .same
}

// rule out case where they are both error bodies
if let errors1 = errors, let errors2 = other.errors {
return .differentErrors(
BodyComparison.compare(
errors: errors1, meta, links,
with: errors2, meta, links
)
)
}

// rule out the case where they are both data
if let data1 = data, let data2 = other.data {
return .differentData(data1.compare(to: data2))
}

// we are left with the case where one is data and the
// other is an error if self.isError, then "the error
// is on the left"
return .dataErrorMismatch(errorOnLeft: isError)
}

public func compare<T>(to other: Self) -> BodyComparison where T: ResourceObjectType, PrimaryResourceBody == SingleResourceBody<T?> {

// rule out case where they are the same
guard self != other else {
return .same
}

// rule out case where they are both error bodies
if let errors1 = errors, let errors2 = other.errors {
return .differentErrors(
BodyComparison.compare(
errors: errors1, meta, links,
with: errors2, meta, links
)
)
}

// rule out the case where they are both data
if let data1 = data, let data2 = other.data {
return .differentData(data1.compare(to: data2))
}

// we are left with the case where one is data and the
// other is an error if self.isError, then "the error
// is on the left"
return .dataErrorMismatch(errorOnLeft: isError)
}

public func compare<T>(to other: Self) -> BodyComparison where T: ResourceObjectType, PrimaryResourceBody == ManyResourceBody<T> {
extension DocumentBody where Self: Equatable, PrimaryResourceBody: _OptionalResourceBody {
public func compare(to other: Self) -> BodyComparison {

// rule out case where they are the same
guard self != other else {
Expand Down
Loading

0 comments on commit 19636a4

Please sign in to comment.