Skip to content

Commit 762fbdd

Browse files
author
simonbility
committed
Support replacement also in inline defined schemas
1 parent 68ef99c commit 762fbdd

File tree

4 files changed

+85
-13
lines changed

4 files changed

+85
-13
lines changed

Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateSchema.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,11 @@ extension TypesFileTranslator {
8787
)
8888
)
8989
}
90-
if let substituteTypeName = schema.vendorExtensions["x-swift-open-api-substitute-type"]?.value
91-
as? String
92-
{
93-
try diagnostics.emit(.note(message: "Substituting type \(typeName) with \(substituteTypeName)"))
94-
let substitutedType = TypeName(swiftKeyPath: substituteTypeName.components(separatedBy: ".")).asUsage
95-
96-
let typealiasDecl = try translateTypealias(
90+
if let substituteType = schema.value.substituteType() {
91+
let typealiasDecl = try translateSubstitutedType(
9792
named: typeName,
9893
userDescription: overrides.userDescription ?? schema.description,
99-
to: substitutedType.withOptional(
94+
to: substituteType.asUsage.withOptional(
10095
overrides.isOptional ?? typeMatcher.isOptional(schema, components: components)
10196
)
10297
)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftOpenAPIGenerator open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
import OpenAPIKit
15+
16+
extension FileTranslator {
17+
18+
/// Returns a declaration of a typealias.
19+
/// - Parameters:
20+
/// - typeName: The name of the type to give to the declared typealias.
21+
/// - userDescription: A user-specified description from the OpenAPI document.
22+
/// - existingTypeUsage: The existing type the alias points to.
23+
/// - Throws: An error if there is an issue during translation.
24+
/// - Returns: A declaration representing the translated typealias.
25+
func translateSubstitutedType(named typeName: TypeName, userDescription: String?, to existingTypeUsage: TypeUsage) throws
26+
-> Declaration
27+
{
28+
let typealiasDescription = TypealiasDescription(
29+
accessModifier: config.access,
30+
name: typeName.shortSwiftName,
31+
existingType: .init(existingTypeUsage.withOptional(false))
32+
)
33+
let typealiasComment: Comment? = typeName.docCommentWithUserDescription(userDescription)
34+
return .commentable(typealiasComment, .typealias(typealiasDescription))
35+
}
36+
}

Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ enum Constants {
372372
/// The substring used in method names for the multipart coding strategy.
373373
static let multipart: String = "Multipart"
374374
}
375+
/// Constants related to the vendor extensions..
376+
enum VendorExtension { static let replaceType: String = "x-swift-open-api-replace-type" }
375377

376378
/// Constants related to types used in many components.
377379
enum Global {

Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/TypeMatcher.swift

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414
import OpenAPIKit
15+
import Foundation
1516

1617
/// A set of functions that match Swift types onto OpenAPI types.
1718
struct TypeMatcher {
@@ -46,7 +47,11 @@ struct TypeMatcher {
4647
matchedArrayHandler: { elementType, nullableItems in
4748
nullableItems ? elementType.asOptional.asArray : elementType.asArray
4849
},
49-
genericArrayHandler: { TypeName.arrayContainer.asUsage }
50+
genericArrayHandler: { TypeName.arrayContainer.asUsage },
51+
substitutedTypeHandler: { substitute in
52+
// never built-in
53+
nil
54+
}
5055
)
5156
}
5257

@@ -75,7 +80,10 @@ struct TypeMatcher {
7580
matchedArrayHandler: { elementType, nullableItems in
7681
nullableItems ? elementType.asOptional.asArray : elementType.asArray
7782
},
78-
genericArrayHandler: { TypeName.arrayContainer.asUsage }
83+
genericArrayHandler: { TypeName.arrayContainer.asUsage },
84+
substitutedTypeHandler: { substitute in
85+
substitute.asUsage
86+
}
7987
)?
8088
.withOptional(isOptional(schema, components: components))
8189
}
@@ -98,7 +106,8 @@ struct TypeMatcher {
98106
return true
99107
},
100108
matchedArrayHandler: { elementIsReferenceable, _ in elementIsReferenceable },
101-
genericArrayHandler: { true }
109+
genericArrayHandler: { true },
110+
substitutedTypeHandler: { _ in true }
102111
) ?? false
103112
}
104113

@@ -353,8 +362,10 @@ struct TypeMatcher {
353362
for schema: JSONSchema.Schema,
354363
test: (JSONSchema.Schema) throws -> R?,
355364
matchedArrayHandler: (R, _ nullableItems: Bool) -> R,
356-
genericArrayHandler: () -> R
365+
genericArrayHandler: () -> R,
366+
substitutedTypeHandler: (TypeName) -> R?
357367
) rethrows -> R? {
368+
if let substitute = schema.substituteType() { return substitutedTypeHandler(substitute) }
358369
switch schema {
359370
case let .array(_, arrayContext):
360371
guard let items = arrayContext.items else { return genericArrayHandler() }
@@ -363,11 +374,39 @@ struct TypeMatcher {
363374
for: items.value,
364375
test: test,
365376
matchedArrayHandler: matchedArrayHandler,
366-
genericArrayHandler: genericArrayHandler
377+
genericArrayHandler: genericArrayHandler,
378+
substitutedTypeHandler: substitutedTypeHandler
367379
)
368380
else { return nil }
369381
return matchedArrayHandler(itemsResult, items.nullable)
370382
default: return try test(schema)
371383
}
372384
}
373385
}
386+
387+
extension JSONSchema.Schema {
388+
func substituteType() -> TypeName? {
389+
let extensions: [String: AnyCodable] =
390+
switch self {
391+
case .null(let context): context.vendorExtensions
392+
case .boolean(let context): context.vendorExtensions
393+
case .number(let context, _): context.vendorExtensions
394+
case .integer(let context, _): context.vendorExtensions
395+
case .string(let context, _): context.vendorExtensions
396+
case .object(let context, _): context.vendorExtensions
397+
case .array(let context, _): context.vendorExtensions
398+
case .all(of: _, core: let context): context.vendorExtensions
399+
case .one(of: _, core: let context): context.vendorExtensions
400+
case .any(of: _, core: let context): context.vendorExtensions
401+
case .not: [:]
402+
case .reference(_, let context): context.vendorExtensions
403+
case .fragment(let context): context.vendorExtensions
404+
}
405+
guard let substituteTypeName = extensions[Constants.VendorExtension.replaceType]?.value as? String else {
406+
return nil
407+
}
408+
assert(!substituteTypeName.isEmpty)
409+
410+
return TypeName(swiftKeyPath: substituteTypeName.components(separatedBy: "."))
411+
}
412+
}

0 commit comments

Comments
 (0)