Skip to content

Commit

Permalink
Merge pull request #60 from outfoxx/feature/transformable
Browse files Browse the repository at this point in the history
Add `ValueCodingTransformerProviding` that allows encoding/decoding without implmenting `Codable`
  • Loading branch information
kdubb committed Sep 24, 2023
2 parents 26135cf + 8ae5389 commit de4bc28
Show file tree
Hide file tree
Showing 2 changed files with 272 additions and 0 deletions.
116 changes: 116 additions & 0 deletions Sources/PotentCodables/ValueTransformerProviding.swift
@@ -0,0 +1,116 @@
//
// ValueTransformerProviding.swift
// PotentCodables
//
// Copyright 漏 2021 Outfox, inc.
//
//
// Distributed under the MIT License, See LICENSE for details.
//

import Foundation


public protocol InitializableValueEncodingTransformer: ValueEncodingTransformer {
init() throws
}

public protocol ValueEncodingTransformerProviding {

associatedtype EncodingTransformer: InitializableValueEncodingTransformer where EncodingTransformer.Target == Self

}


public protocol InitializableValueDecodingTransformer: ValueDecodingTransformer {
init() throws
}

public protocol ValueDecodingTransformerProviding {

associatedtype DecodingTransformer: InitializableValueDecodingTransformer where DecodingTransformer.Target == Self

}


public typealias InitializableValueCodingTransformer = InitializableValueEncodingTransformer & InitializableValueDecodingTransformer

public protocol ValueCodingTransformerProviding: ValueEncodingTransformerProviding, ValueDecodingTransformerProviding
where EncodingTransformer == CodingTransformer, DecodingTransformer == CodingTransformer {

associatedtype CodingTransformer: InitializableValueCodingTransformer where CodingTransformer.Target == Self

}


public extension KeyedDecodingContainer {

func decode<Value: ValueDecodingTransformerProviding>(_ type: Value.Type, forKey key: Key) throws -> Value {
return try decode(forKey: key, using: Value.DecodingTransformer())
}

}

public extension UnkeyedDecodingContainer {

mutating func decode<Value: ValueDecodingTransformerProviding>(_ type: Value.Type) throws -> Value {
return try decode(using: Value.DecodingTransformer())
}

mutating func decodeContents<Value: ValueDecodingTransformerProviding>(_ type: Value.Type) throws -> [Value] {
return try decodeContents(using: Value.DecodingTransformer())
}

}

public extension SingleValueDecodingContainer {

mutating func decode<Value: ValueDecodingTransformerProviding>(_ type: Value.Type) throws -> Value {
return try decode(using: Value.DecodingTransformer())
}

}

public extension KeyedEncodingContainer {

mutating func encode<Value: ValueEncodingTransformerProviding>(_ value: Value, forKey key: Key) throws {
return try encode(value, forKey: key, using: Value.EncodingTransformer())
}

}

public extension UnkeyedEncodingContainer {

mutating func encode<Value: ValueEncodingTransformerProviding>(_ value: Value) throws {
return try encode(value, using: Value.EncodingTransformer())
}

mutating func encode<S: Sequence>(contentsOf values: S) throws where S.Element: ValueEncodingTransformerProviding {
return try encode(contentsOf: values, using: S.Element.EncodingTransformer())
}

}

public extension SingleValueEncodingContainer {

mutating func encode<Value: ValueEncodingTransformerProviding>(_ value: Value) throws {
return try encode(value, using: Value.EncodingTransformer())
}

}

public extension TopLevelDecoder {

func decode<Value: ValueDecodingTransformerProviding>(_ type: Value.Type, from input: Input) throws -> Value {
return try decode(from: input, using: Value.DecodingTransformer())
}

}

public extension TopLevelEncoder {

func encode<Value: ValueEncodingTransformerProviding>(_ value: Value) throws -> Output {
return try encode(value, using: Value.EncodingTransformer())
}

}
156 changes: 156 additions & 0 deletions Tests/ValueTransformerProvidingTests.swift
@@ -0,0 +1,156 @@
//
// ValueTransformerProvidingTests.swift
// PotentCodables
//
// Copyright 漏 2021 Outfox, inc.
//
//
// Distributed under the MIT License, See LICENSE for details.
//

@testable import PotentCodables
@testable import PotentJSON
import XCTest


class ValueTransformableTests: XCTestCase {

func testTopLevel() throws {
let encoded = Uncodable(data: Data([1, 2, 3, 4, 5]))
let data = try JSONEncoder.default.encode(encoded)
let decoded = try JSONDecoder.default.decode(Uncodable.self, from: data)
XCTAssertEqual(encoded, decoded)
}

func testKeyContainer() throws {

struct Test: Codable, Equatable {
var value: Uncodable

init(value: Uncodable) {
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(Uncodable.self, forKey: .value)
}

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

let encoded = Test(value: Uncodable(data: Data([1, 2, 3, 4, 5])))
let data = try JSONEncoder.default.encode(encoded)
let decoded = try JSONDecoder.default.decode(Test.self, from: data)
XCTAssertEqual(encoded, decoded)
}

func testUnkeyedContainerContents() throws {

struct Test: Codable, Equatable {
var value: [Uncodable]

init(value: [Uncodable]) {
self.value = value
}

init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
self.value = try container.decodeContents(Uncodable.self)
}

func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(contentsOf: value)
}
}

let encoded = Test(value: [Uncodable(data: Data([1, 2, 3, 4, 5])), Uncodable(data: Data([6, 7, 8, 9, 0]))])
let data = try JSONEncoder.default.encode(encoded)
let decoded = try JSONDecoder.default.decode(Test.self, from: data)
XCTAssertEqual(encoded, decoded)
}

func testUnkeyedContainer() throws {

struct Test: Codable, Equatable {
var value: [Uncodable]

init(value: [Uncodable]) {
self.value = value
}

init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var value: [Uncodable] = []
while !container.isAtEnd {
value.append(try container.decode(Uncodable.self))
}
self.value = value
}

func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
for element in value {
try container.encode(element)
}
}
}

let encoded = Test(value: [Uncodable(data: Data([1, 2, 3, 4, 5])), Uncodable(data: Data([6, 7, 8, 9, 0]))])
let data = try JSONEncoder.default.encode(encoded)
let decoded = try JSONDecoder.default.decode(Test.self, from: data)
XCTAssertEqual(encoded, decoded)
}

func testSingleValueContainer() throws {

struct Test: Codable, Equatable {
var value: Uncodable

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

init(from decoder: Decoder) throws {
var container = try decoder.singleValueContainer()
self.value = try container.decode(Uncodable.self)
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(value)
}
}

let encoded = Test(value: Uncodable(data: Data([1, 2, 3, 4, 5])))
let data = try JSONEncoder.default.encode(encoded)
let decoded = try JSONDecoder.default.decode(Test.self, from: data)
XCTAssertEqual(encoded, decoded)
}

}


struct Uncodable: Equatable {
var data: Data
}

extension Uncodable: ValueCodingTransformerProviding {

struct CodingTransformer: InitializableValueCodingTransformer {
func decode(_ value: Data) throws -> Uncodable {
return Uncodable(data: try JSONDecoder.default.decode(Data.self, from: value))
}
func encode(_ value: Uncodable) throws -> Data {
return try JSONEncoder.default.encode(value.data)
}
}
}

0 comments on commit de4bc28

Please sign in to comment.