Skip to content

Commit

Permalink
2.19.4
Browse files Browse the repository at this point in the history
  • Loading branch information
dankinsoid committed Dec 1, 2023
1 parent 6533e57 commit 2746032
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 17 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -163,7 +163,7 @@ import PackageDescription
let package = Package(
name: "SomeProject",
dependencies: [
.package(url: "https://github.com/dankinsoid/SwiftOpenAPI.git", from: "2.19.3")
.package(url: "https://github.com/dankinsoid/SwiftOpenAPI.git", from: "2.19.4")
],
targets: [
.target(name: "SomeProject", dependencies: ["SwiftOpenAPI"])
Expand Down
59 changes: 47 additions & 12 deletions Sources/SwiftOpenAPI/Encoders/SchemeEncoder.swift
Expand Up @@ -11,11 +11,10 @@ struct SchemeEncoder {
_ value: Encodable,
into schemas: inout ComponentsMap<SchemaObject>
) throws -> ReferenceOr<SchemaObject> {
let type = Swift.type(of: value)
return try parse(
try parse(
value: TypeRevision().describeType(of: value),
type: type,
into: &schemas
type: Swift.type(of: value),
into: &schemas
)
}

Expand All @@ -24,21 +23,50 @@ struct SchemeEncoder {
_ type: Decodable.Type,
into schemas: inout ComponentsMap<SchemaObject>
) throws -> ReferenceOr<SchemaObject> {
try parse(
try parse(
value: TypeRevision().describe(type: type),
type: type,
into: &schemas
)
}

func parse(
value: TypeInfo,
type: Any.Type,
into schemas: inout ComponentsMap<SchemaObject>
) throws -> ReferenceOr<SchemaObject> {
var needReparse = false
var cache: [ObjectIdentifier: ReferenceOr<SchemaObject>] = [:]
var result = try parse(
value: value,
type: type,
into: &schemas,
cache: &cache,
needReparse: &needReparse
)
if needReparse {
result = try parse(
value: value,
type: type,
into: &schemas,
cache: &cache,
needReparse: &needReparse
)
}
return result
}

func parse(
value: @autoclosure () throws -> TypeInfo,
value: TypeInfo,
type: Any.Type,
into schemas: inout ComponentsMap<SchemaObject>
into schemas: inout ComponentsMap<SchemaObject>,
cache: inout [ObjectIdentifier: ReferenceOr<SchemaObject>],
needReparse: inout Bool
) throws -> ReferenceOr<SchemaObject> {
let name = String.typeName(type)
var result: ReferenceOr<SchemaObject>
let typeInfo = try value()
let typeInfo = value
let typeID = ObjectIdentifier(typeInfo.type)

switch type {
case is Date.Type:
Expand Down Expand Up @@ -67,7 +95,7 @@ struct SchemeEncoder {
properties: keyedInfo.fields.mapKeys {
keyEncodingStrategy.encode($0)
}.mapValues {
try parse(value: $0, type: $0.type, into: &schemas)
try parse(value: $0, type: $0.type, into: &schemas, cache: &cache, needReparse: &needReparse)
},
required: Set(keyedInfo.fields.unorderedHash.filter { !$0.value.isOptional }.keys)
)
Expand All @@ -76,20 +104,25 @@ struct SchemeEncoder {
case false:
let schema = try SchemaObject.dictionary(
of: (keyedInfo.fields.first?.value).map {
try parse(value: $0, type: $0.type, into: &schemas)
try parse(value: $0, type: $0.type, into: &schemas, cache: &cache, needReparse: &needReparse)
} ?? .any
)
result = .value(schema)
}

case let .unkeyed(itemInfo):
let schema = try SchemaObject.array(
of: parse(value: itemInfo, type: itemInfo.type, into: &schemas)
of: parse(value: itemInfo, type: itemInfo.type, into: &schemas, cache: &cache, needReparse: &needReparse)
)
result = .value(schema)

case .recursive:
result = .ref(components: \.schemas, name)
needReparse = true
if let cached = cache[typeID] {
result = cached
} else {
result = .ref(components: \.schemas, name)
}
}
}

Expand All @@ -100,11 +133,13 @@ struct SchemeEncoder {
if extractReferences, result.isReferenceable {
result.object?.nullable = nil
schemas[name] = result
cache[typeID] = .ref(components: \.schemas, name)
return .ref(components: \.schemas, name)
} else {
if typeInfo.isOptional, result.object?.enum == nil {
result.object?.nullable = true
}
cache[typeID] = result
return result
}
}
Expand Down
Expand Up @@ -309,7 +309,7 @@ private struct TypeRevisionSingleValueDecodingContainer: SingleValueDecodingCont

private func _decodeIfPresent<T>(_ type: T.Type, optional: Bool) -> T? where T : Decodable {
let decoder = TypeRevisionDecoder(
path: nestedPath(for: type),
path: nestedPath(for: optional ? Optional<T>.self : type),
context: decoder.context
)
let decodable = try? decoder.decode(type)
Expand Down Expand Up @@ -535,7 +535,10 @@ private struct TypeRevisionKeyedDecodingContainer<Key: CodingKey>: KeyedDecoding
}

private func decode<T: Decodable>(_ type: T.Type, forKey key: Key, optional: Bool) throws -> T {
let decoder = TypeRevisionDecoder(path: nestedPath(for: key, type), context: decoder.context)
let decoder = TypeRevisionDecoder(
path: nestedPath(for: key, optional ? Optional<T>.self : type),
context: decoder.context
)
let decodeResult = Result {
try decoder.decode(type)
}
Expand Down
Expand Up @@ -337,7 +337,7 @@ private struct TypeRevisionKeyedEncodingContainer<Key: CodingKey>: KeyedEncoding

private mutating func encode<T>(_ value: T?, forKey key: Key, optional: Bool) throws where T: Encodable {
let encoder = TypeRevisionEncoder(
path: nestedPath(for: key, T.self),
path: nestedPath(for: key, optional ? Optional<T>.self : T.self),
context: encoder.context
)
var info = try encoder.encode(value, type: T.self)
Expand Down
10 changes: 9 additions & 1 deletion Tests/SwiftOpenAPITests/ArrayDecodingTests.swift
Expand Up @@ -46,7 +46,14 @@ class ArrayDecodingTests: XCTestCase {
func testDecodeEmbeddedRecursiveOptionalArray() throws {
var schemas: ComponentsMap<SchemaObject> = [:]
let _ = try ReferenceOr<SchemaObject>.encodeSchema(EmbeddedOptionalRecursiveTypeInArray.example, into: &schemas)
XCTAssertNoDifference(schemas, ["ProductDependency": .value(ProductDependency.scheme)])
XCTAssertNoDifference(
schemas["EmbeddedOptionalRecursiveTypeInArray"],
.value(EmbeddedOptionalRecursiveTypeInArray.scheme)
)
XCTAssertNoDifference(
schemas["ProductDependency"],
.value(ProductDependency.scheme)
)
}
}

Expand Down Expand Up @@ -76,6 +83,7 @@ public struct EmbeddedOptionalRecursiveTypeInArray: Codable, Equatable {
properties: [
"name": .string,
"dependencies": .array(of: .ref(components: \.schemas, "ProductDependency"))
.with(\.nullable, true)
],
required: ["name"]
)
Expand Down

1 comment on commit 2746032

@heckj
Copy link
Contributor

@heckj heckj commented on 2746032 Dec 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

Please sign in to comment.