Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor] Introduce TranslatorContext (1/2) #638

Merged
merged 3 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ struct ClientFileTranslator: FileTranslator {
let imports =
Constants.File.clientServerImports + config.additionalImports.map { ImportDescription(moduleName: $0) }

let clientMethodDecls =
try OperationDescription.all(from: doc.paths, in: components, asSwiftSafeName: swiftSafeName)
let clientMethodDecls = try OperationDescription.all(from: doc.paths, in: components, context: context)
.map(translateClientMethod(_:))

let clientStructPropertyDecl: Declaration = .commentable(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,7 @@
//===----------------------------------------------------------------------===//
import Foundation

extension FileTranslator {

/// Returns a copy of the string modified to be a valid Swift identifier.
///
/// - Parameter string: The string to convert to be safe for Swift.
/// - Returns: A Swift-safe version of the input string.
func swiftSafeName(for string: String) -> String { string.safeForSwiftCode }
}

fileprivate extension String {
extension String {

/// Returns a string sanitized to be usable as a Swift identifier.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ extension TypesFileTranslator {
originalName: key,
typeUsage: propertyType,
associatedDeclarations: associatedDeclarations,
asSwiftSafeName: swiftSafeName
context: context
)
var referenceStack = ReferenceStack.empty
let isKeyValuePairSchema = try TypeMatcher.isKeyValuePair(
Expand Down Expand Up @@ -209,7 +209,7 @@ extension TypesFileTranslator {
let decoder: Declaration
if let discriminator {
let originalName = discriminator.propertyName
let swiftName = swiftSafeName(for: originalName)
let swiftName = context.asSwiftSafeName(originalName)
codingKeysDecls = [
.enum(
accessModifier: config.access,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ extension TypesFileTranslator {
originalName: key,
typeUsage: propertyType,
associatedDeclarations: associatedDeclarations,
asSwiftSafeName: swiftSafeName
context: context
)
}

Expand Down Expand Up @@ -175,7 +175,7 @@ extension TypesFileTranslator {
default: .emptyInit,
isSerializedInTopLevelDictionary: false,
associatedDeclarations: associatedDeclarations,
asSwiftSafeName: swiftSafeName
context: context
)
return (.allowingAdditionalProperties, extraProperty)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ extension FileTranslator {
// In nullable enum schemas, empty strings are parsed as Void.
// This is unlikely to be fixed, so handling that case here.
// https://github.com/apple/swift-openapi-generator/issues/118
if isNullable && anyValue is Void { return (swiftSafeName(for: ""), .string("")) }
if isNullable && anyValue is Void { return (context.asSwiftSafeName(""), .string("")) }
guard let rawValue = anyValue as? String else {
throw GenericError(message: "Disallowed value for a string enum '\(typeName)': \(anyValue)")
}
let caseName = swiftSafeName(for: rawValue)
let caseName = context.asSwiftSafeName(rawValue)
return (caseName, .string(rawValue))
case .integer:
let rawValue: Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ extension FileTranslator {
/// component.
/// - Parameter type: The `OneOfMappedType` for which to determine the case name.
/// - Returns: A string representing the safe Swift name for the specified `OneOfMappedType`.
func safeSwiftNameForOneOfMappedType(_ type: OneOfMappedType) -> String { swiftSafeName(for: type.rawNames[0]) }
func safeSwiftNameForOneOfMappedType(_ type: OneOfMappedType) -> String {
context.asSwiftSafeName(type.rawNames[0])
}
}

extension OpenAPI.Discriminator {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,14 @@ struct PropertyBlueprint {
/// referring to them in the property.
var associatedDeclarations: [Declaration] = []

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

extension PropertyBlueprint {

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

/// The JSON path to the property.
///
Expand Down
16 changes: 16 additions & 0 deletions Sources/_OpenAPIGeneratorCore/Translator/FileTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,19 @@ protocol FileTranslator {
/// - Throws: An error if translation encounters issues or errors during the process.
func translateFile(parsedOpenAPI: ParsedOpenAPIRepresentation) throws -> StructuredSwiftRepresentation
}

extension FileTranslator {

/// A new context from the file translator.
var context: TranslatorContext { TranslatorContext(asSwiftSafeName: { $0.safeForSwiftCode }) }
}

/// A set of configuration values for concrete file translators.
struct TranslatorContext {

/// A closure that returns a copy of the string modified to be a valid Swift identifier.
///
/// - Parameter string: The string to convert to be safe for Swift.
/// - Returns: A Swift-safe version of the input string.
var asSwiftSafeName: (String) -> String
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ extension FileTranslator {
}
var parts: [MultipartSchemaTypedContent] = try topLevelObject.properties.compactMap {
(key, value) -> MultipartSchemaTypedContent? in
let swiftSafeName = swiftSafeName(for: key)
let swiftSafeName = context.asSwiftSafeName(key)
let typeName = typeName.appending(
swiftComponent: swiftSafeName + Constants.Global.inlineTypeSuffix,
jsonComponent: key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ extension TypesFileTranslator {
typeUsage: headersTypeName.asUsage,
default: headersStructBlueprint.hasEmptyInit ? .emptyInit : nil,
associatedDeclarations: [headersStructDecl],
asSwiftSafeName: swiftSafeName
context: context
)
} else {
headersProperty = nil
Expand All @@ -90,7 +90,7 @@ extension TypesFileTranslator {
originalName: Constants.Operation.Body.variableName,
typeUsage: bodyTypeUsage,
associatedDeclarations: associatedDeclarations,
asSwiftSafeName: swiftSafeName
context: context
)
let structDecl = translateStructBlueprint(
.init(
Expand Down Expand Up @@ -137,7 +137,7 @@ extension TypesFileTranslator {
switch part {
case .documentedTyped(let documentedPart):
let caseDecl: Declaration = .enumCase(
name: swiftSafeName(for: documentedPart.originalName),
name: context.asSwiftSafeName(documentedPart.originalName),
kind: .nameWithAssociatedValues([.init(type: .init(part.wrapperTypeUsage))])
)
let decl = try translateMultipartPartContent(
Expand Down Expand Up @@ -404,7 +404,7 @@ extension FileTranslator {
switch part {
case .documentedTyped(let part):
let originalName = part.originalName
let identifier = swiftSafeName(for: originalName)
let identifier = context.asSwiftSafeName(originalName)
let contentType = part.partInfo.contentType
let partTypeName = part.typeName
let schema = part.schema
Expand Down Expand Up @@ -613,7 +613,7 @@ extension FileTranslator {
switch part {
case .documentedTyped(let part):
let originalName = part.originalName
let identifier = swiftSafeName(for: originalName)
let identifier = context.asSwiftSafeName(originalName)
let contentType = part.partInfo.contentType
let headersTypeName = part.typeName.appending(
swiftComponent: Constants.Operation.Output.Payload.Headers.typeName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ struct OperationDescription {
/// The OpenAPI components, used to resolve JSON references.
var components: OpenAPI.Components

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

/// The OpenAPI operation object.
var operation: OpenAPI.Operation { endpoint.operation }
Expand All @@ -52,7 +51,7 @@ extension OperationDescription {
/// - Parameters:
/// - map: The paths from the OpenAPI document.
/// - components: The components from the OpenAPI document.
/// - asSwiftSafeName: A converted function from user-provided strings
/// - context: A set of configuration values that inform translation.
/// to strings safe to be used as a Swift identifier.
/// - Returns: An array of `OperationDescription` instances, each representing
/// an operation discovered in the provided paths.
Expand All @@ -62,11 +61,9 @@ extension OperationDescription {
/// 1. OpenAPI 3.0.3 only supports external path references (cf. 3.1, which supports internal references too)
/// 2. Swift OpenAPI Generator currently only supports OpenAPI 3.0.x.
/// 3. Swift OpenAPI Generator currently doesn't support external references.
static func all(
from map: OpenAPI.PathItem.Map,
in components: OpenAPI.Components,
asSwiftSafeName: @escaping (String) -> String
) throws -> [OperationDescription] {
static func all(from map: OpenAPI.PathItem.Map, in components: OpenAPI.Components, context: TranslatorContext)
throws -> [OperationDescription]
{
try map.flatMap { path, value in
let value = try value.resolve(in: components)
return value.endpoints.map { endpoint in
Expand All @@ -75,7 +72,7 @@ extension OperationDescription {
endpoint: endpoint,
pathParameters: value.parameters,
components: components,
asSwiftSafeName: asSwiftSafeName
context: context
)
}
}
Expand All @@ -86,7 +83,7 @@ extension OperationDescription {
/// Uses the `operationID` value in the OpenAPI operation, if one was
/// specified. Otherwise, computes a unique name from the operation's
/// path and HTTP method.
var methodName: String { asSwiftSafeName(operationID) }
var methodName: String { context.asSwiftSafeName(operationID) }

/// Returns the identifier for the operation.
///
Expand Down Expand Up @@ -295,7 +292,7 @@ extension OperationDescription {
}
let newPath = OpenAPI.Path(newComponents, trailingSlash: path.trailingSlash)
let names: [Expression] = orderedPathParameters.map { param in
.identifierPattern("input").dot("path").dot(asSwiftSafeName(param))
.identifierPattern("input").dot("path").dot(context.asSwiftSafeName(param))
}
let arrayExpr: Expression = .literal(.array(names))
return (newPath.rawValue, arrayExpr)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ struct TypedParameter {
/// The coding strategy appropriate for this parameter.
var codingStrategy: CodingStrategy

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

extension TypedParameter: CustomStringConvertible {
Expand All @@ -49,7 +48,7 @@ extension TypedParameter {
var name: String { parameter.name }

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

/// A Boolean value that indicates whether the parameter must be specified
/// when performing the OpenAPI operation.
Expand Down Expand Up @@ -208,7 +207,7 @@ extension FileTranslator {
explode: explode,
typeUsage: usage,
codingStrategy: codingStrategy,
asSwiftSafeName: swiftSafeName
context: context
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ extension TypesFileTranslator {
originalName: parameter.name,
typeUsage: parameter.typeUsage,
associatedDeclarations: associatedDeclarations,
asSwiftSafeName: swiftSafeName
context: context
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ extension TypesFileTranslator {
typeUsage: bodyEnumTypeUsage,
default: nil,
associatedDeclarations: extraDecls,
asSwiftSafeName: swiftSafeName
context: context
)
return bodyProperty
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,14 @@ struct TypedResponseHeader {
/// The coding strategy appropriate for this parameter.
var codingStrategy: CodingStrategy

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

extension TypedResponseHeader {

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

/// A Boolean value that indicates whether the response header can
/// be omitted in the HTTP response.
Expand Down Expand Up @@ -152,7 +151,7 @@ extension FileTranslator {
schema: schema,
typeUsage: usage,
codingStrategy: codingStrategy,
asSwiftSafeName: swiftSafeName
context: context
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ extension TypesFileTranslator {
typeUsage: headersTypeName.asUsage,
default: headersStructBlueprint.hasEmptyInit ? .emptyInit : nil,
associatedDeclarations: [headersStructDecl],
asSwiftSafeName: swiftSafeName
context: context
)
} else {
headersProperty = nil
Expand Down Expand Up @@ -92,7 +92,7 @@ extension TypesFileTranslator {
typeUsage: contentTypeUsage,
default: hasNoContent ? .nil : nil,
associatedDeclarations: [contentEnumDecl],
asSwiftSafeName: swiftSafeName
context: context
)
} else {
bodyProperty = nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ extension TypesFileTranslator {
typeUsage: typeUsage,
default: header.header.required ? nil : .nil,
associatedDeclarations: associatedDeclarations,
asSwiftSafeName: swiftSafeName
context: context
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,7 @@ struct ServerFileTranslator: FileTranslator {
let imports =
Constants.File.clientServerImports + config.additionalImports.map { ImportDescription(moduleName: $0) }

let allOperations = try OperationDescription.all(
from: doc.paths,
in: components,
asSwiftSafeName: swiftSafeName
)
let allOperations = try OperationDescription.all(from: doc.paths, in: components, context: context)

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

Expand Down
Loading