Skip to content

Commit

Permalink
Merge pull request #33 from mattpolzin/feature/any-codable-support
Browse files Browse the repository at this point in the history
Add AnyCodable to OpenAPIKit, remove dependency.
  • Loading branch information
mattpolzin authored Mar 20, 2020
2 parents 04a46f6 + 3918251 commit 9a66f0f
Show file tree
Hide file tree
Showing 21 changed files with 505 additions and 33 deletions.
9 changes: 0 additions & 9 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
{
"object": {
"pins": [
{
"package": "AnyCodable",
"repositoryURL": "https://github.com/Flight-School/AnyCodable.git",
"state": {
"branch": null,
"revision": "6e9610258e9f1044b5e0d88545052846448b43c3",
"version": "0.2.3"
}
},
{
"package": "FineJSON",
"repositoryURL": "https://github.com/omochi/FineJSON.git",
Expand Down
12 changes: 6 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ let package = Package(
targets: ["OpenAPIKit"]),
],
dependencies: [
.package(url: "https://github.com/Flight-School/AnyCodable.git", .upToNextMinor(from: "0.2.2")),
.package(url: "https://github.com/jpsim/Yams.git", from: "2.0.0"), // just for tests
.package(url: "https://github.com/omochi/FineJSON.git", from: "1.14.0") // just for tests
],
targets: [
.target(
name: "OpenAPIKit",
dependencies: ["AnyCodable"]),
dependencies: []),
.testTarget(
name: "OpenAPIKitTests",
dependencies: ["OpenAPIKit", "Yams", "FineJSON"]),
Expand All @@ -34,12 +33,13 @@ let package = Package(
dependencies: ["OpenAPIKit", "Yams"]),
.testTarget(
name: "EitherTests",
dependencies: ["OpenAPIKit"]
),
dependencies: ["OpenAPIKit"]),
.testTarget(
name: "OrderedDictionaryTests",
dependencies: ["OpenAPIKit", "Yams", "FineJSON"]
)
dependencies: ["OpenAPIKit", "Yams", "FineJSON"]),
.testTarget(
name: "AnyCodableTests",
dependencies: ["OpenAPIKit"])
],
swiftLanguageVersions: [ .v5 ]
)
259 changes: 259 additions & 0 deletions Sources/OpenAPIKit/AnyCodable/AnyCodable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
/*
Copyright 2018 Read Evaluate Press, LLC

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

import Foundation

/**
A type-erased `Codable` value.

The `AnyCodable` type forwards encoding and decoding responsibilities
to an underlying value, hiding its specific underlying type.

You can encode or decode mixed-type values in dictionaries
and other collections that require `Encodable` or `Decodable` conformance
by declaring their contained type to be `AnyCodable`.
*/
public struct AnyCodable {
public let value: Any

public init<T>(_ value: T?) {
self.value = value ?? ()
}
}

extension AnyCodable: Encodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()

switch value {
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
case let number as NSNumber:
try encode(nsnumber: number, into: &container)
#endif
case is NSNull, is Void:
try container.encodeNil()
case let bool as Bool:
try container.encode(bool)
case let int as Int:
try container.encode(int)
case let int8 as Int8:
try container.encode(int8)
case let int16 as Int16:
try container.encode(int16)
case let int32 as Int32:
try container.encode(int32)
case let int64 as Int64:
try container.encode(int64)
case let uint as UInt:
try container.encode(uint)
case let uint8 as UInt8:
try container.encode(uint8)
case let uint16 as UInt16:
try container.encode(uint16)
case let uint32 as UInt32:
try container.encode(uint32)
case let uint64 as UInt64:
try container.encode(uint64)
case let float as Float:
try container.encode(float)
case let double as Double:
try container.encode(double)
case let string as String:
try container.encode(string)
case let date as Date:
try container.encode(date)
case let url as URL:
try container.encode(url)
case let array as [Any?]:
try container.encode(array.map { AnyCodable($0) })
case let dictionary as [String: Any?]:
try container.encode(dictionary.mapValues { AnyCodable($0) })
default:
let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyCodable value cannot be encoded")
throw EncodingError.invalidValue(value, context)
}
}

