mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-03-30 11:18:38 -07:00
update swift tools version in package file, add some property wrappers, add some tests for wrappers. its all broken but worth holding onto for now.
This commit is contained in:
+1
-1
@@ -34,5 +34,5 @@ let package = Package(
|
||||
name: "JSONAPITestingTests",
|
||||
dependencies: ["JSONAPI", "JSONAPITesting"])
|
||||
],
|
||||
swiftLanguageVersions: [.v5]
|
||||
swiftLanguageVersions: [.version("5.1")]
|
||||
)
|
||||
|
||||
@@ -17,9 +17,9 @@ public protocol AttributeType: Codable {
|
||||
/// A TransformedAttribute takes a Codable type and attempts to turn it into another type.
|
||||
public struct TransformedAttribute<RawValue: Codable, Transformer: JSONAPI.Transformer>: AttributeType where Transformer.From == RawValue {
|
||||
public let rawValue: RawValue
|
||||
|
||||
|
||||
public let value: Transformer.To
|
||||
|
||||
|
||||
public init(rawValue: RawValue) throws {
|
||||
self.rawValue = rawValue
|
||||
value = try Transformer.transform(rawValue)
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// PropertyWrappers.swift
|
||||
//
|
||||
//
|
||||
// Created by Mathew Polzin on 6/20/19.
|
||||
//
|
||||
|
||||
|
||||
// MARK: - Transformed
|
||||
@propertyWrapper
|
||||
public struct Transformed<Transformer: JSONAPI.Transformer> {
|
||||
|
||||
public typealias RawValue = Transformer.From
|
||||
public typealias Value = Transformer.To
|
||||
|
||||
private var _value: Value?
|
||||
|
||||
public var wrappedValue: Value {
|
||||
get {
|
||||
guard let ret = _value else {
|
||||
fatalError("Attribute read from before initialization.")
|
||||
}
|
||||
return ret
|
||||
}
|
||||
set {
|
||||
_value = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public init(initialValue: Value, _ transformer: Transformer.Type) {
|
||||
self._value = initialValue
|
||||
}
|
||||
|
||||
public init(_ transformer: Transformer.Type) {
|
||||
self._value = nil
|
||||
}
|
||||
|
||||
public init(rawValue: RawValue, _ transformer: Transformer.Type) throws {
|
||||
self._value = try Transformer.transform(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension Transformed: Decodable where Transformer.From: Decodable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
|
||||
let rawVal = try container.decode(Transformer.From.self)
|
||||
|
||||
_value = try Transformer.transform(rawVal)
|
||||
}
|
||||
}
|
||||
|
||||
extension Transformed: Encodable where Transformer: ReversibleTransformer, Transformer.From: Encodable {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
|
||||
guard let value = _value else {
|
||||
fatalError("Attribute encoded before initialization.")
|
||||
}
|
||||
|
||||
try container.encode(Transformer.reverse(value))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Nullable
|
||||
|
||||
public protocol _Optional {
|
||||
static var nilValue: Self { get }
|
||||
var isNilValue: Bool { get }
|
||||
}
|
||||
|
||||
extension Optional: _Optional {
|
||||
public static var nilValue: Self {
|
||||
return .none
|
||||
}
|
||||
|
||||
public var isNilValue: Bool { return self == nil }
|
||||
}
|
||||
|
||||
protocol _Nullable {}
|
||||
|
||||
@propertyWrapper
|
||||
public struct Nullable<T: Decodable>: Decodable, _Optional, _Nullable {
|
||||
public var wrappedValue: T?
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
|
||||
if container.decodeNil() {
|
||||
wrappedValue = nil
|
||||
return
|
||||
}
|
||||
|
||||
wrappedValue = try container.decode(T.self)
|
||||
}
|
||||
|
||||
public init(initialValue: T? = nil) {
|
||||
wrappedValue = initialValue
|
||||
}
|
||||
|
||||
public static var nilValue: Self {
|
||||
return .init()
|
||||
}
|
||||
|
||||
public var isNilValue: Bool {
|
||||
return wrappedValue == nil
|
||||
}
|
||||
}
|
||||
|
||||
extension Nullable: Encodable where T: Encodable {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(wrappedValue)
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,70 @@ class AttributeTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Property Wrappers
|
||||
extension AttributeTests {
|
||||
func test_Transformed() {
|
||||
|
||||
struct Test: Codable {
|
||||
@Transformed(IntToString.self)
|
||||
var value: String = ""
|
||||
}
|
||||
|
||||
let test = Test(value: "hello")
|
||||
XCTAssertEqual(test.value, "hello")
|
||||
|
||||
let test2 = try! JSONDecoder().decode(Test.self,
|
||||
from: #"{"value": 12}"#.data(using: .utf8)!)
|
||||
|
||||
XCTAssertEqual(test2.value, "12")
|
||||
try! print(String(data: JSONEncoder().encode(test2), encoding: .utf8)!)
|
||||
|
||||
let test3 = try? JSONDecoder().decode(Test.self,
|
||||
from: #"{"value": null}"#.data(using: .utf8)!)
|
||||
|
||||
XCTAssertNil(test3)
|
||||
}
|
||||
|
||||
func test_Nullable() {
|
||||
struct Test: Codable {
|
||||
@Nullable
|
||||
var value: String?
|
||||
}
|
||||
|
||||
let test = Test(value: nil)
|
||||
XCTAssertNil(test.value)
|
||||
|
||||
let test2 = Test(value: "hello")
|
||||
XCTAssertEqual(test2.value, "hello")
|
||||
|
||||
let test3 = try! JSONDecoder().decode(Test.self,
|
||||
from: #"{"value": "world"}"#.data(using: .utf8)!)
|
||||
|
||||
XCTAssertEqual(test3.value, "world")
|
||||
try! print(String(data: JSONEncoder().encode(test2), encoding: .utf8)!)
|
||||
|
||||
let test4 = try? JSONDecoder().decode(Test.self,
|
||||
from: #"{"value": null}"#.data(using: .utf8)!)
|
||||
|
||||
XCTAssertNotNil(test4)
|
||||
XCTAssertNil(test4?.value)
|
||||
}
|
||||
|
||||
func test_NullableTransformed() {
|
||||
struct Test: Codable {
|
||||
// Nullable<Transformed<IntToString>>
|
||||
let x: Transformed<IntToString>
|
||||
// @Nullable @Transformed(IdentityTransformer.self)
|
||||
@Transformed(IntToString.self) @Nullable
|
||||
var value: String?
|
||||
}
|
||||
|
||||
let test = Test(x: .init(initialValue: "12", IntToString.self))
|
||||
|
||||
print(test.x.wrappedValue)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Test types
|
||||
extension AttributeTests {
|
||||
enum TestTransformer: ReversibleTransformer {
|
||||
@@ -77,12 +141,37 @@ extension AttributeTests {
|
||||
}
|
||||
}
|
||||
|
||||
enum IntToString: Transformer {
|
||||
enum IntToString: ReversibleTransformer {
|
||||
public static func transform(_ from: Int) -> String {
|
||||
return String(from)
|
||||
}
|
||||
|
||||
public static func reverse(_ value: String) throws -> Int {
|
||||
guard let intValue = Int(value) else {
|
||||
fatalError("Reversed IntToString with invalid String value.")
|
||||
}
|
||||
return intValue
|
||||
}
|
||||
}
|
||||
|
||||
enum OptionalIntToOptionalString: ReversibleTransformer {
|
||||
public static func transform(_ from: Int?) -> String? {
|
||||
return from.map(String.init)
|
||||
}
|
||||
|
||||
public static func reverse(_ value: String?) throws -> Int? {
|
||||
guard let stringValue = value else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let intValue = Int(stringValue) else {
|
||||
fatalError("Reversed IntToString with invalid String value.")
|
||||
}
|
||||
|
||||
return intValue
|
||||
}
|
||||
}
|
||||
|
||||
enum IntToInt: Transformer {
|
||||
public static func transform(_ from: Int) -> Int {
|
||||
return from + 100
|
||||
|
||||
@@ -29,7 +29,7 @@ class EntityTests: XCTestCase {
|
||||
let entity1 = TestEntity1(attributes: .none, relationships: .none, meta: .none, links: .none)
|
||||
let entity = TestEntity9(attributes: .none, relationships: .init(one: entity1.pointer, nullableOne: .init(resourceObject: entity1, meta: .none, links: .none), optionalOne: .init(resourceObject: entity1, meta: .none, links: .none), optionalNullableOne: nil, optionalMany: .init(resourceObjects: [entity1, entity1], meta: .none, links: .none)), meta: .none, links: .none)
|
||||
|
||||
XCTAssertEqual(entity ~> \.optionalOne, entity1.id)
|
||||
XCTAssertEqual(entity ~> \.optionalOne, Optional(entity1.id))
|
||||
}
|
||||
|
||||
func test_toMany_relationship_operator_access() {
|
||||
|
||||
Reference in New Issue
Block a user