diff --git a/Package.swift b/Package.swift index a7a2c988..575199f6 100644 --- a/Package.swift +++ b/Package.swift @@ -20,8 +20,8 @@ let package = Package( .target(name: "Bits", dependencies: ["NIO"]), .target(name: "Core", dependencies: ["Async", "Bits", "Debugging"]), .testTarget(name: "CoreTests", dependencies: ["Core"]), - .target(name: "CodableKit", dependencies: ["Async", "Debugging"]), - .testTarget(name: "CodableKitTests", dependencies: ["CodableKit"]), + .target(name: "CodableKit", dependencies: ["Async", "Core", "Debugging"]), + .testTarget(name: "CodableKitTests", dependencies: ["CodableKit", "Core"]), .target(name: "COperatingSystem"), .target(name: "Debugging"), .testTarget(name: "DebuggingTests", dependencies: ["Debugging"]), diff --git a/Sources/CodableKit/StringKey.swift b/Sources/CodableKit/BasicKey.swift similarity index 100% rename from Sources/CodableKit/StringKey.swift rename to Sources/CodableKit/BasicKey.swift diff --git a/Sources/CodableKit/CodableData/CodableData.swift b/Sources/CodableKit/CodableData/CodableData.swift deleted file mode 100644 index 13ec572b..00000000 --- a/Sources/CodableKit/CodableData/CodableData.swift +++ /dev/null @@ -1,49 +0,0 @@ -public enum CodableData { - case string(String) - case bool(Bool) - - case int(Int) - case int8(Int8) - case int16(Int16) - case int32(Int32) - case int64(Int64) - - case uint(UInt) - case uint8(UInt8) - case uint16(UInt16) - case uint32(UInt32) - case uint64(UInt64) - - case float(Float) - case double(Double) - - case dictionary([String: CodableData]) - case array([CodableData]) - - case null -} - -extension CodableData: Equatable { - /// See Equatable.== - public static func ==(lhs: CodableData, rhs: CodableData) -> Bool { - switch (lhs, rhs) { - case (.string(let a), .string(let b)): return a == b - case (.int(let a), .int(let b)): return a == b - case (.int8(let a), .int8(let b)): return a == b - case (.int16(let a), .int16(let b)): return a == b - case (.int32(let a), .int32(let b)): return a == b - case (.int64(let a), .int64(let b)): return a == b - case (.uint(let a), .uint(let b)): return a == b - case (.uint8(let a), .uint8(let b)): return a == b - case (.uint16(let a), .uint16(let b)): return a == b - case (.uint32(let a), .uint32(let b)): return a == b - case (.uint64(let a), .uint64(let b)): return a == b - case (.float(let a), .float(let b)): return a == b - case (.double(let a), .double(let b)): return a == b - case (.dictionary(let a), .dictionary(let b)): return a == b - case (.array(let a), .array(let b)): return a == b - case (.null, .null): return true - default: return false - } - } -} diff --git a/Sources/CodableKit/CodableData/CodableDataArrayKey.swift b/Sources/CodableKit/CodableData/CodableDataArrayKey.swift deleted file mode 100644 index 4581950f..00000000 --- a/Sources/CodableKit/CodableData/CodableDataArrayKey.swift +++ /dev/null @@ -1,24 +0,0 @@ -/// Represents an array index. -struct CodableDataArrayKey: CodingKey { - /// See `CodingKey.intValue` - var intValue: Int? - - /// See `CodingKey.stringValue` - var stringValue: String - - /// See `CodingKey.init(stringValue:)` - init?(stringValue: String) { - return nil - } - - /// See `CodingKey.init(intValue:)` - init?(intValue: Int) { - self.init(intValue) - } - - /// Creates a new `CodableDataArrayKey` from the supplied index. - init(_ index: Int) { - self.intValue = index - self.stringValue = index.description - } -} diff --git a/Sources/CodableKit/CodableData/Decoder/CodableDataDecoder.swift b/Sources/CodableKit/CodableData/Decoder/CodableDataDecoder.swift deleted file mode 100644 index dd54adb2..00000000 --- a/Sources/CodableKit/CodableData/Decoder/CodableDataDecoder.swift +++ /dev/null @@ -1,51 +0,0 @@ -/// Encodes `Decodable` items to `CodableData`. -public final class CodableDataDecoder { - /// Creates a new `CodableDataDecoder`. - public init() {} - - /// Decodes the supplied `Decodable` to `CodableData` - public func decode(_ type: D.Type = D.self, from data: CodableData) throws -> D - where D: Decodable - { - let decoder = _CodableDataDecoder(partialData: .init(data: data), at: []) - return try D(from: decoder) - } -} - -/// Internal `Decoder` implementation for `CodableDataDecoder`. -internal final class _CodableDataDecoder: Decoder { - /// See `Decoder.codingPath` - var codingPath: [CodingKey] - - /// See `Decoder.codingPath` - var userInfo: [CodingUserInfoKey: Any] - - /// Data being encoded. - let partialData: PartialCodableData - - /// Creates a new internal `_CodableDataDecoder`. - init(partialData: PartialCodableData, at path: [CodingKey]) { - self.codingPath = path - self.userInfo = [:] - self.partialData = partialData - } - - /// See `Decoder.container` - func container(keyedBy type: Key.Type) -> KeyedDecodingContainer - where Key: CodingKey - { - let container = CodableDataKeyedDecodingContainer(partialData: partialData, at: codingPath) - return .init(container) - } - - /// See `Decoder.unkeyedContainer` - func unkeyedContainer() -> UnkeyedDecodingContainer { - return CodableDataUnkeyedDecodingContainer(partialData: partialData, at: codingPath) - } - - /// See `Decoder.singleValueContainer` - func singleValueContainer() -> SingleValueDecodingContainer { - return CodableDataSingleValueDecodingContainer(partialData: partialData, at: codingPath) - } -} - diff --git a/Sources/CodableKit/CodableData/Decoder/CodableDataKeyedDecodingContainer.swift b/Sources/CodableKit/CodableData/Decoder/CodableDataKeyedDecodingContainer.swift deleted file mode 100644 index 28f4ed23..00000000 --- a/Sources/CodableKit/CodableData/Decoder/CodableDataKeyedDecodingContainer.swift +++ /dev/null @@ -1,135 +0,0 @@ -/// Internal `KeyedDecodingContainerProtocol` for `CodableDataDecoder` -final class CodableDataKeyedDecodingContainer: KeyedDecodingContainerProtocol - where K: CodingKey -{ - /// See `KeyedDecodingContainerProtocol.allKeys` - var allKeys: [K] - - /// See `KeyedDecodingContainerProtocol.codingPath` - var codingPath: [CodingKey] - - /// Data being encoded. - let partialData: PartialCodableData - - /// Creates a new internal `CodableDataKeyedDecodingContainer`. - init(partialData: PartialCodableData, at path: [CodingKey]) { - self.codingPath = path - self.partialData = partialData - switch partialData.get(at: path) { - case .some(let data): - switch data { - case .dictionary(let value): allKeys = value.keys.compactMap { Key(stringValue: $0) } - default: allKeys = [] - } - default: allKeys = [] - } - } - - /// See `KeyedDecodingContainerProtocol.contains` - func contains(_ key: K) -> Bool { - return allKeys.contains { key.stringValue == $0.stringValue } - } - - /// See `KeyedDecodingContainerProtocol.decodeNil` - func decodeNil(forKey key: K) throws -> Bool { - return partialData.decodeNil(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: Bool.Type, forKey key: K) throws -> Bool { - return try partialData.decodeBool(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: Int.Type, forKey key: K) throws -> Int { - return try partialData.decodeFixedWidthInteger(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: Int8.Type, forKey key: K) throws -> Int8 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: Int16.Type, forKey key: K) throws -> Int16 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: Int32.Type, forKey key: K) throws -> Int32 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: Int64.Type, forKey key: K) throws -> Int64 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: UInt.Type, forKey key: K) throws -> UInt { - return try partialData.decodeFixedWidthInteger(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: UInt8.Type, forKey key: K) throws -> UInt8 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: UInt16.Type, forKey key: K) throws -> UInt16 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: UInt32.Type, forKey key: K) throws -> UInt32 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: UInt64.Type, forKey key: K) throws -> UInt64 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: Float.Type, forKey key: K) throws -> Float { - return try partialData.decodeFloatingPoint(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: Double.Type, forKey key: K) throws -> Double { - return try partialData.decodeFloatingPoint(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: String.Type, forKey key: K) throws -> String { - return try partialData.decodeString(at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.decode` - func decode(_ type: T.Type, forKey key: K) throws -> T where T: Decodable { - let decoder = _CodableDataDecoder(partialData: partialData, at: codingPath + [key]) - return try T(from: decoder) - } - - /// See `KeyedDecodingContainerProtocol.nestedContainer` - func nestedContainer(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer where NestedKey : CodingKey { - let container = CodableDataKeyedDecodingContainer(partialData: partialData, at: codingPath + [key]) - return .init(container) - } - - /// See `KeyedDecodingContainerProtocol.nestedUnkeyedContainer` - func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer { - return CodableDataUnkeyedDecodingContainer(partialData: partialData, at: codingPath + [key]) - } - - /// See `KeyedDecodingContainerProtocol.superDecoder` - func superDecoder() throws -> Decoder { - return _CodableDataDecoder(partialData: partialData, at: codingPath) - } - - /// See `KeyedDecodingContainerProtocol.superDecoder` - func superDecoder(forKey key: K) throws -> Decoder { - return _CodableDataDecoder(partialData: partialData, at: codingPath + [key]) - } -} - diff --git a/Sources/CodableKit/CodableData/Decoder/CodableDataSingleValueDecodingContainer.swift b/Sources/CodableKit/CodableData/Decoder/CodableDataSingleValueDecodingContainer.swift deleted file mode 100644 index 32858a03..00000000 --- a/Sources/CodableKit/CodableData/Decoder/CodableDataSingleValueDecodingContainer.swift +++ /dev/null @@ -1,113 +0,0 @@ -/// Internal `SingleValueDecodingContainer` for `CodableDataDecoder` -final class CodableDataSingleValueDecodingContainer: SingleValueDecodingContainer { - /// See `SingleValueDecodingContainer.codingPath` - var codingPath: [CodingKey] - - /// Data being encoded. - let partialData: PartialCodableData - - /// Creates a new internal `CodableDataSingleValueDecodingContainer`. - init(partialData: PartialCodableData, at path: [CodingKey]) { - self.codingPath = path - self.partialData = partialData - } - - /// See `SingleValueDecodingContainer.decodeNil` - func decodeNil() -> Bool { - return partialData.decodeNil(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: Bool.Type) throws -> Bool { - return try partialData.decodeBool(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: Int.Type) throws -> Int { - return try partialData.decodeFixedWidthInteger(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: Int8.Type) throws -> Int8 { - return try partialData.decodeFixedWidthInteger(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: Int16.Type) throws -> Int16 { - return try partialData.decodeFixedWidthInteger(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: Int32.Type) throws -> Int32 { - return try partialData.decodeFixedWidthInteger(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: Int64.Type) throws -> Int64 { - return try partialData.decodeFixedWidthInteger(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: UInt.Type) throws -> UInt { - return try partialData.decodeFixedWidthInteger(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: UInt8.Type) throws -> UInt8 { - return try partialData.decodeFixedWidthInteger(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: UInt16.Type) throws -> UInt16 { - return try partialData.decodeFixedWidthInteger(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: UInt32.Type) throws -> UInt32 { - return try partialData.decodeFixedWidthInteger(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: UInt64.Type) throws -> UInt64 { - return try partialData.decodeFixedWidthInteger(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: Float.Type) throws -> Float { - return try partialData.decodeFloatingPoint(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: Double.Type) throws -> Double { - return try partialData.decodeFloatingPoint(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: String.Type) throws -> String { - return try partialData.decodeString(at: codingPath) - } - - /// See `SingleValueDecodingContainer.decode` - func decode(_ type: T.Type) throws -> T where T: Decodable { - let decoder = _CodableDataDecoder(partialData: partialData, at: codingPath) - return try T(from: decoder) - } - - /// See `SingleValueDecodingContainer.nestedContainer` - func nestedContainer(keyedBy type: Key.Type) throws -> KeyedDecodingContainer - where Key: CodingKey - { - let container = CodableDataKeyedDecodingContainer(partialData: partialData, at: codingPath) - return .init(container) - } - - /// See `SingleValueDecodingContainer.nestedSingleValueContainer` - func nestedSingleValueContainer() throws -> SingleValueDecodingContainer { - return CodableDataSingleValueDecodingContainer(partialData: partialData, at: codingPath) - } - - /// See `SingleValueDecodingContainer.superDecoder` - func superDecoder() throws -> Decoder { - return _CodableDataDecoder(partialData: partialData, at: codingPath) - } -} diff --git a/Sources/CodableKit/CodableData/Decoder/CodableDataUnkeyedDecodingContainer.swift b/Sources/CodableKit/CodableData/Decoder/CodableDataUnkeyedDecodingContainer.swift deleted file mode 100644 index 8f0ad6e8..00000000 --- a/Sources/CodableKit/CodableData/Decoder/CodableDataUnkeyedDecodingContainer.swift +++ /dev/null @@ -1,139 +0,0 @@ -/// Internal `UnkeyedDecodingContainer` for `CodableDataDecoder` -final class CodableDataUnkeyedDecodingContainer: UnkeyedDecodingContainer { - /// See `UnkeyedDecodingContainer.count` - var count: Int? - - /// See `UnkeyedDecodingContainer.isAtEnd` - var isAtEnd: Bool { - return currentIndex == count - } - - /// See `UnkeyedDecodingContainer.currentIndex` - var currentIndex: Int - - /// Creates a coding key for the current index, then increments the count. - var index: CodingKey { - defer { currentIndex += 1} - return CodableDataArrayKey(currentIndex) - } - - /// See `UnkeyedDecodingContainer.codingPath` - var codingPath: [CodingKey] - - /// Data being encoded. - let partialData: PartialCodableData - - /// Creates a new internal `CodableDataUnkeyedDecodingContainer`. - init(partialData: PartialCodableData, at path: [CodingKey]) { - self.codingPath = path - self.partialData = partialData - switch partialData.get(at: codingPath) { - case .some(let w): - switch w { - case .array(let a): count = a.count - default: count = nil - } - case .none: count = nil - } - currentIndex = 0 - } - - /// See `UnkeyedDecodingContainer.decodeNil` - func decodeNil() throws -> Bool { - return partialData.decodeNil(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: Bool.Type) throws -> Bool { - return try partialData.decodeBool(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: Int.Type) throws -> Int { - return try partialData.decodeFixedWidthInteger(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: Int8.Type) throws -> Int8 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: Int16.Type) throws -> Int16 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: Int32.Type) throws -> Int32 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: Int64.Type) throws -> Int64 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: UInt.Type) throws -> UInt { - return try partialData.decodeFixedWidthInteger(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: UInt8.Type) throws -> UInt8 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: UInt16.Type) throws -> UInt16 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: UInt32.Type) throws -> UInt32 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: UInt64.Type) throws -> UInt64 { - return try partialData.decodeFixedWidthInteger(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: Float.Type) throws -> Float { - return try partialData.decodeFloatingPoint(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: Double.Type) throws -> Double { - return try partialData.decodeFloatingPoint(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: String.Type) throws -> String { - return try partialData.decodeString(at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.decode` - func decode(_ type: T.Type) throws -> T where T: Decodable { - let decoder = _CodableDataDecoder(partialData: partialData, at: codingPath + [index]) - return try T(from: decoder) - } - - /// See `UnkeyedDecodingContainer.nestedContainer` - func nestedContainer(keyedBy type: Key.Type) throws -> KeyedDecodingContainer - where Key: CodingKey - { - let container = CodableDataKeyedDecodingContainer(partialData: partialData, at: codingPath + [index]) - return .init(container) - } - - /// See `UnkeyedDecodingContainer.nestedUnkeyedContainer` - func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { - return CodableDataUnkeyedDecodingContainer(partialData: partialData, at: codingPath + [index]) - } - - /// See `UnkeyedDecodingContainer.superDecoder` - func superDecoder() throws -> Decoder { - return _CodableDataDecoder(partialData: partialData, at: codingPath + [index]) - } -} diff --git a/Sources/CodableKit/CodableData/Encoder/CodableDataEncoder.swift b/Sources/CodableKit/CodableData/Encoder/CodableDataEncoder.swift deleted file mode 100644 index 0d2923ff..00000000 --- a/Sources/CodableKit/CodableData/Encoder/CodableDataEncoder.swift +++ /dev/null @@ -1,49 +0,0 @@ -/// Encodes `Encodable` items to `CodableData`. -public final class CodableDataEncoder { - /// Creates a new `CodableDataEncoder`. - public init() {} - - /// Encodes the supplied `Encodable` to `CodableData` - public func encode(_ encodable: Encodable) throws -> CodableData { - let data = PartialCodableData(data: .null) - let encoder = _CodableDataEncoder(partialData: data, at: []) - try encodable.encode(to: encoder) - return data.data - } -} - -/// Internal `Encoder` implementation for `CodableDataEncoder`. -internal final class _CodableDataEncoder: Encoder { - /// See `Encoder.codingPath` - var codingPath: [CodingKey] - - /// See `Encoder.codingPath` - var userInfo: [CodingUserInfoKey: Any] - - /// Data being encoded. - let partialData: PartialCodableData - - /// Creates a new internal `_CodableDataEncoder`. - init(partialData: PartialCodableData, at path: [CodingKey]) { - self.codingPath = path - self.userInfo = [:] - self.partialData = partialData - } - - /// See `Encoder.container` - func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey { - let container = CodableDataKeyedEncodingContainer(partialData: partialData, at: codingPath) - return .init(container) - } - - /// See `Encoder.unkeyedContainer` - func unkeyedContainer() -> UnkeyedEncodingContainer { - return CodableDataUnkeyedEncodingContainer(partialData: partialData, at: codingPath) - } - - /// See `Encoder.singleValueContainer` - func singleValueContainer() -> SingleValueEncodingContainer { - return CodableDataSingleValueEncodingContainer(partialData: partialData, at: codingPath) - } -} - diff --git a/Sources/CodableKit/CodableData/Encoder/CodableDataKeyedEncodingContainer.swift b/Sources/CodableKit/CodableData/Encoder/CodableDataKeyedEncodingContainer.swift deleted file mode 100644 index 04f17306..00000000 --- a/Sources/CodableKit/CodableData/Encoder/CodableDataKeyedEncodingContainer.swift +++ /dev/null @@ -1,120 +0,0 @@ -/// Internal `KeyedEncodingContainerProtocol` for `CodableDataEncoder`. -internal final class CodableDataKeyedEncodingContainer: KeyedEncodingContainerProtocol - where K: CodingKey -{ - /// See `KeyedEncodingContainerProtocol.codingPath` - var codingPath: [CodingKey] - - /// Data being encoded. - let partialData: PartialCodableData - - /// Creates a new `CodableDataKeyedEncodingContainer` - init(partialData: PartialCodableData, at path: [CodingKey]) { - self.codingPath = path - self.partialData = partialData - } - - /// See `KeyedEncodingContainerProtocol.encodeNil` - func encodeNil(forKey key: K) throws { - partialData.set(.null, at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: Bool, forKey key: K) throws { - partialData.set(.bool(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: Int, forKey key: K) throws { - partialData.set(.int(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: Int8, forKey key: K) throws { - partialData.set(.int8(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: Int16, forKey key: K) throws { - partialData.set(.int16(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: Int32, forKey key: K) throws { - partialData.set(.int32(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: Int64, forKey key: K) throws { - partialData.set(.int64(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: UInt, forKey key: K) throws { - partialData.set(.uint(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: UInt8, forKey key: K) throws { - partialData.set(.uint8(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: UInt16, forKey key: K) throws { - partialData.set(.uint16(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: UInt32, forKey key: K) throws { - partialData.set(.uint32(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: UInt64, forKey key: K) throws { - partialData.set(.uint64(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: Float, forKey key: K) throws { - partialData.set(.float(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: Double, forKey key: K) throws { - partialData.set(.double(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: String, forKey key: K) throws { - partialData.set(.string(value), at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.encode` - func encode(_ value: T, forKey key: K) throws where T : Encodable { - let encoder = _CodableDataEncoder(partialData: partialData, at: codingPath + [key]) - try value.encode(to: encoder) - } - - /// See `KeyedEncodingContainerProtocol.nestedContainer` - func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: K) -> KeyedEncodingContainer - where NestedKey: CodingKey - { - let container = CodableDataKeyedEncodingContainer(partialData: partialData, at: codingPath + [key]) - return .init(container) - } - - /// See `KeyedEncodingContainerProtocol.nestedUnkeyedContainer` - func nestedUnkeyedContainer(forKey key: K) -> UnkeyedEncodingContainer { - return CodableDataUnkeyedEncodingContainer(partialData: partialData, at: codingPath + [key]) - } - - /// See `KeyedEncodingContainerProtocol.superEncoder` - func superEncoder() -> Encoder { - return _CodableDataEncoder(partialData: partialData, at: codingPath) - } - - /// See `KeyedEncodingContainerProtocol.superEncoder` - func superEncoder(forKey key: K) -> Encoder { - return _CodableDataEncoder(partialData: partialData, at: codingPath + [key]) - } -} diff --git a/Sources/CodableKit/CodableData/Encoder/CodableDataSingleValueEncodingContainer.swift b/Sources/CodableKit/CodableData/Encoder/CodableDataSingleValueEncodingContainer.swift deleted file mode 100644 index c834ed4f..00000000 --- a/Sources/CodableKit/CodableData/Encoder/CodableDataSingleValueEncodingContainer.swift +++ /dev/null @@ -1,114 +0,0 @@ -/// Internal `SingleValueEncodingContainer` for `CodableDataEncoder`. -internal final class CodableDataSingleValueEncodingContainer: SingleValueEncodingContainer { - /// See `KeyedEncodingContainerProtocol.codingPath` - var codingPath: [CodingKey] - - /// Data being encoded. - let partialData: PartialCodableData - - /// Creates a new `CodableDataKeyedEncodingContainer` - init(partialData: PartialCodableData, at path: [CodingKey]) { - self.codingPath = path - self.partialData = partialData - } - - /// See `SingleValueEncodingContainer.encodeNil` - func encodeNil() throws { - partialData.set(.null, at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: Bool) throws { - partialData.set(.bool(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: Int) throws { - partialData.set(.int(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: Int8) throws { - partialData.set(.int8(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: Int16) throws { - partialData.set(.int16(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: Int32) throws { - partialData.set(.int32(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: Int64) throws { - partialData.set(.int64(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: UInt) throws { - partialData.set(.uint(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: UInt8) throws { - partialData.set(.uint8(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: UInt16) throws { - partialData.set(.uint16(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: UInt32) throws { - partialData.set(.uint32(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: UInt64) throws { - partialData.set(.uint64(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: Float) throws { - partialData.set(.float(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: Double) throws { - partialData.set(.double(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: String) throws { - partialData.set(.string(value), at: codingPath) - } - - /// See `SingleValueEncodingContainer.encode` - func encode(_ value: T) throws where T : Encodable { - let encoder = _CodableDataEncoder(partialData: partialData, at: codingPath) - try value.encode(to: encoder) - } - - /// See `SingleValueEncodingContainer.nestedContainer` - func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer - where NestedKey: CodingKey - { - let container = CodableDataKeyedEncodingContainer(partialData: partialData, at: codingPath) - return .init(container) - } - - /// See `SingleValueEncodingContainer.nestedSingleValueContainer` - func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - return CodableDataUnkeyedEncodingContainer(partialData: partialData, at: codingPath) - } - - /// See `SingleValueEncodingContainer.superEncoder` - func superEncoder() -> Encoder { - return _CodableDataEncoder(partialData: partialData, at: codingPath) - } -} - diff --git a/Sources/CodableKit/CodableData/Encoder/CodableDataUnkeyedEncodingContainer.swift b/Sources/CodableKit/CodableData/Encoder/CodableDataUnkeyedEncodingContainer.swift deleted file mode 100644 index 7354790f..00000000 --- a/Sources/CodableKit/CodableData/Encoder/CodableDataUnkeyedEncodingContainer.swift +++ /dev/null @@ -1,123 +0,0 @@ -/// Internal `UnkeyedEncodingContainer` for `CodableDataEncoder`. -internal final class CodableDataUnkeyedEncodingContainer: UnkeyedEncodingContainer { - /// See `UnkeyedEncodingContainer.count` - var count: Int - - /// See `KeyedEncodingContainerProtocol.codingPath` - var codingPath: [CodingKey] - - /// Data being encoded. - let partialData: PartialCodableData - - /// Creates a coding key for the current index, then increments the count. - var index: CodingKey { - defer { count += 1 } - return CodableDataArrayKey(count) - } - - /// Creates a new `CodableDataKeyedEncodingContainer` - init(partialData: PartialCodableData, at path: [CodingKey]) { - self.codingPath = path - self.partialData = partialData - self.count = 0 - } - - /// See `UnkeyedEncodingContainer.encodeNil` - func encodeNil() throws { - partialData.set(.null, at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: Bool) throws { - partialData.set(.bool(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: Int) throws { - partialData.set(.int(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: Int8) throws { - partialData.set(.int8(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: Int16) throws { - partialData.set(.int16(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: Int32) throws { - partialData.set(.int32(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: Int64) throws { - partialData.set(.int64(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: UInt) throws { - partialData.set(.uint(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: UInt8) throws { - partialData.set(.uint8(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: UInt16) throws { - partialData.set(.uint16(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: UInt32) throws { - partialData.set(.uint32(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: UInt64) throws { - partialData.set(.uint64(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: Float) throws { - partialData.set(.float(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: Double) throws { - partialData.set(.double(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: String) throws { - partialData.set(.string(value), at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.encode` - func encode(_ value: T) throws where T : Encodable { - let encoder = _CodableDataEncoder(partialData: partialData, at: codingPath + [index]) - try value.encode(to: encoder) - } - - /// See `UnkeyedEncodingContainer.nestedContainer` - func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer - where NestedKey: CodingKey - { - let container = CodableDataKeyedEncodingContainer(partialData: partialData, at: codingPath + [index]) - return .init(container) - } - - /// See `UnkeyedEncodingContainer.nestedUnkeyedContainer` - func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - return CodableDataUnkeyedEncodingContainer(partialData: partialData, at: codingPath + [index]) - } - - /// See `UnkeyedEncodingContainer.superEncoder` - func superEncoder() -> Encoder { - return _CodableDataEncoder(partialData: partialData, at: codingPath + [index]) - } -} diff --git a/Sources/CodableKit/CodableData/PartialCodableData.swift b/Sources/CodableKit/CodableData/PartialCodableData.swift deleted file mode 100644 index 17991d7d..00000000 --- a/Sources/CodableKit/CodableData/PartialCodableData.swift +++ /dev/null @@ -1,194 +0,0 @@ -/// Reference wrapper around `PartialCodableData` -public final class PartialCodableData { - /// The partial data. - var data: CodableData - - /// Creates a new `PartialCodableData`. - init(data: CodableData) { - self.data = data - } - - /// Returns the value, if one at from the given path. - func get(at path: [CodingKey]) -> CodableData? { - var child = data - for seg in path { - switch child { - case .array(let arr): - guard let index = seg.intValue, arr.count > index else { - return nil - } - child = arr[index] - case .dictionary(let dict): - guard let value = dict[seg.stringValue] else { - return nil - } - child = value - default: - return nil - } - } - return child - } - - - /// Sets the `CodableData` at supplied coding path. - func set(_ value: CodableData, at path: [CodingKey]) { - set(&self.data, to: value, at: path) - } - - /// Sets the mutable `CodableData` to supplied data at coding path. - private func set(_ context: inout CodableData, to value: CodableData, at path: [CodingKey]) { - guard path.count >= 1 else { - context = value - return - } - - let end = path[0] - - var child: CodableData? - switch path.count { - case 1: - child = value - case 2...: - if let index = end.intValue { - let array: [CodableData] - switch context { - case .array(let value): array = value - default: array = [] - } - if array.count > index { - child = array[index] - } else { - child = .array([]) - } - set(&child!, to: value, at: Array(path[1...])) - } else { - let dictionary: [String: CodableData] - switch context { - case .dictionary(let value): dictionary = value - default: dictionary = [:] - } - child = dictionary[end.stringValue] ?? .dictionary([:]) - set(&child!, to: value, at: Array(path[1...])) - } - default: break - } - - if let index = end.intValue { - if case .array(var arr) = context { - if arr.count > index { - arr[index] = child ?? .null - } else { - arr.append(child ?? .null) - } - context = .array(arr) - } else if let child = child { - context = .array([child]) - } - } else { - if case .dictionary(var dict) = context { - dict[end.stringValue] = child - context = .dictionary(dict) - } else if let child = child { - context = .dictionary([ - end.stringValue: child - ]) - } - } - } -} - -/// MARK: Decode - -extension PartialCodableData { - /// Gets a `nil` from the supplied path or throws a decoding error. - func decodeNil(at path: [CodingKey]) -> Bool { - if let value = get(at: path) { - return value == .null - } else { - return true - } - } - - /// Gets a `Bool` from the supplied path or throws a decoding error. - func decodeBool(at path: [CodingKey]) throws -> Bool { - switch try requireGet(Bool.self, at: path) { - case .bool(let value): return value - default: throw DecodingError.typeMismatch(Bool.self, .init(codingPath: path, debugDescription: "")) - } - } - - /// Gets a `String` from the supplied path or throws a decoding error. - func decodeString(at path: [CodingKey]) throws -> String { - switch try requireGet(String.self, at: path) { - case .string(let value): return value - default: throw DecodingError.typeMismatch(String.self, .init(codingPath: path, debugDescription: "")) - } - } - - /// Gets a `Float` from the supplied path or throws a decoding error. - func decodeFixedWidthInteger(_ type: I.Type = I.self, at path: [CodingKey]) throws -> I - where I: FixedWidthInteger, I: Decodable - { - switch try requireGet(I.self, at: path) { - case .int(let value): return try safeCast(value, at: path) - case .int8(let value): return try safeCast(value, at: path) - case .int16(let value): return try safeCast(value, at: path) - case .int32(let value): return try safeCast(value, at: path) - case .int64(let value): return try safeCast(value, at: path) - case .uint(let value): return try safeCast(value, at: path) - case .uint8(let value): return try safeCast(value, at: path) - case .uint16(let value): return try safeCast(value, at: path) - case .uint32(let value): return try safeCast(value, at: path) - case .uint64(let value): return try safeCast(value, at: path) - default: throw DecodingError.typeMismatch(type, .init(codingPath: path, debugDescription: "")) - } - } - - /// Gets a `FloatingPoint` from the supplied path or throws a decoding error. - func decodeFloatingPoint(_ type: F.Type = F.self, at path: [CodingKey]) throws -> F - where F: BinaryFloatingPoint, F: Decodable - { - switch try requireGet(F.self, at: path) { - case .int(let value): return F(value) - case .int8(let value): return F(value) - case .int16(let value): return F(value) - case .int32(let value): return F(value) - case .int64(let value): return F(value) - case .uint(let value): return F(value) - case .uint8(let value): return F(value) - case .uint16(let value): return F(value) - case .uint32(let value): return F(value) - case .uint64(let value): return F(value) - case .float(let float): return F(float) - case .double(let double): return F(double) - default: throw DecodingError.typeMismatch(F.self, .init(codingPath: path, debugDescription: "")) - } - } - - /// Gets a value at the supplied path or throws a decoding error. - func requireGet(_ type: T.Type, at path: [CodingKey]) throws -> CodableData { - switch get(at: path) { - case .some(let w): return w - case .none: throw DecodingError.valueNotFound(T.self, .init(codingPath: path, debugDescription: "")) - } - } - - /// Safely casts one `FixedWidthInteger` to another. - private func safeCast(_ value: V, at path: [CodingKey], to type: I.Type = I.self) throws -> I where V: FixedWidthInteger, I: FixedWidthInteger { - if let existing = value as? I { - return existing - } - - guard I.bitWidth >= V.bitWidth else { - throw DecodingError.typeMismatch(type, .init(codingPath: path, debugDescription: "Bit width too wide: \(I.bitWidth) < \(V.bitWidth)")) - } - guard value <= I.max else { - throw DecodingError.typeMismatch(type, .init(codingPath: path, debugDescription: "Value too large: \(value) > \(I.max)")) - } - guard value >= I.min else { - throw DecodingError.typeMismatch(type, .init(codingPath: path, debugDescription: "Value too small: \(value) < \(I.min)")) - } - return I(value) - } -} diff --git a/Sources/CodableKit/Key/Decodable+Properties.swift b/Sources/CodableKit/CodableProperty/Decodable+Properties.swift similarity index 71% rename from Sources/CodableKit/Key/Decodable+Properties.swift rename to Sources/CodableKit/CodableProperty/Decodable+Properties.swift index e7c1d7c0..91d1593c 100644 --- a/Sources/CodableKit/Key/Decodable+Properties.swift +++ b/Sources/CodableKit/CodableProperty/Decodable+Properties.swift @@ -1,8 +1,18 @@ +import Core + +/// Add free `CodingKeyPropertiesStaticRepresentable` conformance to `Decodable` types. +extension Reflectable where Self: Decodable { + /// See `CodingKeyPropertiesStaticRepresentable.properties(depth:)` + public static func reflectProperties(depth: Int) throws -> [ReflectedProperty] { + return try decodeProperties(depth: depth) + } +} + extension Decodable { /// Collect's the Decodable type's properties into an /// array of `CodingKeyProperty` using the `init(from: Decoder)` method. /// - parameter depth: Controls how deeply nested optional decoding will go. - public static func properties(depth: Int = 1) throws -> [CodingKeyProperty] { + public static func decodeProperties(depth: Int) throws -> [ReflectedProperty] { let result = CodingKeyCollectorResult(depth: depth) let decoder = CodingKeyCollector(codingPath: [], result: result) do { @@ -24,42 +34,27 @@ extension Decodable { } } -/// A property from a Decodable type. -public struct CodingKeyProperty { - /// The coding path to this property. - public let codingPath: [CodingKey] - - /// This property's type. - public let type: Any.Type - - /// True if the original property is optional. - public let isOptional: Bool -} - -extension CodingKeyProperty: CustomStringConvertible { - /// See CustomStringConvertible.description - public var description: String { - let path = codingPath.map { $0.stringValue }.joined(separator: ".") - return "\(path): \(type)\(isOptional ? "?" : "")" - } -} - -/// MARK: Private +/// MARK: Private - Decoders fileprivate final class CodingKeyCollectorResult { - var properties: [CodingKeyProperty] + var properties: [ReflectedProperty] var depth: Int - var isOptional: Bool + var nextIsOptional: Bool init(depth: Int) { self.depth = depth properties = [] - isOptional = false + self.nextIsOptional = false } - func add(type: Any.Type, atPath codingPath: [CodingKey]) { - let property = CodingKeyProperty(codingPath: codingPath, type: type, isOptional: isOptional) - isOptional = false + func add(type: T.Type, atPath codingPath: [CodingKey]) { + let property: ReflectedProperty + if nextIsOptional { + nextIsOptional = false + property = ReflectedProperty(T?.self, at: codingPath.map { $0.stringValue }) + } else { + property = ReflectedProperty(T.self, at: codingPath.map { $0.stringValue }) + } properties.append(property) } } @@ -105,26 +100,6 @@ fileprivate struct CodingKeyCollectorSingleValueDecoder: SingleValueDecodingCont return false } - func decode(_ type: Bool.Type) throws -> Bool { - result.add(type: type, atPath: codingPath) - return false - } - - func decode(_ type: Int.Type) throws -> Int { - result.add(type: type, atPath: codingPath) - return 0 - } - - func decode(_ type: Double.Type) throws -> Double { - result.add(type: type, atPath: codingPath) - return 0 - } - - func decode(_ type: String.Type) throws -> String { - result.add(type: type, atPath: codingPath) - return "0" - } - func decode(_ type: T.Type) throws -> T where T: Decodable { if let keyString = T.self as? AnyKeyStringDecodable.Type { result.add(type: type, atPath: codingPath) @@ -156,12 +131,12 @@ fileprivate struct CodingKeyCollectorKeyedDecoder: KeyedDecodingContainerProt } func contains(_ key: K) -> Bool { + result.nextIsOptional = true return true } func decodeNil(forKey key: K) throws -> Bool { if result.depth > codingPath.count { - result.isOptional = true return false } return true @@ -178,7 +153,7 @@ fileprivate struct CodingKeyCollectorKeyedDecoder: KeyedDecodingContainerProt } func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer { - fatalError() + return CodingKeyCollectorUnkeyedDecoder(codingPath: codingPath + [key], result: result) } func superDecoder() throws -> Decoder { @@ -189,29 +164,9 @@ fileprivate struct CodingKeyCollectorKeyedDecoder: KeyedDecodingContainerProt return CodingKeyCollector(codingPath: codingPath + [key], result: result) } - func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { - result.add(type: type, atPath: codingPath + [key]) - return false - } - - func decode(_ type: Int.Type, forKey key: K) throws -> Int { - result.add(type: type, atPath: codingPath + [key]) - return 0 - } - - func decode(_ type: Double.Type, forKey key: K) throws -> Double { - result.add(type: type, atPath: codingPath + [key]) - return 0 - } - - func decode(_ type: String.Type, forKey key: K) throws -> String { - result.add(type: type, atPath: codingPath + [key]) - return "0" - } - func decode(_ type: T.Type, forKey key: K) throws -> T where T: Decodable { if let keyString = T.self as? AnyKeyStringDecodable.Type { - result.add(type: type, atPath: codingPath + [key]) + result.add(type: T.self, atPath: codingPath + [key]) return keyString._keyStringFalse as! T } else { let path = codingPath + [key] @@ -247,26 +202,6 @@ fileprivate struct CodingKeyCollectorUnkeyedDecoder: UnkeyedDecodingContainer { return false } - func decode(_ type: Bool.Type) throws -> Bool { - result.add(type: [Bool].self, atPath: codingPath) - return false - } - - func decode(_ type: Int.Type) throws -> Int { - result.add(type: [Int].self, atPath: codingPath) - return 0 - } - - func decode(_ type: Double.Type) throws -> Double { - result.add(type: [Double].self, atPath: codingPath) - return 0 - } - - func decode(_ type: String.Type) throws -> String { - result.add(type: [String].self, atPath: codingPath) - return "0" - } - func decode(_ type: T.Type) throws -> T where T: Decodable { if let keyString = T.self as? AnyKeyStringDecodable.Type { result.add(type: [T].self, atPath: codingPath) @@ -296,5 +231,4 @@ fileprivate struct CodingKeyCollectorUnkeyedDecoder: UnkeyedDecodingContainer { mutating func superDecoder() throws -> Decoder { return CodingKeyCollector(codingPath: codingPath, result: result) } - } diff --git a/Sources/CodableKit/Key/Decodable+CodingPath.swift b/Sources/CodableKit/CodableProperty/Decodable+Property.swift similarity index 75% rename from Sources/CodableKit/Key/Decodable+CodingPath.swift rename to Sources/CodableKit/CodableProperty/Decodable+Property.swift index f796b82d..26756d28 100644 --- a/Sources/CodableKit/Key/Decodable+CodingPath.swift +++ b/Sources/CodableKit/CodableProperty/Decodable+Property.swift @@ -1,11 +1,16 @@ -import Foundation +import Core /// Maps KeyPath to [CodingKey] on Decodable types. +extension Reflectable where Self: Decodable { + /// See `CodableProperties.property(forKey:)` + public static func reflectProperty(forKey keyPath: KeyPath) throws -> ReflectedProperty { + return try decodeProperty(forKey: keyPath) + } +} + extension Decodable { - /// Returns the Decodable coding path `[CodingKey]` for the supplied key path. - /// Note: Attempting to resolve a keyPath for non-decoded key paths (i.e., count, etc) - /// will result in a fatalError. - public static func codingPath(forKey keyPath: KeyPath) throws -> [CodingKey] where T: KeyStringDecodable { + /// Automatically decodes a `CodableProperty` for the supplied `KeyPath`. + public static func decodeProperty(forKey keyPath: KeyPath) throws -> ReflectedProperty { var depth = 0 a: while true { defer { depth += 1 } @@ -50,8 +55,12 @@ extension Decodable { break b } - if T.keyStringIsTrue(decoded[keyPath: keyPath]) { - return codingPath + guard let t = T.self as? AnyKeyStringDecodable.Type else { + break b + } + + if t._keyStringIsTrue(decoded[keyPath: keyPath]) { + return .init(T.self, at: codingPath.map { $0.stringValue }) } } } @@ -118,38 +127,6 @@ fileprivate struct KeyStringSingleValueDecoder: SingleValueDecodingContainer { return false } - func decode(_ type: Bool.Type) throws -> Bool { - if result.cycle { - result.codingPath = codingPath - return true - } - return false - } - - func decode(_ type: Int.Type) throws -> Int { - if result.cycle { - result.codingPath = codingPath - return 1 - } - return 0 - } - - func decode(_ type: Double.Type) throws -> Double { - if result.cycle { - result.codingPath = codingPath - return 1 - } - return 0 - } - - func decode(_ type: String.Type) throws -> String { - if result.cycle { - result.codingPath = codingPath - return "1" - } - return "0" - } - func decode(_ type: T.Type) throws -> T where T: Decodable { if let type = T.self as? AnyKeyStringDecodable.Type { if result.cycle { @@ -195,7 +172,7 @@ fileprivate struct KeyStringKeyedDecoder: KeyedDecodingContainerProtocol wher } func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer { - fatalError() + return KeyStringUnkeyedDecoder(codingPath: codingPath + [key], result: result) } func superDecoder() throws -> Decoder { @@ -206,38 +183,6 @@ fileprivate struct KeyStringKeyedDecoder: KeyedDecodingContainerProtocol wher return KeyStringDecoder(codingPath: codingPath + [key], result: result) } - func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { - if result.cycle { - result.codingPath = codingPath + [key] - return true - } - return false - } - - func decode(_ type: Int.Type, forKey key: K) throws -> Int { - if result.cycle { - result.codingPath = codingPath + [key] - return 1 - } - return 0 - } - - func decode(_ type: Double.Type, forKey key: K) throws -> Double { - if result.cycle { - result.codingPath = codingPath + [key] - return 1 - } - return 0 - } - - func decode(_ type: String.Type, forKey key: K) throws -> String { - if result.cycle { - result.codingPath = codingPath + [key] - return "1" - } - return "0" - } - func decode(_ type: T.Type, forKey key: K) throws -> T where T : Decodable { if let type = T.self as? AnyKeyStringDecodable.Type { if result.cycle { @@ -278,26 +223,6 @@ fileprivate struct KeyStringUnkeyedDecoder: UnkeyedDecodingContainer { return true } - mutating func decode(_ type: Bool.Type) throws -> Bool { - isAtEnd = true - return true - } - - mutating func decode(_ type: Int.Type) throws -> Int { - isAtEnd = true - return 1 - } - - mutating func decode(_ type: Double.Type) throws -> Double { - isAtEnd = true - return 1.0 - } - - mutating func decode(_ type: String.Type) throws -> String { - isAtEnd = true - return "1" - } - mutating func decode(_ type: T.Type) throws -> T where T : Decodable { isAtEnd = true if let type = T.self as? AnyKeyStringDecodable.Type { diff --git a/Sources/CodableKit/CodableProperty/Deprecated.swift b/Sources/CodableKit/CodableProperty/Deprecated.swift new file mode 100644 index 00000000..481f5148 --- /dev/null +++ b/Sources/CodableKit/CodableProperty/Deprecated.swift @@ -0,0 +1,49 @@ +import Core + +extension Decodable { + @available(*, deprecated, renamed: "property(forKey:)") + public static func codingPath(forKey keyPath: KeyPath) throws -> [CodingKey] { + return try decodeProperty(forKey: keyPath).path.map { BasicKey($0) } + } + + @available(*, deprecated, renamed: "properties(depth:)") + public static func properties() throws -> [CodingKeyProperty] { + return try decodeProperties(depth: 1).map { .init($0) } + } +} + +@available(*, deprecated, renamed: "CodableProperty") +public struct CodingKeyProperty { + public var type: Any.Type { + if let o = _p.type as? AnyOptionalType.Type { + return o.anyWrappedType + } else { + return _p.type + } + } + + public var isOptional: Bool { + return _p.type is AnyOptionalType.Type + } + + public var codingPath: [CodingKey] { + return _p.path.map { BasicKey($0) } + } + + private let _p: ReflectedProperty + + init(_ p: ReflectedProperty) { + _p = p + } +} + +@available(*, deprecated, renamed: "LosslessStringConvertible") +public protocol StringDecodable: LosslessStringConvertible { + static func decode(from: String) -> Self? +} + +extension UUID: LosslessStringConvertible { + public init?(_ string: String) { + self.init(uuidString: string) + } +} diff --git a/Sources/CodableKit/Key/KeyStringDecodable.swift b/Sources/CodableKit/CodableProperty/KeyStringDecodable.swift similarity index 95% rename from Sources/CodableKit/Key/KeyStringDecodable.swift rename to Sources/CodableKit/CodableProperty/KeyStringDecodable.swift index da85f01a..e9dc4e8a 100644 --- a/Sources/CodableKit/Key/KeyStringDecodable.swift +++ b/Sources/CodableKit/CodableProperty/KeyStringDecodable.swift @@ -127,6 +127,16 @@ extension Optional: KeyStringDecodable, AnyKeyStringDecodable { } } +extension URL: KeyStringDecodable { + public static var keyStringTrue: URL { + return URL(string: "true")! + } + + public static var keyStringFalse: URL { + return URL(string: "false")! + } +} + func requireKeyStringDecodable(_ type: T.Type) -> AnyKeyStringDecodable.Type { guard let type = T.self as? AnyKeyStringDecodable.Type else { fatalError("\(T.self) does not conform to `KeyStringDecodable`.") diff --git a/Sources/CodableKit/DecoderExtensions.swift b/Sources/CodableKit/DecoderExtensions.swift deleted file mode 100644 index 5c0a9817..00000000 --- a/Sources/CodableKit/DecoderExtensions.swift +++ /dev/null @@ -1,133 +0,0 @@ -// MARK: Single - -extension SingleValueDecodingContainer { - public mutating func decode(_ type: Int8.Type) throws -> Int8 { - return try Int8(decode(Int.self)) - } - - public mutating func decode(_ type: Int16.Type) throws -> Int16 { - return try Int16(decode(Int.self)) - } - - public mutating func decode(_ type: Int32.Type) throws -> Int32 { - return try Int32(decode(Int.self)) - } - - public mutating func decode(_ type: Int64.Type) throws -> Int64 { - return try Int64(decode(Int.self)) - } - - public mutating func decode(_ type: UInt8.Type) throws -> UInt8 { - return try UInt8(decode(UInt.self)) - } - - public mutating func decode(_ type: UInt16.Type) throws -> UInt16 { - return try UInt16(decode(UInt.self)) - } - - public mutating func decode(_ type: UInt32.Type) throws -> UInt32 { - return try UInt32(decode(UInt.self)) - } - - public mutating func decode(_ type: UInt64.Type) throws -> UInt64 { - return try UInt64(decode(UInt.self)) - } - - public mutating func decode(_ type: UInt.Type) throws -> UInt { - return try UInt(decode(Int.self)) - } - - public mutating func decode(_ type: Float.Type) throws -> Float { - return try Float(decode(Double.self)) - } -} - -// MARK: Keyed - -extension KeyedDecodingContainerProtocol { - public mutating func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 { - return try .init(decode(Int.self, forKey: key)) - } - - public mutating func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 { - return try .init(decode(Int.self, forKey: key)) - } - - public mutating func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { - return try .init(decode(Int.self, forKey: key)) - } - - public mutating func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { - return try .init(decode(Int.self, forKey: key)) - } - - public mutating func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt { - return try .init(decode(Int.self, forKey: key)) - } - - public mutating func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 { - return try .init(decode(Int.self, forKey: key)) - } - - public mutating func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 { - return try .init(decode(Int.self, forKey: key)) - } - - public mutating func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 { - return try .init(decode(Int.self, forKey: key)) - } - - public mutating func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 { - return try .init(decode(Int.self, forKey: key)) - } - - public mutating func decode(_ type: Float.Type, forKey key: Key) throws -> Float { - return try .init(decode(Double.self, forKey: key)) - } -} - -// MARK: Unkeyed - -// MARK: Single - -extension UnkeyedDecodingContainer { - public mutating func decode(_ type: Int8.Type) throws -> Int8 { - return try Int8(decode(Int.self)) - } - - public mutating func decode(_ type: Int16.Type) throws -> Int16 { - return try Int16(decode(Int.self)) - } - - public mutating func decode(_ type: Int32.Type) throws -> Int32 { - return try Int32(decode(Int.self)) - } - - public mutating func decode(_ type: Int64.Type) throws -> Int64 { - return try Int64(decode(Int.self)) - } - - public mutating func decode(_ type: UInt8.Type) throws -> UInt8 { - return try UInt8(decode(UInt.self)) - } - - public mutating func decode(_ type: UInt16.Type) throws -> UInt16 { - return try UInt16(decode(UInt.self)) - } - - public mutating func decode(_ type: UInt32.Type) throws -> UInt32 { - return try UInt32(decode(UInt.self)) - } - - public mutating func decode(_ type: UInt64.Type) throws -> UInt64 { - return try UInt64(decode(UInt.self)) - } - - public mutating func decode(_ type: UInt.Type) throws -> UInt { - return try UInt(decode(Int.self)) - } - - public mutating func decode(_ type: Float.Type) throws -> Float { - return try Float(decode(Double.self)) - } -} diff --git a/Sources/CodableKit/DecoderHelper.swift b/Sources/CodableKit/DecoderHelper.swift deleted file mode 100644 index 1362d069..00000000 --- a/Sources/CodableKit/DecoderHelper.swift +++ /dev/null @@ -1,799 +0,0 @@ -public protocol DecoderHelper : Decoder { - associatedtype Value - associatedtype Keyed - associatedtype Unkeyed - - var either: Either { get } - - var lossyIntegers: Bool { get } - var lossyStrings: Bool { get } - - func decode(_ wrapped: Value) throws -> String - func decode(_ wrapped: Value) throws -> Bool - func decode(_ wrapped: Value) throws -> Int8 - func decode(_ wrapped: Value) throws -> Int16 - func decode(_ wrapped: Value) throws -> Int32 - func decode(_ wrapped: Value) throws -> Int64 - func decode(_ wrapped: Value) throws -> Int - func decode(_ wrapped: Value) throws -> UInt8 - func decode(_ wrapped: Value) throws -> UInt16 - func decode(_ wrapped: Value) throws -> UInt32 - func decode(_ wrapped: Value) throws -> UInt64 - func decode(_ wrapped: Value) throws -> UInt - func decode(_ wrapped: Value) throws -> Double - func decode(_ wrapped: Value) throws -> Float - func decode(_ type: D.Type, from wrapped: Value) throws -> D - - func integers(for value: Value) throws -> Integers? - - init(keyed: Keyed, lossyIntegers: Bool, lossyStrings: Bool) throws - init(value: Value, lossyIntegers: Bool, lossyStrings: Bool) throws - init(unkeyed: Unkeyed, lossyIntegers: Bool, lossyStrings: Bool) throws - init(any: Value, lossyIntegers: Bool, lossyStrings: Bool) throws -} - -public enum Either { - case keyed(Keyed) - case unkeyed(Unkeyed) - case value(Value) - - public func getValue() throws -> Value { - guard case .value(let value) = self else { - throw CodableDecodingError.invalidContext - } - - return value - } - - public func getKeyed() throws -> Keyed { - guard case .keyed(let keyed) = self else { - throw CodableDecodingError.invalidContext - } - - return keyed - } -} - -public enum Integers { - case uint(UInt) - case int(Int) - case uint64(UInt64) - case int64(Int64) - case uint32(UInt32) - case int32(Int32) - case uint16(UInt16) - case int16(Int16) - case uint8(UInt8) - case int8(Int8) - case double(Double) - case float(Float) - - fileprivate var signedNumber: Int64? { - switch self { - case .int(let s): return numericCast(s) - case .int8(let s): return numericCast(s) - case .int16(let s): return numericCast(s) - case .int32(let s): return numericCast(s) - case .int64(let s): return numericCast(s) - default: return nil - } - } - - fileprivate var unsignedNumber: UInt64? { - switch self { - case .uint(let u): return numericCast(u) - case .uint8(let u): return numericCast(u) - case .uint16(let u): return numericCast(u) - case .uint32(let u): return numericCast(u) - case .uint64(let u): return numericCast(u) - default: return nil - } - } -} - -extension DecoderHelper { - public func decode(_ wrapped: Value) throws -> Int8 { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .int8(let int) = integers else { - throw CodableDecodingError.incorrectValue - } - - return int - } - } - - throw CodableDecodingError.unimplemented - } - - public func decode(_ wrapped: Value) throws -> Int16 { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .int16(let int) = integers else { - throw CodableDecodingError.incorrectValue - } - - return int - } - } - - throw CodableDecodingError.unimplemented - } - public func decode(_ wrapped: Value) throws -> Int32 { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .int32(let int) = integers else { - throw CodableDecodingError.incorrectValue - } - - return int - } - } - - throw CodableDecodingError.unimplemented - } - public func decode(_ wrapped: Value) throws -> Int64 { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .int64(let int) = integers else { - throw CodableDecodingError.incorrectValue - } - - return int - } - } - - throw CodableDecodingError.unimplemented - } - public func decode(_ wrapped: Value) throws -> Int { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .int(let int) = integers else { - throw CodableDecodingError.incorrectValue - } - - return int - } - } - - throw CodableDecodingError.unimplemented - } - public func decode(_ wrapped: Value) throws -> UInt8 { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .uint8(let int) = integers else { - throw CodableDecodingError.incorrectValue - } - - return int - } - } - - throw CodableDecodingError.unimplemented - } - public func decode(_ wrapped: Value) throws -> UInt16 { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .uint16(let int) = integers else { - throw CodableDecodingError.incorrectValue - } - - return int - } - } - - throw CodableDecodingError.unimplemented - } - public func decode(_ wrapped: Value) throws -> UInt32 { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .uint32(let int) = integers else { - throw CodableDecodingError.incorrectValue - } - - return int - } - } - - throw CodableDecodingError.unimplemented - } - public func decode(_ wrapped: Value) throws -> UInt64 { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .uint64(let int) = integers else { - throw CodableDecodingError.incorrectValue - } - - return int - } - } - - throw CodableDecodingError.unimplemented - } - public func decode(_ wrapped: Value) throws -> UInt { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .uint(let int) = integers else { - throw CodableDecodingError.incorrectValue - } - - return int - } - } - - throw CodableDecodingError.unimplemented - } - public func decode(_ wrapped: Value) throws -> Double { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .double(let double) = integers else { - throw CodableDecodingError.incorrectValue - } - - return double - } - } - - throw CodableDecodingError.unimplemented - } - public func decode(_ wrapped: Value) throws -> Float { - if let integers = try integers(for: wrapped) { - if lossyIntegers { - return try decodeLossy(integers) - } else { - guard case .float(let float) = integers else { - throw CodableDecodingError.incorrectValue - } - - return float - } - } - - throw CodableDecodingError.unimplemented - } - - // MARK - Integer unwrap helper - - func unwrap(_ wrapped: Value) throws -> Int8 { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - func unwrap(_ wrapped: Value) throws -> Int16 { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - func unwrap(_ wrapped: Value) throws -> Int32 { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - func unwrap(_ wrapped: Value) throws -> Int64 { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - func unwrap(_ wrapped: Value) throws -> Int { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - func unwrap(_ wrapped: Value) throws -> UInt8 { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - func unwrap(_ wrapped: Value) throws -> UInt16 { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - func unwrap(_ wrapped: Value) throws -> UInt32 { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - func unwrap(_ wrapped: Value) throws -> UInt64 { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - func unwrap(_ wrapped: Value) throws -> UInt { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - func unwrap(_ wrapped: Value) throws -> Float { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - func unwrap(_ wrapped: Value) throws -> Double { - if lossyIntegers, let integers = try integers(for: wrapped) { - return try decodeLossy(integers) - } - - return try self.decode(wrapped) - } - - // MARK - Lossy integer decoder - - public func decodeLossy(_ wrapped: Integers) throws -> Int8 { - if let num = wrapped.signedNumber { - guard num <= numericCast(Int8.max), num >= numericCast(Int8.min) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else if let num = wrapped.unsignedNumber { - guard num <= numericCast(Int8.max) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } - - public func decodeLossy(_ wrapped: Integers) throws -> Int16 { - if let num = wrapped.signedNumber { - guard num <= numericCast(Int16.max), num >= numericCast(Int16.min) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else if let num = wrapped.unsignedNumber { - guard num <= numericCast(Int16.max) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } - - public func decodeLossy(_ wrapped: Integers) throws -> Int32 { - if let num = wrapped.signedNumber { - guard num <= numericCast(Int32.max), num >= numericCast(Int32.min) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else if let num = wrapped.unsignedNumber { - guard num <= numericCast(Int32.max) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } - - public func decodeLossy(_ wrapped: Integers) throws -> Int64 { - if let num = wrapped.signedNumber { - guard num <= numericCast(Int64.max), num >= numericCast(Int64.min) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else if let num = wrapped.unsignedNumber { - guard num <= numericCast(Int64.max) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } - - public func decodeLossy(_ wrapped: Integers) throws -> Int { - if let num = wrapped.signedNumber { - guard num <= numericCast(Int.max), num >= numericCast(Int.min) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else if let num = wrapped.unsignedNumber { - guard num <= numericCast(Int.max) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } - - public func decodeLossy(_ wrapped: Integers) throws -> UInt8 { - if let num = wrapped.signedNumber { - guard num <= numericCast(UInt8.max), num >= numericCast(UInt8.min) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else if let num = wrapped.unsignedNumber { - guard num <= numericCast(UInt8.max) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } - - public func decodeLossy(_ wrapped: Integers) throws -> UInt16 { - if let num = wrapped.signedNumber { - guard num <= numericCast(UInt16.max), num >= numericCast(UInt16.min) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else if let num = wrapped.unsignedNumber { - guard num <= numericCast(UInt16.max) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } - - public func decodeLossy(_ wrapped: Integers) throws -> UInt32 { - if let num = wrapped.signedNumber { - guard num <= numericCast(UInt32.max), num >= numericCast(UInt32.min) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else if let num = wrapped.unsignedNumber { - guard num <= numericCast(UInt32.max) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } - - public func decodeLossy(_ wrapped: Integers) throws -> UInt64 { - if let num = wrapped.signedNumber { - guard num <= numericCast(UInt64.max), num >= numericCast(UInt64.min) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else if let num = wrapped.unsignedNumber { - guard num <= numericCast(UInt64.max) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } - - public func decodeLossy(_ wrapped: Integers) throws -> UInt { - if let num = wrapped.signedNumber { - guard num <= numericCast(UInt.max), num >= numericCast(UInt.min) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else if let num = wrapped.unsignedNumber { - guard num <= numericCast(UInt.max) else { - throw CodableDecodingError.failedLossyIntegerConversion - } - - return numericCast(num) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } - - public func decodeLossy(_ wrapped: Integers) throws -> Double { - if case .double(let double) = wrapped { - return double - } else if case .float(let float) = wrapped { - return Double(float) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } - - public func decodeLossy(_ wrapped: Integers) throws -> Float { - if case .float(let float) = wrapped { - return float - } else if case .double(let double) = wrapped { - return Float(double) - } else { - throw CodableDecodingError.failedLossyIntegerConversion - } - } -} - -public protocol KeyedDecodingContainerProtocolHelper : KeyedDecodingContainerProtocol { - associatedtype D: DecoderHelper - - var decoder: D { get } - - init(decoder: D) -} - -public protocol KeyedDecodingHelper { - associatedtype Value - - func value(forKey key: String) throws -> Value? -} - -extension KeyedDecodingContainerProtocolHelper where D.Keyed : KeyedDecodingHelper, D.Keyed.Value == D.Value { - public func value(forKey key: Key) throws -> D.Value? { - return try decoder.either.getKeyed().value(forKey: key.stringValue) - } - - public func decode(_ type: Int.Type, forKey key: Key) throws -> Int { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: Float.Type, forKey key: Key) throws -> Float { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: Double.Type, forKey key: Key) throws -> Double { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.unwrap(value) - } - - public func decode(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable { - guard let value = try decoder.either.getKeyed().value(forKey: key.stringValue) else { - throw CodableDecodingError.incorrectValue - } - - return try decoder.decode(T.self, from: value) - } - - public func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer where NestedKey : CodingKey { - guard let keyed = try decoder.either.getKeyed().value(forKey: key.stringValue) as? D.Keyed else { - throw CodableDecodingError.incorrectValue - } - - return try D(keyed: keyed, lossyIntegers: decoder.lossyIntegers, lossyStrings: decoder.lossyStrings).container(keyedBy: type) - } - - public func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { - guard let unkeyed = try decoder.either.getKeyed().value(forKey: key.stringValue) as? D.Unkeyed else { - throw CodableDecodingError.incorrectValue - } - - return try D(unkeyed: unkeyed, lossyIntegers: decoder.lossyIntegers, lossyStrings: decoder.lossyStrings).unkeyedContainer() - } - - public func superDecoder() throws -> Decoder { - guard let value = try decoder.either.getKeyed().value(forKey: "super") else { - throw CodableDecodingError.incorrectValue - } - - return try D(any: value, lossyIntegers: decoder.lossyIntegers, lossyStrings: decoder.lossyStrings) - } - - public func superDecoder(forKey key: Key) throws -> Decoder { - guard let keyed = try decoder.either.getKeyed().value(forKey: key.stringValue) as? D.Keyed else { - throw CodableDecodingError.incorrectValue - } - - return try D(keyed: keyed, lossyIntegers: decoder.lossyIntegers, lossyStrings: decoder.lossyStrings) - } -} - -public protocol SingleValueDecodingContainerHelper : SingleValueDecodingContainer { - associatedtype D: DecoderHelper - - var decoder: D { get } - - init(decoder: D) -} - -extension SingleValueDecodingContainerHelper { - public func decode(_ type: Int.Type) throws -> Int { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: Int8.Type) throws -> Int8 { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: Int16.Type) throws -> Int16 { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: Int32.Type) throws -> Int32 { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: Int64.Type) throws -> Int64 { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: UInt.Type) throws -> UInt { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: UInt8.Type) throws -> UInt8 { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: UInt16.Type) throws -> UInt16 { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: UInt32.Type) throws -> UInt32 { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: UInt64.Type) throws -> UInt64 { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: Float.Type) throws -> Float { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: Double.Type) throws -> Double { - return try decoder.unwrap(try decoder.either.getValue()) - } - - public func decode(_ type: Bool.Type) throws -> Bool { - return try decoder.decode(try decoder.either.getValue()) - } -} - -public enum CodableDecodingError : Error { - case failedLossyIntegerConversion, invalidContext, incorrectValue, unimplemented -} - diff --git a/Sources/CodableKit/EncoderExtensions.swift b/Sources/CodableKit/EncoderExtensions.swift deleted file mode 100644 index 29cee33e..00000000 --- a/Sources/CodableKit/EncoderExtensions.swift +++ /dev/null @@ -1,131 +0,0 @@ -// MARK: Single - -extension SingleValueEncodingContainer { - public mutating func encode(_ value: Int8) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: Int16) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: Int32) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: Int64) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: UInt) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: UInt8) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: UInt16) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: UInt32) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: UInt64) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: Float) throws { - try encode(Double(value)) - } -} - -// MARK: Keyed - -extension KeyedEncodingContainerProtocol { - public mutating func encode(_ value: Int8, forKey key: Key) throws { - try encode(Int(value), forKey: key) - } - - public mutating func encode(_ value: Int16, forKey key: Key) throws { - try encode(Int(value), forKey: key) - } - - public mutating func encode(_ value: Int32, forKey key: Key) throws { - try encode(Int(value), forKey: key) - } - - public mutating func encode(_ value: Int64, forKey key: Key) throws { - try encode(Int(value), forKey: key) - } - - public mutating func encode(_ value: UInt, forKey key: Key) throws { - try encode(Int(value), forKey: key) - } - - public mutating func encode(_ value: UInt8, forKey key: Key) throws { - try encode(Int(value), forKey: key) - } - - public mutating func encode(_ value: UInt16, forKey key: Key) throws { - try encode(Int(value), forKey: key) - } - - public mutating func encode(_ value: UInt32, forKey key: Key) throws { - try encode(Int(value), forKey: key) - } - - public mutating func encode(_ value: UInt64, forKey key: Key) throws { - try encode(Int(value), forKey: key) - } - - public mutating func encode(_ value: Float, forKey key: Key) throws { - try encode(Double(value), forKey: key) - } -} - -// MARK: Unkeyed - -extension UnkeyedEncodingContainer { - public mutating func encode(_ value: Int8) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: Int16) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: Int32) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: Int64) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: UInt) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: UInt8) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: UInt16) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: UInt32) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: UInt64) throws { - try encode(Int(value)) - } - - public mutating func encode(_ value: Float) throws { - try encode(Double(value)) - } -} diff --git a/Sources/CodableKit/File.swift b/Sources/CodableKit/File.swift deleted file mode 100644 index 3ed82689..00000000 --- a/Sources/CodableKit/File.swift +++ /dev/null @@ -1,40 +0,0 @@ -import Debugging - -/// Errors that can be thrown while working with TCP sockets. -public struct CodableError: Debuggable { - public static let readableName = "Codable Error" - public let identifier: String - public var reason: String - public var file: String - public var function: String - public var line: UInt - public var column: UInt - public var stackTrace: [String] - public var possibleCauses: [String] - public var suggestedFixes: [String] - - /// Create a new TCP error. - public init( - identifier: String, - reason: String, - possibleCauses: [String] = [], - suggestedFixes: [String] = [], - file: String = #file, - function: String = #function, - line: UInt = #line, - column: UInt = #column - ) { - self.identifier = identifier - self.reason = reason - self.file = file - self.function = function - self.line = line - self.column = column - self.stackTrace = CodableError.makeStackTrace() - self.possibleCauses = possibleCauses - self.suggestedFixes = suggestedFixes - } -} - - - diff --git a/Sources/CodableKit/KeyPreEncoder.swift b/Sources/CodableKit/KeyPreEncoder.swift deleted file mode 100644 index 58331ea9..00000000 --- a/Sources/CodableKit/KeyPreEncoder.swift +++ /dev/null @@ -1,131 +0,0 @@ -public final class CodingPathKeyPreEncoder { - public init() {} - - var nested = false - - public func keys(for encodable: Encodable) throws -> [[String]] { - let encoder = _KeyPreEncoder(nested: nested) - try encodable.encode(to: encoder) - - return encoder.keys - } -} - -fileprivate final class _KeyPreEncoder: Encoder { - fileprivate var userInfo = [CodingUserInfoKey : Any]() - fileprivate var codingPath = [CodingKey]() - fileprivate var nested: Bool - - var keys = [[String]]() - - init(nested: Bool) { - self.nested = nested - keys.reserveCapacity(32) - } - - fileprivate func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey { - return KeyedEncodingContainer(KeyPathKeyedEncodingContainer(codingPath: codingPath, encoder: self)) - } - - fileprivate func unkeyedContainer() -> UnkeyedEncodingContainer { - return KeyPathUnkeyedEncodingContainer(count: 0, codingPath: codingPath, encoder: self) - } - - fileprivate func singleValueContainer() -> SingleValueEncodingContainer { - return KeyPathSingleValueEncodingContainer(codingPath: self.codingPath) - } -} - -fileprivate struct KeyPathSingleValueEncodingContainer: SingleValueEncodingContainer { - var codingPath: [CodingKey] - - mutating func encodeNil() throws {} - mutating func encode(_ value: Int) throws {} - mutating func encode(_ value: Bool) throws {} - mutating func encode(_ value: T) throws where T : Encodable {} - mutating func encode(_ value: String) throws {} - mutating func encode(_ value: Double) throws {} -} - -fileprivate struct KeyPathKeyedEncodingContainer: KeyedEncodingContainerProtocol { - var codingPath: [CodingKey] - var stringPath: [String] - var encoder: _KeyPreEncoder - - init(codingPath: [CodingKey], encoder: _KeyPreEncoder) { - self.codingPath = codingPath - self.encoder = encoder - - self.stringPath = codingPath.map { $0.stringValue } - } - - mutating func encodeNil(forKey key: K) throws { - encoder.keys.append(stringPath + [key.stringValue]) - } - - mutating func encode(_ value: T, forKey key: K) throws where T : Encodable { - encoder.keys.append(stringPath + [key.stringValue]) - } - - mutating func encode(_ value: String, forKey key: K) throws { - encoder.keys.append(stringPath + [key.stringValue]) - } - - mutating func encode(_ value: Double, forKey key: K) throws { - encoder.keys.append(stringPath + [key.stringValue]) - } - - mutating func encode(_ value: Int, forKey key: K) throws { - encoder.keys.append(stringPath + [key.stringValue]) - } - - mutating func encode(_ value: Bool, forKey key: K) throws { - encoder.keys.append(stringPath + [key.stringValue]) - } - - mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: K) -> KeyedEncodingContainer where NestedKey : CodingKey { - encoder.keys.append(stringPath + [key.stringValue]) - return KeyedEncodingContainer(KeyPathKeyedEncodingContainer(codingPath: codingPath, encoder: encoder)) - } - - mutating func nestedUnkeyedContainer(forKey key: K) -> UnkeyedEncodingContainer { - encoder.keys.append(stringPath + [key.stringValue]) - return KeyPathUnkeyedEncodingContainer(count: 0, codingPath: codingPath, encoder: encoder) - } - - mutating func superEncoder() -> Encoder { - return encoder - } - - mutating func superEncoder(forKey key: K) -> Encoder { - return encoder - } - - typealias Key = K -} - -fileprivate struct KeyPathUnkeyedEncodingContainer: UnkeyedEncodingContainer { - var count: Int = 0 - - var codingPath: [CodingKey] - var encoder: _KeyPreEncoder - - mutating func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey { - return KeyedEncodingContainer(KeyPathKeyedEncodingContainer(codingPath: codingPath, encoder: encoder)) - } - - mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - return KeyPathUnkeyedEncodingContainer(count: count, codingPath: codingPath, encoder: encoder) - } - - mutating func superEncoder() -> Encoder { - return encoder - } - - mutating func encodeNil() throws {} - mutating func encode(_ value: Int) throws {} - mutating func encode(_ value: Bool) throws {} - mutating func encode(_ value: T) throws where T : Encodable {} - mutating func encode(_ value: String) throws {} - mutating func encode(_ value: Double) throws {} -} diff --git a/Sources/CodableKit/UnsupportedEncodingContainer.swift b/Sources/CodableKit/UnsupportedEncodingContainer.swift deleted file mode 100644 index a160194e..00000000 --- a/Sources/CodableKit/UnsupportedEncodingContainer.swift +++ /dev/null @@ -1,182 +0,0 @@ -public struct UnsupportedEncodingError: Error {} - -public final class UnsupportedEncodingContainer { - let encoder: Encoder - public init(encoder: Encoder) { - self.encoder = encoder - } -} - -// MARK: Single - -extension UnsupportedEncodingContainer: SingleValueEncodingContainer { - public var codingPath: [CodingKey] { - return [] - } - - public func encodeNil() throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Bool) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Int) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Int8) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Int16) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Int32) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Int64) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: UInt) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: UInt8) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: UInt16) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: UInt32) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: UInt64) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Float) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Double) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: String) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: T) throws { - throw UnsupportedEncodingError() - } -} - -// MARK: Unkeyed - -extension UnsupportedEncodingContainer: UnkeyedEncodingContainer { - public var count: Int { - return 0 - } - - public func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer { - return KeyedEncodingContainer(UnsupportedEncodingContainer(encoder: encoder)) - } - - public func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - return self - } - - public func superEncoder() -> Encoder { - return encoder - } -} - -// MARK: Keyed - -extension UnsupportedEncodingContainer: KeyedEncodingContainerProtocol { - public typealias Key = K - - public func encodeNil(forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { - return KeyedEncodingContainer(UnsupportedEncodingContainer(encoder: encoder)) - } - - public func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { - return self - } - - public func superEncoder(forKey key: Key) -> Encoder { - return encoder - } - - public func encode(_ value: Bool, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Int, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Int8, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Int16, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Int32, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Int64, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: UInt, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: UInt8, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: UInt16, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: UInt32, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: UInt64, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Float, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: Double, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: String, forKey key: Key) throws { - throw UnsupportedEncodingError() - } - - public func encode(_ value: T, forKey key: Key) throws { - throw UnsupportedEncodingError() - } -} diff --git a/Sources/Core/Future+Unwrap.swift b/Sources/Core/Future+Unwrap.swift new file mode 100644 index 00000000..346b1aa9 --- /dev/null +++ b/Sources/Core/Future+Unwrap.swift @@ -0,0 +1,12 @@ +public extension Future where Expectation: OptionalType { + /// Unwraps an optional value contained inside a Future's expectation. + /// If the optional resolves to `nil` (`.none`), the supplied error will be thrown instead. + public func unwrap(or error: @autoclosure @escaping () -> Error) -> Future { + return map(to: Expectation.WrappedType.self) { optional in + guard let wrapped = optional.wrapped else { + throw error() + } + return wrapped + } + } +} diff --git a/Sources/Async/Future+Unwrap.swift b/Sources/Core/OptionalType.swift similarity index 64% rename from Sources/Async/Future+Unwrap.swift rename to Sources/Core/OptionalType.swift index 7e0999b8..0b31f40c 100644 --- a/Sources/Async/Future+Unwrap.swift +++ b/Sources/Core/OptionalType.swift @@ -1,21 +1,8 @@ -public extension Future where Expectation: OptionalType { - /// Unwraps an optional value contained inside a Future's expectation. - /// If the optional resolves to `nil` (`.none`), the supplied error will be thrown instead. - public func unwrap(or error: @autoclosure @escaping () -> Error) -> Future { - return map(to: Expectation.WrappedType.self) { optional in - guard let wrapped = optional.wrapped else { - throw error() - } - return wrapped - } - } -} - /// Capable of being represented by an optional wrapped type. /// /// This protocol mostly exists to allow constrained extensions on generic /// types where an associatedtype is an `Optional`. -public protocol OptionalType { +public protocol OptionalType: AnyOptionalType { /// Underlying wrapped type. associatedtype WrappedType @@ -45,3 +32,20 @@ extension Optional: OptionalType { return wrapped } } + +/// Type-erased `OptionalType` +public protocol AnyOptionalType { + /// Returns the wrapped type, if it exists. + var anyWrapped: Any? { get } + + /// Returns the wrapped type, if it exists. + static var anyWrappedType: Any.Type { get } +} + +extension AnyOptionalType where Self: OptionalType { + /// See `AnyOptionalType.anyWrapped` + public var anyWrapped: Any? { return wrapped } + + /// See `AnyOptionalType.anyWrappedType` + public static var anyWrappedType: Any.Type { return WrappedType.self } +} diff --git a/Sources/Core/Reflectable.swift b/Sources/Core/Reflectable.swift new file mode 100644 index 00000000..fd6e5666 --- /dev/null +++ b/Sources/Core/Reflectable.swift @@ -0,0 +1,54 @@ +/// This protocol allows for reflection of properties on conforming types. +/// +/// Ideally Swift type mirroring would handle this completely. In the interim, this protocol +/// acts to fill in the missing gaps. +/// +/// Types that conform to this protocol and are also `Decodable` will get the implementations for free +/// from the `CodableKit` module. +public protocol Reflectable { + /// Reflects all of this type's `ReflectedProperty`s. + /// + /// - parameter depth: Controls how deeply to reflect the properties. + /// Each `ReflectedProperty` has a `[String]` path. + /// The depth supplied here is a maximum for the depth of the properties. + static func reflectProperties(depth: Int) throws -> [ReflectedProperty] + + /// Returns a `ReflectedProperty` for the supplied key path. + static func reflectProperty(forKey keyPath: KeyPath) throws -> ReflectedProperty +} + +extension Reflectable { + /// Reflects all of this type's `ReflectedProperty`s with depth = 1. + public static func reflectProperties() throws -> [ReflectedProperty] { + return try reflectProperties(depth: 1) + } +} + + +/// Represents a property on a type that has been refleted using the `Reflectable` protocol. +public struct ReflectedProperty { + /// This property's type. + public let type: Any.Type + + /// The path to this property. + public let path: [String] + + /// Creates a new `ReflectedProperty` from a type and path. + public init(_ type: T.Type, at path: [String]) { + self.type = T.self + self.path = path + } + + /// Creates a new `ReflectedProperty` using `Any.Type` and a path. + public init(any type: Any.Type, at path: [String]) { + self.type = type + self.path = path + } +} + +extension ReflectedProperty: CustomStringConvertible { + /// See CustomStringConvertible.description + public var description: String { + return "\(path.joined(separator: ".")): \(type)" + } +} diff --git a/Sources/CodableKit/String+Convert.swift b/Sources/Core/String+Utilities.swift similarity index 50% rename from Sources/CodableKit/String+Convert.swift rename to Sources/Core/String+Utilities.swift index d2e5640e..09a7adb3 100644 --- a/Sources/CodableKit/String+Convert.swift +++ b/Sources/Core/String+Utilities.swift @@ -1,19 +1,3 @@ -import Debugging - -extension String { - /// Convert self to any type that conforms to LosslessStringConvertible - func convertTo(_ type: T.Type) throws -> T { - guard let converted = T.self.init(self) else { - throw CodableError( - identifier: "string", - reason: "Unable to convert \(self) to \(T.self)" - ) - } - - return converted - } -} - extension String { /// Converts the string to a boolean or return nil. public var bool: Bool? { @@ -36,9 +20,3 @@ extension String { return self + end } } - -/// Capable of being decoded from a string. -public protocol StringDecodable { - /// Decode self from a string. - static func decode(from string: String) -> Self? -} diff --git a/Tests/CodableKitTests/KeyStringDecoderTests.swift b/Tests/CodableKitTests/KeyStringDecoderTests.swift index b9869f9d..a1e71af6 100644 --- a/Tests/CodableKitTests/KeyStringDecoderTests.swift +++ b/Tests/CodableKitTests/KeyStringDecoderTests.swift @@ -1,23 +1,62 @@ +import Core import CodableKit import XCTest class KeyStringDecoderTests: XCTestCase { - func testSimpleStruct() throws { - struct Foo: Decodable { - var name: String - var age: Double - var luckyNumber: Int - var maybe: UInt32? + func testStruct() throws { + struct Foo: Reflectable, Decodable { + var bool: Bool + var obool: Bool? + var int: Int + var oint: Int? + var sarr: [String] + var osarr: [String]? + } + + let properties = try Foo.reflectProperties() + XCTAssertEqual(properties.description, "[bool: Bool, obool: Optional, int: Int, oint: Optional, sarr: Array, osarr: Optional>]") + + try XCTAssertEqual(Foo.reflectProperty(forKey: \.bool).path, ["bool"]) + try XCTAssert(Foo.reflectProperty(forKey: \.bool).type is Bool.Type) + + try XCTAssertEqual(Foo.reflectProperty(forKey: \.obool).path, ["obool"]) + try XCTAssert(Foo.reflectProperty(forKey: \.obool).type is Bool?.Type) + + try XCTAssertEqual(Foo.reflectProperty(forKey: \.int).path, ["int"]) + try XCTAssert(Foo.reflectProperty(forKey: \.int).type is Int.Type) + + try XCTAssertEqual(Foo.reflectProperty(forKey: \.oint).path, ["oint"]) + try XCTAssert(Foo.reflectProperty(forKey: \.oint).type is Int?.Type) + + try XCTAssertEqual(Foo.reflectProperty(forKey: \.sarr).path, ["sarr"]) + try XCTAssert(Foo.reflectProperty(forKey: \.sarr).type is [String].Type) + + try XCTAssertEqual(Foo.reflectProperty(forKey: \.osarr).path, ["osarr"]) + try XCTAssert(Foo.reflectProperty(forKey: \.osarr).type is [String]?.Type) + } + + func testStructCustomProperties() throws { + struct CustomStruct: Reflectable { + var hi: Bool + + static func reflectProperties(depth: Int) throws -> [ReflectedProperty] { + return [ReflectedProperty(Bool.self, at: ["hi"])] + } + + static func reflectProperty(forKey keyPath: KeyPath) throws -> ReflectedProperty { + return ReflectedProperty(Bool.self, at: ["hi"]) + } } - try XCTAssertEqual(Foo.codingPath(forKey: \.name).map { $0.stringValue }, ["name"]) - try XCTAssertEqual(Foo.codingPath(forKey: \.age).map { $0.stringValue }, ["age"]) - try XCTAssertEqual(Foo.codingPath(forKey: \.luckyNumber).map { $0.stringValue }, ["luckyNumber"]) - try XCTAssertEqual(Foo.codingPath(forKey: \.maybe).map { $0.stringValue }, ["maybe"]) + let properties = try CustomStruct.reflectProperties(depth: 1) + XCTAssertEqual(properties.description, "[hi: Bool]") + + try XCTAssertEqual(CustomStruct.reflectProperty(forKey: \.hi).path, ["hi"]) + try XCTAssert(CustomStruct.reflectProperty(forKey: \.hi).type is Bool.Type) } func testNestedStruct() throws { - struct Foo: Decodable { + struct Foo: Reflectable, Decodable { var name: String var age: Double var luckyNumber: Int @@ -31,18 +70,18 @@ class KeyStringDecoderTests: XCTestCase { var dict: [String: String] } - try XCTAssertEqual(Foo.codingPath(forKey: \.name).map { $0.stringValue }, ["name"]) - try XCTAssertEqual(Foo.codingPath(forKey: \.age).map { $0.stringValue }, ["age"]) - try XCTAssertEqual(Foo.codingPath(forKey: \.luckyNumber).map { $0.stringValue }, ["luckyNumber"]) - // XCTAssertEqual(Foo.codingPath(forKey: \.bar).map { $0.stringValue }, ["bar"]) - try XCTAssertEqual(Foo.codingPath(forKey: \.bar.name).map { $0.stringValue }, ["bar", "name"]) - try XCTAssertEqual(Foo.codingPath(forKey: \.bar.age).map { $0.stringValue }, ["bar", "age"]) - // XCTAssertEqual(Foo.codingPath(forKey: \.bar.luckyNumbers).map { $0.stringValue }, ["bar", "luckyNumbers"]) - // XCTAssertEqual(Foo.codingPath(forKey: \.bar.dict).map { $0.stringValue }, ["bar", "dict"]) + try XCTAssertEqual(Foo.reflectProperty(forKey: \.name).path, ["name"]) + try XCTAssertEqual(Foo.reflectProperty(forKey: \.age).path, ["age"]) + try XCTAssertEqual(Foo.reflectProperty(forKey: \.luckyNumber).path, ["luckyNumber"]) + XCTAssertThrowsError(try Foo.reflectProperty(forKey: \.bar)) + try XCTAssertEqual(Foo.reflectProperty(forKey: \.bar.name).path, ["bar", "name"]) + try XCTAssertEqual(Foo.reflectProperty(forKey: \.bar.age).path, ["bar", "age"]) + try XCTAssertEqual(Foo.reflectProperty(forKey: \.bar.luckyNumbers).path, ["bar", "luckyNumbers"]) + try XCTAssertEqual(Foo.reflectProperty(forKey: \.bar.dict).path, ["bar", "dict"]) } func testProperties() throws { - struct User: Decodable { + struct User: Reflectable, Decodable { var int: Int var oint: Int? var int8: Int8 @@ -88,8 +127,8 @@ class KeyStringDecoderTests: XCTestCase { var odict: [String: String]? } - let properties = try User.properties() - XCTAssertEqual(properties.description, "[int: Int, oint: Int?, int8: Int8, oint8: Int8?, int16: Int16, oint16: Int16?, int32: Int32, oint32: Int32?, int64: Int64, oint64: Int64?, uint: UInt, uoint: UInt?, uint8: UInt8, uoint8: UInt8?, uint16: UInt16, uoint16: UInt16?, uint32: UInt32, uoint32: UInt32?, uint64: UInt64, uoint64: UInt64?, uuid: UUID, ouuid: UUID?, date: Date, odate: Date?, float: Float, ofloat: Float?, double: Double, odouble: Double?, string: String, ostring: String?, bool: Bool, obool: Bool?, array: Array, oarray: Array?, dict: Dictionary, odict: Dictionary?]") + let properties = try User.reflectProperties() + XCTAssertEqual(properties.description, "[int: Int, oint: Optional, int8: Int8, oint8: Optional, int16: Int16, oint16: Optional, int32: Int32, oint32: Optional, int64: Int64, oint64: Optional, uint: UInt, uoint: Optional, uint8: UInt8, uoint8: Optional, uint16: UInt16, uoint16: Optional, uint32: UInt32, uoint32: Optional, uint64: UInt64, uoint64: Optional, uuid: UUID, ouuid: Optional, date: Date, odate: Optional, float: Float, ofloat: Optional, double: Double, odouble: Optional, string: String, ostring: Optional, bool: Bool, obool: Optional, array: Array, oarray: Optional>, dict: Dictionary, odict: Optional>]") } func testPropertyDepth() throws { @@ -97,39 +136,40 @@ class KeyStringDecoderTests: XCTestCase { var nickname: String var favoriteTreat: String } - struct User: Decodable { + struct User: Reflectable, Decodable { var pet: Pet var name: String var age: Int } - try XCTAssertEqual(User.properties(depth: 1).description, "[pet: Pet #1, name: String, age: Int]") - try XCTAssertEqual(User.properties(depth: 2).description, "[pet.nickname: String, pet.favoriteTreat: String, name: String, age: Int]") + try XCTAssertEqual(User.reflectProperties().description, "[pet: Pet #1, name: String, age: Int]") + try XCTAssertEqual(User.reflectProperties(depth: 2).description, "[pet.nickname: String, pet.favoriteTreat: String, name: String, age: Int]") } func testPropertyA() throws { - final class A: Decodable { + final class A: Reflectable, Decodable { public var id: UUID? public var date: Date public var length: Double public var isOpen: Bool } - try XCTAssertEqual(A.properties().description, "[id: UUID?, date: Date, length: Double, isOpen: Bool]") + try XCTAssertEqual(A.reflectProperties(depth: 1).description, "[id: Optional, date: Date, length: Double, isOpen: Bool]") } func testThrows() throws { - struct FooDesc: Decodable, CustomStringConvertible { + struct FooDesc: Reflectable, Decodable { var name: String var description: String { return "foo" } } - XCTAssertThrowsError(try FooDesc.codingPath(forKey: \.description).description) + XCTAssertThrowsError(try FooDesc.reflectProperty(forKey: \.description).description) } static let allTests = [ - ("testSimpleStruct", testSimpleStruct), + ("testStruct", testStruct), + ("testStructCustomProperties", testStructCustomProperties), ("testNestedStruct", testNestedStruct), ("testProperties", testProperties), ("testPropertyDepth", testPropertyDepth), diff --git a/circle.yml b/circle.yml index ffcc8665..5ff6b856 100644 --- a/circle.yml +++ b/circle.yml @@ -43,12 +43,31 @@ jobs: working_directory: ~/vapor + linux-fluent: + docker: + - image: codevapor/swift:4.1 + steps: + - run: + name: Clone Fluent SQLite + command: git clone -b master https://github.com/vapor/fluent-sqlite.git + working_directory: ~/ + - run: + name: Switch Fluent SQLite to this Core revision + command: swift package edit Core --revision $CIRCLE_SHA1 + working_directory: ~/fluent-sqlite + - run: + name: Run Fluent unit tests + command: swift test + working_directory: ~/fluent-sqlite + + workflows: version: 2 tests: jobs: - linux - linux-vapor + - linux-fluent # - macos nightly: