Skip to content

Commit

Permalink
Cleanup Ref/EmbeddRef API
Browse files Browse the repository at this point in the history
  • Loading branch information
kdubb committed Feb 11, 2023
1 parent fd10f63 commit 0532716
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 18 deletions.
50 changes: 37 additions & 13 deletions Sources/PotentCodables/Refs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -287,18 +287,30 @@ public struct CustomRef<TKP: TypeKeyProvider, VKP: ValueKeyProvider, TI: TypeInd
/// {"@type" : "MyApp.VyValue", "value": {"name" : "Foo"}}
/// ```
///
public struct Value<EncodedValue: Encodable>: Encodable {
public struct Value: Encodable {

public let value: EncodedValue
public let value: Any?

public init(_ value: EncodedValue) {
public init<T>(_ value: T?) {
precondition(value == nil || value is Encodable)
self.value = value
}

public init(_ value: Any) {
precondition(value is Encodable)
self.value = value
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: AnyCodingKey.self)
try container.encode(TI.typeId(of: EncodedValue.self), forKey: TKP.typeKey)
try container.encode(value, forKey: VKP.valueKey)
if let value = value {
var container = encoder.container(keyedBy: AnyCodingKey.self)
try container.encode(TI.typeId(of: type(of: value)), forKey: TKP.typeKey)
try container.encode((value as? Encodable).unsafelyUnwrapped, forKey: VKP.valueKey)
}
else {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}

}
Expand Down Expand Up @@ -397,18 +409,30 @@ public struct CustomEmbeddedRef<TKP: TypeKeyProvider, TI: TypeIndex>: Decodable
/// {"@type" : "MyApp.VyValue", "name" : "Foo"}
/// ```
///
public struct Value<EncodedValue: Encodable>: Encodable {
public struct Value: Encodable {

public let value: EncodedValue
public let value: Any?

public init<T>(_ value: T?) {
precondition(value == nil || value is Encodable)
self.value = value
}

public init(_ value: EncodedValue) {
public init(_ value: Any) {
precondition(value is Encodable)
self.value = value
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: AnyCodingKey.self)
try container.encode(TI.typeId(of: EncodedValue.self), forKey: TKP.typeKey)
try value.encode(to: encoder)
if let value = value {
var container = encoder.container(keyedBy: AnyCodingKey.self)
try container.encode(TI.typeId(of: type(of: value)), forKey: TKP.typeKey)
try (value as? Encodable).unsafelyUnwrapped.encode(to: encoder)
}
else {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}

}
Expand All @@ -420,7 +444,7 @@ public struct CustomEmbeddedRef<TKP: TypeKeyProvider, TI: TypeIndex>: Decodable
///
public enum Refs {

enum Error: Swift.Error {
public enum Error: Swift.Error {
case typeNotFound(String)
case invalidValue(String)
}
Expand Down
55 changes: 50 additions & 5 deletions Tests/RefTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,40 @@ class RefTests: XCTestCase {
XCTAssertEqual(src.name, try ref.as(AValue.self).name)
}

func testNestedRef() throws {

struct TestValue: Codable {
var value: RefTestValue

init(value: RefTestValue) {
self.value = value
}

enum CodingKeys: CodingKey {
case value
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.value = try container.decode(Ref.self, forKey: .value).as(RefTestValue.self)
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(Ref.Value(value), forKey: .value)
}
}

let src = TestValue(value: AValue(name: "test"))

let json = try JSON.Encoder.default.encodeTree(src)
XCTAssertNotNil(json["value"]?["@type"], "AValue")
XCTAssertEqual(json.value?.value?.name, "test")

let testValue = try JSONDecoder.default.decodeTree(TestValue.self, from: json)
XCTAssertEqual((testValue.value as? AValue)?.name, (src.value as? AValue)?.name)
}

func testCustomRef() throws {

struct MyTypeKeys: TypeKeyProvider, ValueKeyProvider {
Expand Down Expand Up @@ -279,13 +313,13 @@ class RefTests: XCTestCase {

func testRefSingleNil() throws {

let json = try JSON.Encoder.default.encodeTree(Ref.Value(nil as Bool?))
XCTAssertNotNil(json["@type"])
XCTAssertEqual(json["value"], .null)
let nullValue: RefTestValue? = nil
let json = try JSON.Encoder.default.encodeTree(Ref.Value(nullValue))
XCTAssertEqual(json, .null)

let ref = try JSONDecoder.default.decodeTree(Ref.self, from: json)
let ref = try JSONDecoder.default.decodeTreeIfPresent(Ref.self, from: json)

XCTAssertEqual(try ref.as(Bool?.self), nil)
XCTAssertNil(ref)
}

func testRefSingleBoolArray() throws {
Expand Down Expand Up @@ -323,6 +357,17 @@ class RefTests: XCTestCase {
XCTAssertThrowsError(try JSONDecoder.default.decodeTree(Ref.self, from: json))
}

func testEmbeddedRefSingleNil() throws {

let nullValue: RefTestValue? = nil
let json = try JSON.Encoder.default.encodeTree(EmbeddedRef.Value(nullValue))
XCTAssertEqual(json, .null)

let ref = try JSONDecoder.default.decodeTreeIfPresent(EmbeddedRef.self, from: json)

XCTAssertNil(ref)
}

func testEmbeddedRefDecodeFailsWhenTypeNotAuthorized() throws {

struct TestValue: Codable {
Expand Down

0 comments on commit 0532716

Please sign in to comment.