#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
private func encode(nsnumber: NSNumber, into container: inout SingleValueEncodingContainer) throws {
switch CFNumberGetType(nsnumber) {
case .charType:
try container.encode(nsnumber.boolValue)
case .sInt8Type:
try container.encode(nsnumber.int8Value)
case .sInt16Type:
try container.encode(nsnumber.int16Value)
case .sInt32Type:
try container.encode(nsnumber.int32Value)
case .sInt64Type:
try container.encode(nsnumber.int64Value)
case .shortType:
try container.encode(nsnumber.uint16Value)
case .longType:
try container.encode(nsnumber.uint32Value)
case .longLongType:
try container.encode(nsnumber.uint64Value)
case .intType, .nsIntegerType, .cfIndexType:
try container.encode(nsnumber.intValue)
case .floatType, .float32Type:
try container.encode(nsnumber.floatValue)
case .doubleType, .float64Type, .cgFloatType:
try container.encode(nsnumber.doubleValue)
#if swift(>=5.0)
@unknown default:
fatalError()
#endif
}
}
#endif
}

extension AnyCodable: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()

if container.decodeNil() {
self.init(NSNull())
} else if let bool = try? container.decode(Bool.self) {
self.init(bool)
} else if let int = try? container.decode(Int.self) {
self.init(int)
} else if let uint = try? container.decode(UInt.self) {
self.init(uint)
} else if let double = try? container.decode(Double.self) {
self.init(double)
} else if let string = try? container.decode(String.self) {
self.init(string)
} else if let array = try? container.decode([AnyCodable].self) {
self.init(array.map { $0.value })
} else if let dictionary = try? container.decode([String: AnyCodable].self) {
self.init(dictionary.mapValues { $0.value })
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyCodable value cannot be decoded")
}
}
}

extension AnyCodable: Equatable {
public static func == (lhs: AnyCodable, rhs: AnyCodable) -> Bool {
switch (lhs.value, rhs.value) {
case is (Void, Void):
return true
case let (lhs as Bool, rhs as Bool):
return lhs == rhs
case let (lhs as Int, rhs as Int):
return lhs == rhs
case let (lhs as Int8, rhs as Int8):
return lhs == rhs
case let (lhs as Int16, rhs as Int16):
return lhs == rhs
case let (lhs as Int32, rhs as Int32):
return lhs == rhs
case let (lhs as Int64, rhs as Int64):
return lhs == rhs
case let (lhs as UInt, rhs as UInt):
return lhs == rhs
case let (lhs as UInt8, rhs as UInt8):
return lhs == rhs
case let (lhs as UInt16, rhs as UInt16):
return lhs == rhs
case let (lhs as UInt32, rhs as UInt32):
return lhs == rhs
case let (lhs as UInt64, rhs as UInt64):
return lhs == rhs
case let (lhs as Float, rhs as Float):
return lhs == rhs
case let (lhs as Double, rhs as Double):
return lhs == rhs
case let (lhs as String, rhs as String):
return lhs == rhs
case let (lhs as [String: AnyCodable], rhs as [String: AnyCodable]):
return lhs == rhs
case let (lhs as [AnyCodable], rhs as [AnyCodable]):
return lhs == rhs
default:
return false
}
}
}

extension AnyCodable: CustomStringConvertible {
public var description: String {
switch value {
case is Void:
return String(describing: nil as Any?)
case let value as CustomStringConvertible:
return value.description
default:
return String(describing: value)
}
}
}

extension AnyCodable: CustomDebugStringConvertible {
public var debugDescription: String {
switch value {
case let value as CustomDebugStringConvertible:
return "AnyCodable(\(value.debugDescription))"
default:
return "AnyCodable(\(description))"
}
}
}

