Skip to content
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