diff --git a/Package.resolved b/Package.resolved index 0703061..69dfbfa 100644 --- a/Package.resolved +++ b/Package.resolved @@ -10,6 +10,15 @@ "version": "0.1.0" } }, + { + "package": "FileCheck", + "repositoryURL": "https://github.com/llvm-swift/FileCheck.git", + "state": { + "branch": null, + "revision": "89b8480055f9adf8ce2f9ad5e2fac7ac1076242e", + "version": "0.0.8" + } + }, { "package": "Poly", "repositoryURL": "https://github.com/mattpolzin/Poly.git", @@ -18,6 +27,15 @@ "revision": "77f45b8963a51c02d71fc4075eba5cff47ff0d07", "version": null } + }, + { + "package": "SwiftCheck", + "repositoryURL": "https://github.com/typelift/SwiftCheck.git", + "state": { + "branch": null, + "revision": "cf9958085b2ee1643e541e407c3233d1b76c18ff", + "version": "0.11.0" + } } ] }, diff --git a/Package.swift b/Package.swift index d1b92a5..3622d82 100644 --- a/Package.swift +++ b/Package.swift @@ -9,33 +9,43 @@ let package = Package( .library( name: "JSONAPI", targets: ["JSONAPI"]), - .library( - name: "JSONAPITesting", - targets: ["JSONAPITesting"]), + .library( + name: "JSONAPITesting", + targets: ["JSONAPITesting"]), + .library( + name: "JSONAPIArbitrary", + targets: ["JSONAPIArbitrary"]), .library( name: "JSONAPIOpenAPI", targets: ["JSONAPIOpenAPI"]) ], dependencies: [ - .package(url: "https://github.com/mattpolzin/Poly.git", .branch("master")), - .package(url: "https://github.com/Flight-School/AnyCodable.git", from: "0.1.0") + .package(url: "https://github.com/mattpolzin/Poly.git", .branch("master")), + .package(url: "https://github.com/Flight-School/AnyCodable.git", from: "0.1.0"), + .package(url: "https://github.com/typelift/SwiftCheck.git", from: "0.11.0") ], targets: [ .target( name: "JSONAPI", dependencies: ["Poly"]), - .target( - name: "JSONAPITesting", - dependencies: ["JSONAPI"]), + .target( + name: "JSONAPITesting", + dependencies: ["JSONAPI"]), + .target( + name: "JSONAPIArbitrary", + dependencies: ["JSONAPI", "SwiftCheck"]), .target( name: "JSONAPIOpenAPI", - dependencies: ["JSONAPI", "AnyCodable"]), + dependencies: ["JSONAPI", "AnyCodable", "JSONAPIArbitrary"]), .testTarget( name: "JSONAPITests", dependencies: ["JSONAPI", "JSONAPITesting"]), .testTarget( name: "JSONAPITestingTests", dependencies: ["JSONAPI", "JSONAPITesting"]), + .testTarget( + name: "JSONAPIArbitraryTests", + dependencies: ["JSONAPI", "SwiftCheck", "JSONAPIArbitrary"]), .testTarget( name: "JSONAPIOpenAPITests", dependencies: ["JSONAPI", "JSONAPIOpenAPI"]) diff --git a/Sources/JSONAPIArbitrary/Id+Arbitrary.swift b/Sources/JSONAPIArbitrary/Id+Arbitrary.swift new file mode 100644 index 0000000..17b7e23 --- /dev/null +++ b/Sources/JSONAPIArbitrary/Id+Arbitrary.swift @@ -0,0 +1,15 @@ +// +// Id+Arbitrary.swift +// JSONAPIArbitrary +// +// Created by Mathew Polzin on 1/14/19. +// + +import SwiftCheck +import JSONAPI + +extension Id: Arbitrary where RawType: Arbitrary { + public static var arbitrary: Gen> { + return RawType.arbitrary.map { Id(rawValue: $0) } + } +} diff --git a/Sources/JSONAPIOpenAPI/JSONAPIOpenAPITypes.swift b/Sources/JSONAPIOpenAPI/JSONAPIOpenAPITypes.swift index ecd76db..97865df 100644 --- a/Sources/JSONAPIOpenAPI/JSONAPIOpenAPITypes.swift +++ b/Sources/JSONAPIOpenAPI/JSONAPIOpenAPITypes.swift @@ -11,7 +11,7 @@ private protocol _Optional {} extension Optional: _Optional {} extension Attribute: OpenAPINodeType where RawValue: OpenAPINodeType { - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { // If the RawValue is not required, we actually consider it // nullable. To be not required is for the Attribute itself // to be optional. @@ -23,7 +23,7 @@ extension Attribute: OpenAPINodeType where RawValue: OpenAPINodeType { } extension TransformedAttribute: OpenAPINodeType where RawValue: OpenAPINodeType { - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { // If the RawValue is not required, we actually consider it // nullable. To be not required is for the Attribute itself // to be optional. @@ -37,7 +37,7 @@ extension TransformedAttribute: OpenAPINodeType where RawValue: OpenAPINodeType extension ToOneRelationship: OpenAPINodeType { // TODO: const for json `type` // TODO: metadata & links - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { let nullable = Identifiable.self is _Optional.Type return .object(.init(format: .generic, required: true), @@ -60,7 +60,7 @@ extension ToOneRelationship: OpenAPINodeType { extension ToManyRelationship: OpenAPINodeType { // TODO: const for json `type` // TODO: metadata & links - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { return .object(.init(format: .generic, required: true), .init(properties: [ diff --git a/Sources/JSONAPIOpenAPI/OpenAPI.swift b/Sources/JSONAPIOpenAPI/OpenAPI.swift deleted file mode 100644 index 966d43a..0000000 --- a/Sources/JSONAPIOpenAPI/OpenAPI.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// OpenAPI.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 1/13/19. -// - -public enum OpenAPI {} diff --git a/Sources/JSONAPIOpenAPI/OpenAPITypes+Codable.swift b/Sources/JSONAPIOpenAPI/OpenAPITypes+Codable.swift index 4212ccc..7b04957 100644 --- a/Sources/JSONAPIOpenAPI/OpenAPITypes+Codable.swift +++ b/Sources/JSONAPIOpenAPI/OpenAPITypes+Codable.swift @@ -5,7 +5,7 @@ // Created by Mathew Polzin on 1/14/19. // -extension OpenAPI.JSONNode.Context: Encodable { +extension JSONNode.Context: Encodable { private enum CodingKeys: String, CodingKey { case type @@ -31,7 +31,7 @@ extension OpenAPI.JSONNode.Context: Encodable { } } -extension OpenAPI.JSONNode.NumericContext: Encodable { +extension JSONNode.NumericContext: Encodable { private enum CodingKeys: String, CodingKey { case multipleOf case maximum @@ -65,7 +65,7 @@ extension OpenAPI.JSONNode.NumericContext: Encodable { } } -extension OpenAPI.JSONNode.StringContext: Encodable { +extension JSONNode.StringContext: Encodable { private enum CodingKeys: String, CodingKey { case maxLength case minLength @@ -87,7 +87,7 @@ extension OpenAPI.JSONNode.StringContext: Encodable { } } -extension OpenAPI.JSONNode.ArrayContext: Encodable { +extension JSONNode.ArrayContext: Encodable { private enum CodingKeys: String, CodingKey { case items case maxItems @@ -110,7 +110,7 @@ extension OpenAPI.JSONNode.ArrayContext: Encodable { } } -extension OpenAPI.JSONNode.ObjectContext : Encodable{ +extension JSONNode.ObjectContext : Encodable{ private enum CodingKeys: String, CodingKey { case maxProperties case minProperties @@ -142,7 +142,7 @@ extension OpenAPI.JSONNode.ObjectContext : Encodable{ } } -extension OpenAPI.JSONNode: Encodable { +extension JSONNode: Encodable { public func encode(to encoder: Encoder) throws { switch self { case .boolean(let context): diff --git a/Sources/JSONAPIOpenAPI/OpenAPITypes.swift b/Sources/JSONAPIOpenAPI/OpenAPITypes.swift index 87271ae..c039975 100644 --- a/Sources/JSONAPIOpenAPI/OpenAPITypes.swift +++ b/Sources/JSONAPIOpenAPI/OpenAPITypes.swift @@ -8,7 +8,7 @@ import AnyCodable public protocol OpenAPINodeType { - static var openAPINode: OpenAPI.JSONNode { get } + static var openAPINode: JSONNode { get } } public protocol SwiftTyped { @@ -18,49 +18,30 @@ public protocol SwiftTyped { public protocol OpenAPIFormat: SwiftTyped, Codable, Equatable { static var unspecified: Self { get } - var jsonType: OpenAPI.JSONType { get } + var jsonType: JSONType { get } } public protocol JSONNodeContext { var required: Bool { get } } -public extension OpenAPI { - enum JSONType: String, Codable { - case boolean = "boolean" - case object = "object" - case array = "array" - case number = "number" - case integer = "integer" - case string = "string" - } - - enum JSONTypeFormat: Equatable { - case boolean(BooleanFormat) - case object(ObjectFormat) - case array(ArrayFormat) - case number(NumberFormat) - case integer(IntegerFormat) - case string(StringFormat) - } - - /// A JSON Node is what OpenAPI calls a - /// "Schema Object" - enum JSONNode { - case boolean(Context) - indirect case object(Context, ObjectContext) - indirect case array(Context, ArrayContext) - case number(Context, NumericContext) - case integer(Context, NumericContext) - case string(Context, StringContext) - indirect case allOf([JSONNode]) - indirect case oneOf([JSONNode]) - indirect case anyOf([JSONNode]) - indirect case not(JSONNode) - } +public enum JSONType: String, Codable { + case boolean = "boolean" + case object = "object" + case array = "array" + case number = "number" + case integer = "integer" + case string = "string" } -public extension OpenAPI.JSONTypeFormat { +public enum JSONTypeFormat: Equatable { + case boolean(BooleanFormat) + case object(ObjectFormat) + case array(ArrayFormat) + case number(NumberFormat) + case integer(IntegerFormat) + case string(StringFormat) + public enum BooleanFormat: String, Equatable, OpenAPIFormat { case generic = "" @@ -70,7 +51,7 @@ public extension OpenAPI.JSONTypeFormat { return .generic } - public var jsonType: OpenAPI.JSONType { + public var jsonType: JSONType { return .boolean } } @@ -84,7 +65,7 @@ public extension OpenAPI.JSONTypeFormat { return .generic } - public var jsonType: OpenAPI.JSONType { + public var jsonType: JSONType { return .object } } @@ -98,7 +79,7 @@ public extension OpenAPI.JSONTypeFormat { return .generic } - public var jsonType: OpenAPI.JSONType { + public var jsonType: JSONType { return .array } } @@ -114,7 +95,7 @@ public extension OpenAPI.JSONTypeFormat { return .generic } - public var jsonType: OpenAPI.JSONType { + public var jsonType: JSONType { return .number } } @@ -130,7 +111,7 @@ public extension OpenAPI.JSONTypeFormat { return .generic } - public var jsonType: OpenAPI.JSONType { + public var jsonType: JSONType { return .integer } } @@ -149,12 +130,12 @@ public extension OpenAPI.JSONTypeFormat { return .generic } - public var jsonType: OpenAPI.JSONType { + public var jsonType: JSONType { return .string } } - public var jsonType: OpenAPI.JSONType { + public var jsonType: JSONType { switch self { case .boolean: return .boolean @@ -172,7 +153,20 @@ public extension OpenAPI.JSONTypeFormat { } } -extension OpenAPI.JSONNode { +/// A JSON Node is what OpenAPI calls a +/// "Schema Object" +public enum JSONNode { + case boolean(Context) + indirect case object(Context, ObjectContext) + indirect case array(Context, ArrayContext) + case number(Context, NumericContext) + case integer(Context, NumericContext) + case string(Context, StringContext) + indirect case allOf([JSONNode]) + indirect case oneOf([JSONNode]) + indirect case anyOf([JSONNode]) + indirect case not(JSONNode) + public struct Context: JSONNodeContext, Equatable { public let format: Format public let required: Bool @@ -258,7 +252,7 @@ extension OpenAPI.JSONNode { public struct ArrayContext { /// A JSON Type Node that describes /// the type of each element in the array. - public let items: OpenAPI.JSONNode + public let items: JSONNode /// Maximum number of items in array. public let maxItems: Int? @@ -272,7 +266,7 @@ extension OpenAPI.JSONNode { /// to be unique. Defaults to false. public let uniqueItems: Bool - public init(items: OpenAPI.JSONNode, + public init(items: JSONNode, maxItems: Int? = nil, minItems: Int = 0, uniqueItems: Bool = false) { @@ -286,8 +280,8 @@ extension OpenAPI.JSONNode { public struct ObjectContext { public let maxProperties: Int? public let minProperties: Int - public let properties: [String: OpenAPI.JSONNode] - public let additionalProperties: [String: OpenAPI.JSONNode]? + public let properties: [String: JSONNode] + public let additionalProperties: [String: JSONNode]? /* // NOTE that an object's required properties @@ -296,8 +290,8 @@ extension OpenAPI.JSONNode { public let required: [String] */ - public init(properties: [String: OpenAPI.JSONNode], - additionalProperties: [String: OpenAPI.JSONNode]? = nil, + public init(properties: [String: JSONNode], + additionalProperties: [String: JSONNode]? = nil, maxProperties: Int? = nil, minProperties: Int = 0) { self.properties = properties @@ -307,7 +301,7 @@ extension OpenAPI.JSONNode { } } - public var jsonTypeFormat: OpenAPI.JSONTypeFormat? { + public var jsonTypeFormat: JSONTypeFormat? { switch self { case .boolean(let context): return .boolean(context.format) @@ -341,7 +335,7 @@ extension OpenAPI.JSONNode { } /// Return the optional version of this JSONNode - public func optionalNode() -> OpenAPI.JSONNode { + public func optionalNode() -> JSONNode { switch self { case .boolean(let context): return .boolean(context.optionalContext()) @@ -361,7 +355,7 @@ extension OpenAPI.JSONNode { } /// Return the required version of this JSONNode - public func requiredNode() -> OpenAPI.JSONNode { + public func requiredNode() -> JSONNode { switch self { case .boolean(let context): return .boolean(context.requiredContext()) @@ -381,7 +375,7 @@ extension OpenAPI.JSONNode { } /// Return the nullable version of this JSONNode - public func nullableNode() -> OpenAPI.JSONNode { + public func nullableNode() -> JSONNode { switch self { case .boolean(let context): return .boolean(context.nullableContext()) diff --git a/Sources/JSONAPIOpenAPI/SwiftPrimitiveTypes.swift b/Sources/JSONAPIOpenAPI/SwiftPrimitiveTypes.swift index c217eb4..ce7fe6b 100644 --- a/Sources/JSONAPIOpenAPI/SwiftPrimitiveTypes.swift +++ b/Sources/JSONAPIOpenAPI/SwiftPrimitiveTypes.swift @@ -30,13 +30,13 @@ Any object: **/ extension Optional: OpenAPINodeType where Wrapped: OpenAPINodeType { - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { return Wrapped.openAPINode.optionalNode() } } extension String: OpenAPINodeType { - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { return .string(.init(format: .generic, required: true), .init()) @@ -44,14 +44,14 @@ extension String: OpenAPINodeType { } extension Bool: OpenAPINodeType { - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { return .boolean(.init(format: .generic, required: true)) } } extension Array: OpenAPINodeType where Element: OpenAPINodeType { - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { return .array(.init(format: .generic, required: true), .init(items: Element.openAPINode)) @@ -59,7 +59,7 @@ extension Array: OpenAPINodeType where Element: OpenAPINodeType { } extension Double: OpenAPINodeType { - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { return .number(.init(format: .double, required: true), .init()) @@ -67,7 +67,7 @@ extension Double: OpenAPINodeType { } extension Float: OpenAPINodeType { - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { return .number(.init(format: .float, required: true), .init()) @@ -75,7 +75,7 @@ extension Float: OpenAPINodeType { } extension Int: OpenAPINodeType { - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { return .integer(.init(format: .generic, required: true), .init()) @@ -83,7 +83,7 @@ extension Int: OpenAPINodeType { } extension Int32: OpenAPINodeType { - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { return .integer(.init(format: .int32, required: true), .init()) @@ -91,7 +91,7 @@ extension Int32: OpenAPINodeType { } extension Int64: OpenAPINodeType { - static public var openAPINode: OpenAPI.JSONNode { + static public var openAPINode: JSONNode { return .integer(.init(format: .int64, required: true), .init())