mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-03-30 11:18:38 -07:00
Add ability to specify an Attribute needs to be transformed once it has been decoded.
This commit is contained in:
@@ -5,17 +5,28 @@
|
||||
// Created by Mathew Polzin on 11/13/18.
|
||||
//
|
||||
|
||||
public struct Attribute<Value: Codable>: Codable {
|
||||
public let value: Value
|
||||
public struct TransformAttribute<RawValue: Codable, Transformer: JSONAPI.Transformer>: Codable where Transformer.From == RawValue {
|
||||
private let rawValue: RawValue
|
||||
|
||||
public let value: Transformer.To
|
||||
|
||||
public init(rawValue: RawValue) throws {
|
||||
self.rawValue = rawValue
|
||||
value = try Transformer.transform(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension Attribute: Equatable where Value: Equatable {}
|
||||
public typealias Attribute<T: Codable> = TransformAttribute<T, IdentityTransformer<T>>
|
||||
|
||||
extension TransformAttribute: Equatable where Transformer.From: Equatable, Transformer.To: Equatable {}
|
||||
|
||||
// MARK: - Codable
|
||||
extension Attribute {
|
||||
extension TransformAttribute {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
|
||||
let rawVal: RawValue
|
||||
|
||||
// A little trickery follows. If the value is nil, the
|
||||
// container.decode(Value.self) will fail even if Value
|
||||
// is Optional. However, we can check if decoding nil
|
||||
@@ -23,12 +34,14 @@ extension Attribute {
|
||||
// type at which point we can store nil in `value`.
|
||||
let anyNil: Any? = nil
|
||||
if container.decodeNil(),
|
||||
let val = anyNil as? Value {
|
||||
value = val
|
||||
return
|
||||
let val = anyNil as? Transformer.From {
|
||||
rawVal = val
|
||||
} else {
|
||||
rawVal = try container.decode(Transformer.From.self)
|
||||
}
|
||||
|
||||
value = try container.decode(Value.self)
|
||||
rawValue = rawVal
|
||||
value = try Transformer.transform(rawVal)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@@ -37,11 +50,24 @@ extension Attribute {
|
||||
// See note in decode above about the weirdness
|
||||
// going on here.
|
||||
let anyNil: Any? = nil
|
||||
if let _ = anyNil as? Value,
|
||||
(value as Any?) == nil {
|
||||
if let _ = anyNil as? Transformer.From,
|
||||
(rawValue as Any?) == nil {
|
||||
try container.encodeNil()
|
||||
}
|
||||
|
||||
try container.encode(value)
|
||||
try container.encode(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Transformers
|
||||
|
||||
public protocol Transformer {
|
||||
associatedtype From
|
||||
associatedtype To
|
||||
|
||||
static func transform(_ from: From) throws -> To
|
||||
}
|
||||
|
||||
public enum IdentityTransformer<T>: Transformer {
|
||||
public static func transform(_ from: T) throws -> T { return from }
|
||||
}
|
||||
|
||||
@@ -139,16 +139,23 @@ public extension Entity {
|
||||
/// Access the attribute at the given keypath. This just
|
||||
/// allows you to write `entity[\.propertyName]` instead
|
||||
/// of `entity.relationships.propertyName`.
|
||||
subscript<T>(_ path: KeyPath<EntityType.Attributes, Attribute<T>>) -> T {
|
||||
subscript<T, TFRM: Transformer>(_ path: KeyPath<EntityType.Attributes, TransformAttribute<T, TFRM>>) -> TFRM.To {
|
||||
return attributes[keyPath: path].value
|
||||
}
|
||||
|
||||
/// Access the attribute at the given keypath. This just
|
||||
/// allows you to write `entity[\.propertyName]` instead
|
||||
/// of `entity.relationships.propertyName`.
|
||||
subscript<T>(_ path: KeyPath<EntityType.Attributes, Attribute<T>?>) -> T? {
|
||||
subscript<T, TFRM: Transformer>(_ path: KeyPath<EntityType.Attributes, TransformAttribute<T, TFRM>?>) -> TFRM.To? {
|
||||
return attributes[keyPath: path]?.value
|
||||
}
|
||||
|
||||
/// Access the attribute at the given keypath. This just
|
||||
/// allows you to write `entity[\.propertyName]` instead
|
||||
/// of `entity.relationships.propertyName`.
|
||||
subscript<T, TFRM: Transformer, U>(_ path: KeyPath<EntityType.Attributes, TransformAttribute<T, TFRM>?>) -> U? where TFRM.To == U? {
|
||||
return attributes[keyPath: path].flatMap { $0.value }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Relationship Access
|
||||
|
||||
Reference in New Issue
Block a user