extension AnyCodable: ExpressibleByNilLiteral {}
extension AnyCodable: ExpressibleByBooleanLiteral {}
extension AnyCodable: ExpressibleByIntegerLiteral {}
extension AnyCodable: ExpressibleByFloatLiteral {}
extension AnyCodable: ExpressibleByStringLiteral {}
extension AnyCodable: ExpressibleByArrayLiteral {}
extension AnyCodable: ExpressibleByDictionaryLiteral {}

extension AnyCodable {
public init(nilLiteral _: ()) {
self.init(nil as Any?)
}

public init(booleanLiteral value: Bool) {
self.init(value)
}

public init(integerLiteral value: Int) {
self.init(value)
}

public init(floatLiteral value: Double) {
self.init(value)
}

public init(stringLiteral value: String) {
self.init(value)
}

public init(arrayLiteral elements: Any...) {
self.init(elements)
}

public init(dictionaryLiteral elements: (AnyHashable, Any)...) {
self.init([AnyHashable: Any](elements, uniquingKeysWith: { first, _ in first }))
}
}
4 changes: 4 additions & 0 deletions Sources/OpenAPIKit/AnyCodable/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

This folder contains work that is derivative of the AnyCodable library you can find at https://github.com/Flight-School/AnyCodable.

OpenAPIKit takes on the burden of maintaining and testing the types found herein, but the license from the originating work is included at the top of each file per the instructions of the MIT license.
1 change: 0 additions & 1 deletion Sources/OpenAPIKit/CodableVendorExtendable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import Foundation
import AnyCodable

/// A `VendorExtendable` type is a type that supports arbitrary
/// additions as long as those additions are keyed by strings starting
Expand Down
1 change: 0 additions & 1 deletion Sources/OpenAPIKit/Content.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import Foundation
import AnyCodable

extension OpenAPI {
/// OpenAPI Spec "Media Type Object"
Expand Down
1 change: 0 additions & 1 deletion Sources/OpenAPIKit/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// Created by Mathew Polzin on 1/13/19.
//

import AnyCodable
import Foundation

extension OpenAPI {
Expand Down
1 change: 0 additions & 1 deletion Sources/OpenAPIKit/Either/Either.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import Foundation
import AnyCodable

public enum Either<A, B> {
case a(A)
Expand Down
1 change: 0 additions & 1 deletion Sources/OpenAPIKit/Example.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import Foundation
import AnyCodable

extension OpenAPI {
/// OpenAPI Spec "Example Object"
Expand Down
1 change: 0 additions & 1 deletion Sources/OpenAPIKit/Header.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import Foundation
import AnyCodable

extension OpenAPI {
/// OpenAPI Spec "Header Object"
Expand Down
2 changes: 0 additions & 2 deletions Sources/OpenAPIKit/Path Item/ParameterSchema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
// Created by Mathew Polzin on 12/29/19.
//

import AnyCodable

extension OpenAPI.PathItem.Parameter {
/// OpenAPI Spec "Parameter Object" schema and style configuration.
///
Expand Down
3 changes: 0 additions & 3 deletions Sources/OpenAPIKit/Schema Conformances/SchemaProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
// declare themselves able to represent themselves
// as an Open API Schema Object.

import Foundation
import AnyCodable

/// Anything conforming to `OpenAPISchemaType` can provide an
/// OpenAPI schema representing itself.
public protocol OpenAPISchemaType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// Created by Mathew Polzin on 01/13/19.
//

import AnyCodable
import Foundation

/**
Expand Down
1 change: 0 additions & 1 deletion Sources/OpenAPIKit/Schema Object/SchemaObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import Foundation
import AnyCodable

/// OpenAPI "Schema Object"
///
Expand Down
Loading

0 comments on commit 9a66f0f

Please sign in to comment.