Skip to content

Commit

Permalink
split things into two modules
Browse files Browse the repository at this point in the history
  • Loading branch information
mattpolzin committed Sep 24, 2023
1 parent 11300f1 commit 6b00ecb
Show file tree
Hide file tree
Showing 16 changed files with 1,286 additions and 7 deletions.
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"package": "OpenAPIKit",
"repositoryURL": "https://github.com/mattpolzin/OpenAPIKit.git",
"state": {
"branch": null,
"revision": "d96f819964a665438c15134465d334d4d3446034",
"branch": "release/3_0",
"revision": "b069168ebd9bac3704beab3aadff07b589aadeb2",
"version": null
}
},
Expand Down
11 changes: 8 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@ let package = Package(
targets: ["OpenAPIReflection"]),
],
dependencies: [
// .package(url: "https://github.com/mattpolzin/OpenAPIKit.git", from: "2.0.0"),
.package(url: "https://github.com/mattpolzin/OpenAPIKit.git", .revision("d96f819964a665438c15134465d334d4d3446034")),
.package(url: "https://github.com/mattpolzin/OpenAPIKit.git", .branch("release/3_0")),
.package(url: "https://github.com/mattpolzin/Sampleable.git", from: "2.1.0")
],
targets: [
.target(
name: "OpenAPIReflection",
name: "OpenAPIReflection30",
dependencies: [.product(name: "OpenAPIKit30", package: "OpenAPIKit"), "Sampleable"]),
.testTarget(
name: "OpenAPIReflection30Tests",
dependencies: ["OpenAPIReflection30"]),
.target(
name: "OpenAPIReflection",
dependencies: [.product(name: "OpenAPIKit", package: "OpenAPIKit"), "Sampleable"]),
.testTarget(
name: "OpenAPIReflectionTests",
dependencies: ["OpenAPIReflection"]),
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
[![Swift 5.1+](http://img.shields.io/badge/Swift-5.2+-blue.svg)](https://swift.org)
[![Swift 5.2+](http://img.shields.io/badge/Swift-5.2+-blue.svg)](https://swift.org)

[![MIT license](http://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) ![Tests](https://github.com/mattpolzin/OpenAPIReflection/workflows/Tests/badge.svg)

# OpenAPI support

See parent library at https://github.com/mattpolzin/OpenAPIKit
See parent library at https://github.com/mattpolzin/OpenAPIKit.

To generate OpenAPI 3.1.x types, use the `OpenAPIReflection` module. To generate OpenAPI 3.0.x types, use the `OpenAPIReflection30` module.

# OpenAPIReflection

Expand Down
75 changes: 75 additions & 0 deletions Sources/OpenAPIReflection30/AnyJSONCaseIterable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// AnyJSONCaseIterable.swift
// OpenAPI
//
// Created by Mathew Polzin on 6/22/19.
//

import Foundation
import OpenAPIKit30

public protocol AnyRawRepresentable {
/// The `RawValue` type of this type.
static var rawValueType: Any.Type { get }
}

extension AnyRawRepresentable where Self: RawRepresentable {
/// The default `rawValueType` of a `RawRepresentable` is just the
/// type of `Self.RawValue`.
public static var rawValueType: Any.Type { return Self.RawValue.self }
}

/// Anything conforming to `AnyJSONCaseIterable` can provide a
/// list of its possible values.
public protocol AnyJSONCaseIterable: AnyRawRepresentable {
static func allCases(using encoder: JSONEncoder) -> [AnyCodable]
}

extension AnyJSONCaseIterable where Self: RawRepresentable {
/// The default `rawValueType` of a `RawRepresentable` is just the
/// type of `Self.RawValue`.
public static var rawValueType: Any.Type { return Self.RawValue.self }
}

public extension AnyJSONCaseIterable {
/// Given an array of Codable values, retrieve an array of AnyCodables.
static func allCases<T: Encodable>(from input: [T], using encoder: JSONEncoder) throws -> [AnyCodable] {
return try OpenAPIReflection30.allCases(from: input, using: encoder)
}
}

public extension AnyJSONCaseIterable where Self: CaseIterable, Self: Codable {
static func caseIterableOpenAPISchemaGuess(using encoder: JSONEncoder) throws -> JSONSchema {
guard let first = allCases.first else {
throw OpenAPI.EncodableError.exampleNotCodable
}
let itemSchema = try OpenAPIReflection30.nestedGenericOpenAPISchemaGuess(for: first, using: encoder)

return itemSchema.with(allowedValues: allCases.map { AnyCodable($0) })
}
}

extension CaseIterable where Self: Encodable {
public static func allCases(using encoder: JSONEncoder) -> [AnyCodable] {
return (try? OpenAPIReflection30.allCases(from: Array(Self.allCases), using: encoder)) ?? []
}
}

fileprivate func allCases<T: Encodable>(from input: [T], using encoder: JSONEncoder) throws -> [AnyCodable] {
if let alreadyGoodToGo = input as? [AnyCodable] {
return alreadyGoodToGo
}

// The following is messy, but it does get us the intended result:
// Given any array of things that can be encoded, we want
// to map to an array of AnyCodable so we can store later. We need to
// muck with JSONSerialization because something like an `enum` may
// very well be encoded as a string, and therefore representable
// by AnyCodable, but AnyCodable wants it to actually BE a String
// upon initialization.

guard let arrayOfCodables = try JSONSerialization.jsonObject(with: encoder.encode(input), options: []) as? [Any] else {
throw OpenAPI.EncodableError.allCasesArrayNotCodable
}
return arrayOfCodables.map(AnyCodable.init)
}
48 changes: 48 additions & 0 deletions Sources/OpenAPIReflection30/Date+OpenAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// Date+OpenAPI.swift
// OpenAPI
//
// Created by Mathew Polzin on 1/24/19.
//

import Foundation
import OpenAPIKit30

extension Date: DateOpenAPISchemaType {
public static func dateOpenAPISchemaGuess(using encoder: JSONEncoder) -> JSONSchema? {

switch encoder.dateEncodingStrategy {
case .deferredToDate, .custom:
// I don't know if we can say anything about this case without
// encoding the Date and looking at it, which is what `primitiveGuess()`
// does.
return nil

case .secondsSince1970,
.millisecondsSince1970:
return .number(format: .double)

case .iso8601:
return .string(format: .dateTime)

case .formatted(let formatter):
let hasTime = formatter.timeStyle != .none
let format: JSONTypeFormat.StringFormat = hasTime ? .dateTime : .date

return .string(format: format)

@unknown default:
return nil
}
}
}

extension Date: OpenAPIEncodedSchemaType {
public static func openAPISchema(using encoder: JSONEncoder) throws -> JSONSchema {
guard let dateSchema: JSONSchema = try openAPISchemaGuess(for: Date(), using: encoder) else {
throw OpenAPI.TypeError.unknownSchemaType(type(of: self))
}

return dateSchema
}
}
32 changes: 32 additions & 0 deletions Sources/OpenAPIReflection30/OpenAPI+Errors.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// OpenAPI+Errors.swift
//
//
// Created by Mathew Polzin on 4/21/20.
//

import Foundation
import OpenAPIKit30

extension OpenAPI {
public enum TypeError: Swift.Error, CustomDebugStringConvertible {
case invalidSchema
case unknownSchemaType(Any.Type)

public var debugDescription: String {
switch self {
case .invalidSchema:
return "Invalid Schema"
case .unknownSchemaType(let type):
return "Could not determine OpenAPI schema type of \(String(describing: type))"
}
}
}

public enum EncodableError: Swift.Error, Equatable {
case allCasesArrayNotCodable
case exampleNotCodable
case primitiveGuessFailed
case exampleNotSupported(String)
}
}
13 changes: 13 additions & 0 deletions Sources/OpenAPIReflection30/Optional+ZipWith.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Optional+ZipWith.swift
// OpenAPIKit
//
// Created by Mathew Polzin on 1/19/19.
//

/// Zip two optionals together with the given operation performed on
/// the unwrapped contents. If either optional is nil, the zip
/// yields nil.
func zip<X, Y, Z>(_ left: X?, _ right: Y?, with fn: (X, Y) -> Z) -> Z? {
return left.flatMap { lft in right.map { rght in fn(lft, rght) }}
}
Loading

0 comments on commit 6b00ecb

Please sign in to comment.