Skip to content

Commit 07ee940

Browse files
authored
[Refactor] Introduce TranslatorContext (1/2) (#638)
### Motivation To make upcoming PRs less noisy, we need a way to propagate configuration all the way deep to types like TypeAssigner/TypeMatcher, which historically didn't have that need. ### Modifications Introduced a new wrapper type called TranslatorContext, which wraps the existing closure that converts arbitrary strings into safe Swift names (which might in the future also need to be further customized). The idea is that this new struct is what we'd attach more config to shortly. There'll be a few more smaller PRs like this, I'm breaking them up for easier review. ### Result Generalized the way to propagate config into low level translator utilities. ### Test Plan All tests still pass.
1 parent de51f3d commit 07ee940

28 files changed

+92
-101
lines changed

Sources/_OpenAPIGeneratorCore/Translator/ClientTranslator/ClientTranslator.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ struct ClientFileTranslator: FileTranslator {
3737
let imports =
3838
Constants.File.clientServerImports + config.additionalImports.map { ImportDescription(moduleName: $0) }
3939

40-
let clientMethodDecls =
41-
try OperationDescription.all(from: doc.paths, in: components, asSwiftSafeName: swiftSafeName)
40+
let clientMethodDecls = try OperationDescription.all(from: doc.paths, in: components, context: context)
4241
.map(translateClientMethod(_:))
4342

4443
let clientStructPropertyDecl: Declaration = .commentable(

Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/SwiftSafeNames.swift

+1-10
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414
import Foundation
1515

16-
extension FileTranslator {
17-
18-
/// Returns a copy of the string modified to be a valid Swift identifier.
19-
///
20-
/// - Parameter string: The string to convert to be safe for Swift.
21-
/// - Returns: A Swift-safe version of the input string.
22-
func swiftSafeName(for string: String) -> String { string.safeForSwiftCode }
23-
}
24-
25-
fileprivate extension String {
16+
extension String {
2617

2718
/// Returns a string sanitized to be usable as a Swift identifier.
2819
///

Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateAllAnyOneOf.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ extension TypesFileTranslator {
7878
originalName: key,
7979
typeUsage: propertyType,
8080
associatedDeclarations: associatedDeclarations,
81-
asSwiftSafeName: swiftSafeName
81+
context: context
8282
)
8383
var referenceStack = ReferenceStack.empty
8484
let isKeyValuePairSchema = try TypeMatcher.isKeyValuePair(
@@ -209,7 +209,7 @@ extension TypesFileTranslator {
209209
let decoder: Declaration
210210
if let discriminator {
211211
let originalName = discriminator.propertyName
212-
let swiftName = swiftSafeName(for: originalName)
212+
let swiftName = context.asSwiftSafeName(originalName)
213213
codingKeysDecls = [
214214
.enum(
215215
accessModifier: config.access,

Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateObjectStruct.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ extension TypesFileTranslator {
100100
originalName: key,
101101
typeUsage: propertyType,
102102
associatedDeclarations: associatedDeclarations,
103-
asSwiftSafeName: swiftSafeName
103+
context: context
104104
)
105105
}
106106

@@ -175,7 +175,7 @@ extension TypesFileTranslator {
175175
default: .emptyInit,
176176
isSerializedInTopLevelDictionary: false,
177177
associatedDeclarations: associatedDeclarations,
178-
asSwiftSafeName: swiftSafeName
178+
context: context
179179
)
180180
return (.allowingAdditionalProperties, extraProperty)
181181
}

Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateStringEnum.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ extension FileTranslator {
4949
// In nullable enum schemas, empty strings are parsed as Void.
5050
// This is unlikely to be fixed, so handling that case here.
5151
// https://github.com/apple/swift-openapi-generator/issues/118
52-
if isNullable && anyValue is Void { return (swiftSafeName(for: ""), .string("")) }
52+
if isNullable && anyValue is Void { return (context.asSwiftSafeName(""), .string("")) }
5353
guard let rawValue = anyValue as? String else {
5454
throw GenericError(message: "Disallowed value for a string enum '\(typeName)': \(anyValue)")
5555
}
56-
let caseName = swiftSafeName(for: rawValue)
56+
let caseName = context.asSwiftSafeName(rawValue)
5757
return (caseName, .string(rawValue))
5858
case .integer:
5959
let rawValue: Int

Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/DiscriminatorExtensions.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ extension FileTranslator {
7979
/// component.
8080
/// - Parameter type: The `OneOfMappedType` for which to determine the case name.
8181
/// - Returns: A string representing the safe Swift name for the specified `OneOfMappedType`.
82-
func safeSwiftNameForOneOfMappedType(_ type: OneOfMappedType) -> String { swiftSafeName(for: type.rawNames[0]) }
82+
func safeSwiftNameForOneOfMappedType(_ type: OneOfMappedType) -> String {
83+
context.asSwiftSafeName(type.rawNames[0])
84+
}
8385
}
8486

8587
extension OpenAPI.Discriminator {

Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/StructBlueprint.swift

+3-4
Original file line numberDiff line numberDiff line change
@@ -146,15 +146,14 @@ struct PropertyBlueprint {
146146
/// referring to them in the property.
147147
var associatedDeclarations: [Declaration] = []
148148

149-
/// A converted function from user-provided strings to strings
150-
/// safe to be used as a Swift identifier.
151-
var asSwiftSafeName: (String) -> String
149+
/// A set of configuration values that inform translation.
150+
var context: TranslatorContext
152151
}
153152

154153
extension PropertyBlueprint {
155154

156155
/// A name that is verified to be a valid Swift identifier.
157-
var swiftSafeName: String { asSwiftSafeName(originalName) }
156+
var swiftSafeName: String { context.asSwiftSafeName(originalName) }
158157

159158
/// The JSON path to the property.
160159
///

Sources/_OpenAPIGeneratorCore/Translator/FileTranslator.swift

+16
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,19 @@ protocol FileTranslator {
4343
/// - Throws: An error if translation encounters issues or errors during the process.
4444
func translateFile(parsedOpenAPI: ParsedOpenAPIRepresentation) throws -> StructuredSwiftRepresentation
4545
}
46+
47+
extension FileTranslator {
48+
49+
/// A new context from the file translator.
50+
var context: TranslatorContext { TranslatorContext(asSwiftSafeName: { $0.safeForSwiftCode }) }
51+
}
52+
53+
/// A set of configuration values for concrete file translators.
54+
struct TranslatorContext {
55+
56+
/// A closure that returns a copy of the string modified to be a valid Swift identifier.
57+
///
58+
/// - Parameter string: The string to convert to be safe for Swift.
59+
/// - Returns: A Swift-safe version of the input string.
60+
var asSwiftSafeName: (String) -> String
61+
}

Sources/_OpenAPIGeneratorCore/Translator/Multipart/MultipartContentInspector.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ extension FileTranslator {
120120
}
121121
var parts: [MultipartSchemaTypedContent] = try topLevelObject.properties.compactMap {
122122
(key, value) -> MultipartSchemaTypedContent? in
123-
let swiftSafeName = swiftSafeName(for: key)
123+
let swiftSafeName = context.asSwiftSafeName(key)
124124
let typeName = typeName.appending(
125125
swiftComponent: swiftSafeName + Constants.Global.inlineTypeSuffix,
126126
jsonComponent: key

Sources/_OpenAPIGeneratorCore/Translator/Multipart/translateMultipart.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ extension TypesFileTranslator {
6464
typeUsage: headersTypeName.asUsage,
6565
default: headersStructBlueprint.hasEmptyInit ? .emptyInit : nil,
6666
associatedDeclarations: [headersStructDecl],
67-
asSwiftSafeName: swiftSafeName
67+
context: context
6868
)
6969
} else {
7070
headersProperty = nil
@@ -90,7 +90,7 @@ extension TypesFileTranslator {
9090
originalName: Constants.Operation.Body.variableName,
9191
typeUsage: bodyTypeUsage,
9292
associatedDeclarations: associatedDeclarations,
93-
asSwiftSafeName: swiftSafeName
93+
context: context
9494
)
9595
let structDecl = translateStructBlueprint(
9696
.init(
@@ -137,7 +137,7 @@ extension TypesFileTranslator {
137137
switch part {
138138
case .documentedTyped(let documentedPart):
139139
let caseDecl: Declaration = .enumCase(
140-
name: swiftSafeName(for: documentedPart.originalName),
140+
name: context.asSwiftSafeName(documentedPart.originalName),
141141
kind: .nameWithAssociatedValues([.init(type: .init(part.wrapperTypeUsage))])
142142
)
143143
let decl = try translateMultipartPartContent(
@@ -404,7 +404,7 @@ extension FileTranslator {
404404
switch part {
405405
case .documentedTyped(let part):
406406
let originalName = part.originalName
407-
let identifier = swiftSafeName(for: originalName)
407+
let identifier = context.asSwiftSafeName(originalName)
408408
let contentType = part.partInfo.contentType
409409
let partTypeName = part.typeName
410410
let schema = part.schema
@@ -613,7 +613,7 @@ extension FileTranslator {
613613
switch part {
614614
case .documentedTyped(let part):
615615
let originalName = part.originalName
616-
let identifier = swiftSafeName(for: originalName)
616+
let identifier = context.asSwiftSafeName(originalName)
617617
let contentType = part.partInfo.contentType
618618
let headersTypeName = part.typeName.appending(
619619
swiftComponent: Constants.Operation.Output.Payload.Headers.typeName,

Sources/_OpenAPIGeneratorCore/Translator/Operations/OperationDescription.swift

+9-12
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@ struct OperationDescription {
3131
/// The OpenAPI components, used to resolve JSON references.
3232
var components: OpenAPI.Components
3333

34-
/// A converted function from user-provided strings to strings
35-
/// safe to be used as a Swift identifier.
36-
var asSwiftSafeName: (String) -> String
34+
/// A set of configuration values that inform translation.
35+
var context: TranslatorContext
3736

3837
/// The OpenAPI operation object.
3938
var operation: OpenAPI.Operation { endpoint.operation }
@@ -52,7 +51,7 @@ extension OperationDescription {
5251
/// - Parameters:
5352
/// - map: The paths from the OpenAPI document.
5453
/// - components: The components from the OpenAPI document.
55-
/// - asSwiftSafeName: A converted function from user-provided strings
54+
/// - context: A set of configuration values that inform translation.
5655
/// to strings safe to be used as a Swift identifier.
5756
/// - Returns: An array of `OperationDescription` instances, each representing
5857
/// an operation discovered in the provided paths.
@@ -62,11 +61,9 @@ extension OperationDescription {
6261
/// 1. OpenAPI 3.0.3 only supports external path references (cf. 3.1, which supports internal references too)
6362
/// 2. Swift OpenAPI Generator currently only supports OpenAPI 3.0.x.
6463
/// 3. Swift OpenAPI Generator currently doesn't support external references.
65-
static func all(
66-
from map: OpenAPI.PathItem.Map,
67-
in components: OpenAPI.Components,
68-
asSwiftSafeName: @escaping (String) -> String
69-
) throws -> [OperationDescription] {
64+
static func all(from map: OpenAPI.PathItem.Map, in components: OpenAPI.Components, context: TranslatorContext)
65+
throws -> [OperationDescription]
66+
{
7067
try map.flatMap { path, value in
7168
let value = try value.resolve(in: components)
7269
return value.endpoints.map { endpoint in
@@ -75,7 +72,7 @@ extension OperationDescription {
7572
endpoint: endpoint,
7673
pathParameters: value.parameters,
7774
components: components,
78-
asSwiftSafeName: asSwiftSafeName
75+
context: context
7976
)
8077
}
8178
}
@@ -86,7 +83,7 @@ extension OperationDescription {
8683
/// Uses the `operationID` value in the OpenAPI operation, if one was
8784
/// specified. Otherwise, computes a unique name from the operation's
8885
/// path and HTTP method.
89-
var methodName: String { asSwiftSafeName(operationID) }
86+
var methodName: String { context.asSwiftSafeName(operationID) }
9087

9188
/// Returns the identifier for the operation.
9289
///
@@ -295,7 +292,7 @@ extension OperationDescription {
295292
}
296293
let newPath = OpenAPI.Path(newComponents, trailingSlash: path.trailingSlash)
297294
let names: [Expression] = orderedPathParameters.map { param in
298-
.identifierPattern("input").dot("path").dot(asSwiftSafeName(param))
295+
.identifierPattern("input").dot("path").dot(context.asSwiftSafeName(param))
299296
}
300297
let arrayExpr: Expression = .literal(.array(names))
301298
return (newPath.rawValue, arrayExpr)

Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift

+4-5
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,8 @@ struct TypedParameter {
3434
/// The coding strategy appropriate for this parameter.
3535
var codingStrategy: CodingStrategy
3636

37-
/// A converted function from user-provided strings to strings
38-
/// safe to be used as a Swift identifier.
39-
var asSwiftSafeName: (String) -> String
37+
/// A set of configuration values that inform translation.
38+
var context: TranslatorContext
4039
}
4140

4241
extension TypedParameter: CustomStringConvertible {
@@ -49,7 +48,7 @@ extension TypedParameter {
4948
var name: String { parameter.name }
5049

5150
/// The name of the parameter sanitized to be a valid Swift identifier.
52-
var variableName: String { asSwiftSafeName(name) }
51+
var variableName: String { context.asSwiftSafeName(name) }
5352

5453
/// A Boolean value that indicates whether the parameter must be specified
5554
/// when performing the OpenAPI operation.
@@ -208,7 +207,7 @@ extension FileTranslator {
208207
explode: explode,
209208
typeUsage: usage,
210209
codingStrategy: codingStrategy,
211-
asSwiftSafeName: swiftSafeName
210+
context: context
212211
)
213212
}
214213
}

Sources/_OpenAPIGeneratorCore/Translator/Parameters/translateParameter.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ extension TypesFileTranslator {
4242
originalName: parameter.name,
4343
typeUsage: parameter.typeUsage,
4444
associatedDeclarations: associatedDeclarations,
45-
asSwiftSafeName: swiftSafeName
45+
context: context
4646
)
4747
}
4848

Sources/_OpenAPIGeneratorCore/Translator/RequestBody/translateRequestBody.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ extension TypesFileTranslator {
9292
typeUsage: bodyEnumTypeUsage,
9393
default: nil,
9494
associatedDeclarations: extraDecls,
95-
asSwiftSafeName: swiftSafeName
95+
context: context
9696
)
9797
return bodyProperty
9898
}

Sources/_OpenAPIGeneratorCore/Translator/Responses/TypedResponseHeader.swift

+4-5
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,14 @@ struct TypedResponseHeader {
3232
/// The coding strategy appropriate for this parameter.
3333
var codingStrategy: CodingStrategy
3434

35-
/// A converted function from user-provided strings to strings
36-
/// safe to be used as a Swift identifier.
37-
var asSwiftSafeName: (String) -> String
35+
/// A set of configuration values that inform translation.
36+
var context: TranslatorContext
3837
}
3938

4039
extension TypedResponseHeader {
4140

4241
/// The name of the header sanitized to be a valid Swift identifier.
43-
var variableName: String { asSwiftSafeName(name) }
42+
var variableName: String { context.asSwiftSafeName(name) }
4443

4544
/// A Boolean value that indicates whether the response header can
4645
/// be omitted in the HTTP response.
@@ -152,7 +151,7 @@ extension FileTranslator {
152151
schema: schema,
153152
typeUsage: usage,
154153
codingStrategy: codingStrategy,
155-
asSwiftSafeName: swiftSafeName
154+
context: context
156155
)
157156
}
158157
}

Sources/_OpenAPIGeneratorCore/Translator/Responses/translateResponse.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ extension TypesFileTranslator {
5050
typeUsage: headersTypeName.asUsage,
5151
default: headersStructBlueprint.hasEmptyInit ? .emptyInit : nil,
5252
associatedDeclarations: [headersStructDecl],
53-
asSwiftSafeName: swiftSafeName
53+
context: context
5454
)
5555
} else {
5656
headersProperty = nil
@@ -92,7 +92,7 @@ extension TypesFileTranslator {
9292
typeUsage: contentTypeUsage,
9393
default: hasNoContent ? .nil : nil,
9494
associatedDeclarations: [contentEnumDecl],
95-
asSwiftSafeName: swiftSafeName
95+
context: context
9696
)
9797
} else {
9898
bodyProperty = nil

Sources/_OpenAPIGeneratorCore/Translator/Responses/translateResponseHeader.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ extension TypesFileTranslator {
7878
typeUsage: typeUsage,
7979
default: header.header.required ? nil : .nil,
8080
associatedDeclarations: associatedDeclarations,
81-
asSwiftSafeName: swiftSafeName
81+
context: context
8282
)
8383
}
8484

Sources/_OpenAPIGeneratorCore/Translator/ServerTranslator/ServerTranslator.swift

+1-5
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,7 @@ struct ServerFileTranslator: FileTranslator {
3535
let imports =
3636
Constants.File.clientServerImports + config.additionalImports.map { ImportDescription(moduleName: $0) }
3737

38-
let allOperations = try OperationDescription.all(
39-
from: doc.paths,
40-
in: components,
41-
asSwiftSafeName: swiftSafeName
42-
)
38+
let allOperations = try OperationDescription.all(from: doc.paths, in: components, context: context)
4339

4440
let (registerHandlersDecl, serverMethodDecls) = try translateRegisterHandlers(allOperations)
4541

0 commit comments

Comments
 (0)