Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DynamicCodable Coders #8

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
98 changes: 98 additions & 0 deletions Sources/DynamicCodable/CoderInternals.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//
// CoderInternals.swift
// DynamicCodable
//
// Created by Dimitri Bouniol on 4/18/21.
// Copyright © 2021 Mochi Development, Inc. All rights reserved.
//

extension Dictionary where Key == DynamicCodable.Key, Value == DynamicCodable {
@inline(__always)
subscript(key: CodingKey) -> DynamicCodable? {
if let intKey = key.intValue, let value = self[intKey] {
return value
} else if let value = self[key.stringValue] {
return value
}
return nil
}
}

struct DynamicCoderCodingKey: CodingKey {
public var stringValue: String
public var intValue: Int?

public init?(stringValue: String) {
self.stringValue = stringValue
self.intValue = nil
}

public init?(intValue: Int) {
self.stringValue = "\(intValue)"
self.intValue = intValue
}

init(stringValue: String, intValue: Int?) {
self.stringValue = stringValue
self.intValue = intValue
}

init(index: Int) {
self.stringValue = "Index \(index)"
self.intValue = index
}

static let `super` = DynamicCoderCodingKey(stringValue: "super", intValue: nil)
}

extension DynamicCodable {
var debugDataTypeDescription: String {
switch self {
case .keyed(_): return "a keyed container"
case .unkeyed(_): return "an unkeyed container"
case .nil: return "nil"
case .bool(_): return "a boolean"
case .string(_): return "a string"
case .float64(_): return "a float64"
case .float32(_): return "a float32"
case .int(_): return "an int"
case .int8(_): return "an int8"
case .int16(_): return "an int16"
case .int32(_): return "an int32"
case .int64(_): return "an int64"
case .uint(_): return "a uint"
case .uint8(_): return "a uint8"
case .uint16(_): return "a uint16"
case .uint32(_): return "a uint32"
case .uint64(_): return "a uint64"
case .empty: return "an empty container"
}
}

@inline(__always)
func unwrap<T>(errorHandler: () throws -> T) rethrows -> T {
switch T.self {
case is Keyed.Type: if case .keyed(let keyed) = self { return unsafeBitCast(keyed, to: T.self) }
case is Unkeyed.Type: if case .unkeyed(let unkeyed) = self { return unsafeBitCast(unkeyed, to: T.self) }
case is Nil.Type: if case .nil = self { return unsafeBitCast(Nil.none, to: T.self) }
case is Bool.Type: if case .bool(let bool) = self { return unsafeBitCast(bool, to: T.self) }
case is String.Type: if case .string(let string) = self { return unsafeBitCast(string, to: T.self) }
case is Float64.Type: if case .float64(let float64) = self { return unsafeBitCast(float64, to: T.self) }
case is Float32.Type: if case .float64(let float32) = self { return unsafeBitCast(float32, to: T.self) }
case is Int.Type: if case .int(let int) = self { return unsafeBitCast(int, to: T.self) }
case is Int8.Type: if case .int8(let int8) = self { return unsafeBitCast(int8, to: T.self) }
case is Int16.Type: if case .int16(let int16) = self { return unsafeBitCast(int16, to: T.self) }
case is Int32.Type: if case .int32(let int32) = self { return unsafeBitCast(int32, to: T.self) }
case is Int64.Type: if case .int64(let int64) = self { return unsafeBitCast(int64, to: T.self) }
case is UInt.Type: if case .uint(let uint) = self { return unsafeBitCast(uint, to: T.self) }
case is UInt8.Type: if case .uint8(let uint8) = self { return unsafeBitCast(uint8, to: T.self) }
case is UInt16.Type: if case .uint16(let uint16) = self { return unsafeBitCast(uint16, to: T.self) }
case is UInt32.Type: if case .uint32(let uint32) = self { return unsafeBitCast(uint32, to: T.self) }
case is UInt64.Type: if case .uint64(let uint64) = self { return unsafeBitCast(uint64, to: T.self) }
case is Empty.Type: if case .empty = self { return unsafeBitCast((), to: T.self) }
default: break // TODO: We should do something different here, so we can ignore this case in the caller. Perhaps return a specialized error?
}

return try errorHandler()
}
}
80 changes: 78 additions & 2 deletions Sources/DynamicCodable/DynamicCodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
public enum DynamicCodable: Equatable, Hashable {
/// A value coded using a keyed container such as a dictionary.
/// - Tag: DynamicCodable.keyed
case keyed([Key : Self])
case keyed(Keyed)

/// A value coded using a keyed container such as an array.
/// - Tag: DynamicCodable.unkeyed
case unkeyed([Self])
case unkeyed(Unkeyed)

/// A value coding nil as a single value container.
/// - Tag: DynamicCodable.nil
Expand Down Expand Up @@ -80,18 +80,94 @@ public enum DynamicCodable: Equatable, Hashable {
/// A (rare) value coding an empty single value container. Only certain decoders may even support this.
/// - Tag: DynamicCodable.empty
case empty

// MARK: - DynamicCodableTypes

/// The underlying type for [.keyed](x-source-tag://DynamicCodable.keyed) values.
/// - Tag: DynamicCodable.Keyed
public typealias Keyed = [DynamicCodable.Key : DynamicCodable]

/// The underlying type for [.unkeyed](x-source-tag://DynamicCodable.unkeyed) values.
/// - Tag: DynamicCodable.Unkeyed
public typealias Unkeyed = [DynamicCodable]

/// The underlying type for [.nil](x-source-tag://DynamicCodable.nil) values.
/// - Tag: DynamicCodable.Nil
public typealias Nil = Optional<Any>

/// The underlying type for [.bool](x-source-tag://DynamicCodable.bool) values.
/// - Tag: DynamicCodable.Bool
public typealias Bool = Swift.Bool

/// The underlying type for [.string](x-source-tag://DynamicCodable.string) values.
/// - Tag: DynamicCodable.String
public typealias String = Swift.String

/// The underlying type for [.float64](x-source-tag://DynamicCodable.float64) values.
/// - Tag: DynamicCodable.Float64
public typealias Float64 = Swift.Float64

/// The underlying type for [.float32](x-source-tag://DynamicCodable.float32) values.
/// - Tag: DynamicCodable.Float32
public typealias Float32 = Swift.Float32

/// The underlying type for [.int](x-source-tag://DynamicCodable.int) values.
/// - Tag: DynamicCodable.Int
public typealias Int = Swift.Int

/// The underlying type for [.int8](x-source-tag://DynamicCodable.int8) values.
/// - Tag: DynamicCodable.Int8
public typealias Int8 = Swift.Int8

/// The underlying type for [.int16](x-source-tag://DynamicCodable.int16) values.
/// - Tag: DynamicCodable.Int16
public typealias Int16 = Swift.Int16

/// The underlying type for [.int32](x-source-tag://DynamicCodable.int32) values.
/// - Tag: DynamicCodable.Int32
public typealias Int32 = Swift.Int32

/// The underlying type for [.int64](x-source-tag://DynamicCodable.int64) values.
/// - Tag: DynamicCodable.Int64
public typealias Int64 = Swift.Int64

/// The underlying type for [.uint](x-source-tag://DynamicCodable.uint) values.
/// - Tag: DynamicCodable.UInt
public typealias UInt = Swift.UInt

/// The underlying type for [.uint8](x-source-tag://DynamicCodable.uint8) values.
/// - Tag: DynamicCodable.UInt8
public typealias UInt8 = Swift.UInt8

/// The underlying type for [.uint16](x-source-tag://DynamicCodable.uint16) values.
/// - Tag: DynamicCodable.UInt16
public typealias UInt16 = Swift.UInt16

/// The underlying type for [.uint32](x-source-tag://DynamicCodable.uint32) values.
/// - Tag: DynamicCodable.UInt32
public typealias UInt32 = Swift.UInt32

/// The underlying type for [.uint64](x-source-tag://DynamicCodable.uint64) values.
/// - Tag: DynamicCodable.UInt64
public typealias UInt64 = Swift.UInt64

/// The underlying type for [.empty](x-source-tag://DynamicCodable.empty) values.
/// - Tag: DynamicCodable.Empty
public typealias Empty = Swift.Void
}

extension DynamicCodable {
/// A convenience case for creating a [float32 case](x-source-tag://DynamicCodable.float32).
/// - Parameter float: The float to represent.
/// - Returns: DynamicCodable.float32
/// - Tag: DynamicCodable.float
@inlinable
public static func float(_ float: Float) -> Self { .float32(float) }

/// A convenience case for creating a [float64 case](x-source-tag://DynamicCodable.float64).
/// - Parameter float: The float to represent.
/// - Returns: DynamicCodable.float64
/// - Tag: DynamicCodable.double
@inlinable
public static func double(_ double: Double) -> Self { .float64(double) }
}
Loading