From 2699bfd3c0dca34c16420beee08fb315a1ae4b17 Mon Sep 17 00:00:00 2001 From: Josh Elkins Date: Sun, 28 Apr 2024 11:32:19 -0500 Subject: [PATCH 1/3] feat!: Remove model namespace --- .../codegen/AuthSchemeResolverGenerator.kt | 5 +- .../swift/codegen/ClientRuntimeTypes.kt | 2 + .../smithy/swift/codegen/EnumGenerator.kt | 15 +--- .../smithy/swift/codegen/IntEnumGenerator.kt | 14 +-- .../codegen/ServiceNamespaceIntegration.kt | 26 ------ .../swift/codegen/StructureGenerator.kt | 14 +-- .../swift/codegen/SwiftCodegenPlugin.kt | 2 - .../swift/codegen/SwiftSymbolProvider.kt | 40 +++------ .../smithy/swift/codegen/UnionGenerator.kt | 14 +-- .../swift/codegen/customtraits/NestedTrait.kt | 17 ---- .../swift/codegen/model/AddOperationShapes.kt | 16 ++-- .../codegen/model/NestedShapeTransformer.kt | 50 ----------- .../waiters/WaiterAcceptorGenerator.kt | 13 ++- ...swift.codegen.integration.SwiftIntegration | 1 - .../kotlin/StructDecodeGenerationTests.kt | 2 - .../test/kotlin/StructureGeneratorTests.kt | 2 +- .../src/test/kotlin/SymbolProviderTest.kt | 85 ------------------- .../src/test/kotlin/TestUtils.kt | 2 - .../test/kotlin/UnionDecodeGeneratorTests.kt | 2 - .../test/kotlin/UnionEncodeGeneratorTests.kt | 2 - 20 files changed, 41 insertions(+), 283 deletions(-) delete mode 100644 smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ServiceNamespaceIntegration.kt delete mode 100644 smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/customtraits/NestedTrait.kt delete mode 100644 smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/NestedShapeTransformer.kt diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/AuthSchemeResolverGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/AuthSchemeResolverGenerator.kt index 1ee2a1cb5..74a4e2d12 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/AuthSchemeResolverGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/AuthSchemeResolverGenerator.kt @@ -236,9 +236,10 @@ class AuthSchemeResolverGenerator { ) { writer.apply { openBlock( - "public func constructParameters(context: HttpContext) throws -> \$L {", + "public func constructParameters(context: \$N) throws -> \$L {", "}", - ClientRuntimeTypes.Auth.AuthSchemeResolverParams + ClientRuntimeTypes.Http.HttpContext, + ClientRuntimeTypes.Auth.AuthSchemeResolverParams, ) { if (usesRulesBasedAuthResolver(ctx)) { write("return try Default${getSdkId(ctx) + AUTH_SCHEME_RESOLVER}().constructParameters(context: context)") diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt index 7411f1c91..00ab38cd7 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ClientRuntimeTypes.kt @@ -27,6 +27,7 @@ object ClientRuntimeTypes { val HttpResponseErrorBinding = runtimeSymbol("HttpResponseErrorBinding") val HttpError = runtimeSymbol("HTTPError") val UnknownHttpServiceError = runtimeSymbol("UnknownHTTPServiceError") + val HttpContext = runtimeSymbol("HttpContext") val HttpContextBuilder = runtimeSymbol("HttpContextBuilder") val HTTPResponseOutputBinding = runtimeSymbol("HTTPResponseOutputBinding") val HTTPResponseErrorBinding = runtimeSymbol("HTTPResponseErrorBinding") @@ -126,6 +127,7 @@ object ClientRuntimeTypes { val DateFormatter = runtimeSymbol("DateFormatter") val PaginateToken = runtimeSymbol("PaginateToken") val PaginatorSequence = runtimeSymbol("PaginatorSequence") + val Plugin = runtimeSymbol("Plugin") } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/EnumGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/EnumGenerator.kt index 99ac290d2..e9caaa5b1 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/EnumGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/EnumGenerator.kt @@ -7,14 +7,9 @@ package software.amazon.smithy.swift.codegen import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.traits.EnumDefinition import software.amazon.smithy.model.traits.EnumTrait -import software.amazon.smithy.swift.codegen.customtraits.NestedTrait -import software.amazon.smithy.swift.codegen.model.expectShape -import software.amazon.smithy.swift.codegen.model.hasTrait -import software.amazon.smithy.swift.codegen.model.nestedNamespaceType /** * Generates an appropriate Swift type for a Smithy enum string. @@ -125,15 +120,7 @@ class EnumGenerator( fun render() { val symbol = symbolProvider.toSymbol(shape) writer.putContext("enum.name", symbol.name) - val isNestedType = shape.hasTrait() - if (isNestedType) { - val service = model.expectShape(settings.service) - writer.openBlock("extension ${service.nestedNamespaceType(symbolProvider)} {", "}") { - renderEnum() - } - } else { - renderEnum() - } + renderEnum() writer.removeContext("enum.name") } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/IntEnumGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/IntEnumGenerator.kt index b20ab1cb4..fba325e58 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/IntEnumGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/IntEnumGenerator.kt @@ -4,12 +4,8 @@ import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.IntEnumShape import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.traits.EnumValueTrait -import software.amazon.smithy.swift.codegen.customtraits.NestedTrait -import software.amazon.smithy.swift.codegen.model.expectShape import software.amazon.smithy.swift.codegen.model.hasTrait -import software.amazon.smithy.swift.codegen.model.nestedNamespaceType class IntEnumGenerator( private val model: Model, @@ -24,15 +20,7 @@ class IntEnumGenerator( fun render() { val symbol = symbolProvider.toSymbol(shape) writer.putContext("enum.name", symbol.name) - val isNestedType = shape.hasTrait() - if (isNestedType) { - val service = model.expectShape(settings.service) - writer.openBlock("extension ${service.nestedNamespaceType(symbolProvider)} {", "}") { - renderEnum() - } - } else { - renderEnum() - } + renderEnum() writer.removeContext("enum.name") } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ServiceNamespaceIntegration.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ServiceNamespaceIntegration.kt deleted file mode 100644 index 8acd3d858..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/ServiceNamespaceIntegration.kt +++ /dev/null @@ -1,26 +0,0 @@ -package software.amazon.smithy.swift.codegen - -import software.amazon.smithy.model.Model -import software.amazon.smithy.swift.codegen.core.SwiftCodegenContext -import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator -import software.amazon.smithy.swift.codegen.integration.SwiftIntegration -import software.amazon.smithy.swift.codegen.model.nestedNamespaceType - -class ServiceNamespaceIntegration : SwiftIntegration { - override fun enabledForService(model: Model, settings: SwiftSettings): Boolean { - return true - } - - override fun writeAdditionalFiles( - ctx: SwiftCodegenContext, - protoCtx: ProtocolGenerator.GenerationContext, - delegator: SwiftDelegator - ) { - val service = ctx.settings.getService(ctx.model) - val namespaceName = service.nestedNamespaceType(ctx.symbolProvider) - val filename = "${ctx.settings.moduleName}/models/$namespaceName.swift" - delegator.useFileWriter(filename) { writer -> - writer.write("public enum \$L {}", namespaceName) - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt index c5406f546..87b83d128 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt @@ -9,20 +9,16 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.RetryableTrait import software.amazon.smithy.swift.codegen.customtraits.EquatableConformanceTrait -import software.amazon.smithy.swift.codegen.customtraits.NestedTrait import software.amazon.smithy.swift.codegen.customtraits.SwiftBoxTrait import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.SectionId -import software.amazon.smithy.swift.codegen.model.expectShape import software.amazon.smithy.swift.codegen.model.getTrait import software.amazon.smithy.swift.codegen.model.hasTrait import software.amazon.smithy.swift.codegen.model.isError -import software.amazon.smithy.swift.codegen.model.nestedNamespaceType import software.amazon.smithy.swift.codegen.model.toLowerCamelCase import software.amazon.smithy.swift.codegen.utils.errorShapeName import software.amazon.smithy.swift.codegen.utils.toUpperCamelCase @@ -98,15 +94,7 @@ class StructureGenerator( * ``` */ private fun renderNonErrorStructure() { - val isNestedType = shape.hasTrait() - if (isNestedType) { - val service = model.expectShape(settings.service) - writer.openBlock("extension ${service.nestedNamespaceType(symbolProvider)} {", "}") { - generateStruct() - } - } else { - generateStruct() - } + generateStruct() } private fun generateStruct() { diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftCodegenPlugin.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftCodegenPlugin.kt index 527686387..8d3342ba5 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftCodegenPlugin.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftCodegenPlugin.kt @@ -15,7 +15,6 @@ import software.amazon.smithy.swift.codegen.core.GenerationContext import software.amazon.smithy.swift.codegen.integration.SwiftIntegration import software.amazon.smithy.swift.codegen.model.AddOperationShapes import software.amazon.smithy.swift.codegen.model.EquatableConformanceTransformer -import software.amazon.smithy.swift.codegen.model.NestedShapeTransformer import software.amazon.smithy.swift.codegen.model.RecursiveShapeBoxer import software.amazon.smithy.swift.codegen.model.UnionIndirectivizer import java.util.ServiceLoader @@ -52,7 +51,6 @@ class SwiftCodegenPlugin : SmithyBuildPlugin { resolvedModel = ModelTransformer.create().flattenAndRemoveMixins(resolvedModel) resolvedModel = AddOperationShapes.execute(resolvedModel, settings.getService(resolvedModel), settings.moduleName) resolvedModel = RecursiveShapeBoxer.transform(resolvedModel) - resolvedModel = NestedShapeTransformer.transform(resolvedModel, settings.getService(resolvedModel)) resolvedModel = UnionIndirectivizer.transform(resolvedModel) resolvedModel = EquatableConformanceTransformer.transform(resolvedModel, settings.getService(resolvedModel)) return resolvedModel diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt index b24fd821d..46a5853f8 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt @@ -44,14 +44,12 @@ import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.SparseTrait import software.amazon.smithy.model.traits.StreamingTrait -import software.amazon.smithy.swift.codegen.customtraits.NestedTrait import software.amazon.smithy.swift.codegen.lang.swiftReservedWords import software.amazon.smithy.swift.codegen.model.SymbolProperty import software.amazon.smithy.swift.codegen.model.boxed import software.amazon.smithy.swift.codegen.model.buildSymbol import software.amazon.smithy.swift.codegen.model.defaultName import software.amazon.smithy.swift.codegen.model.hasTrait -import software.amazon.smithy.swift.codegen.model.nestedNamespaceType import software.amazon.smithy.swift.codegen.utils.clientName import software.amazon.smithy.swift.codegen.utils.toLowerCamelCase import software.amazon.smithy.utils.StringUtils.lowerCase @@ -141,7 +139,7 @@ class SwiftSymbolProvider(private val model: Model, swiftSettings: SwiftSettings private fun createEnumSymbol(shape: Shape): Symbol { val name = shape.defaultName(service) - val builder = createSymbolBuilder(shape, name, boxed = true) + val builder = createSymbolBuilder(shape, name, null, boxed = true) .definitionFile(formatModuleName(shape.type, name)) // add a reference to each member symbol @@ -149,9 +147,6 @@ class SwiftSymbolProvider(private val model: Model, swiftSettings: SwiftSettings addDeclareMemberReferences(builder, shape.allMembers.values) } - if (shape.hasTrait() && service != null) { - builder.namespace(service.nestedNamespaceType(this).name, ".") - } return builder.build() } @@ -161,16 +156,12 @@ class SwiftSymbolProvider(private val model: Model, swiftSettings: SwiftSettings override fun structureShape(shape: StructureShape): Symbol { val name = shape.defaultName(service) - val builder = createSymbolBuilder(shape, name, boxed = true) + val builder = createSymbolBuilder(shape, name, null, boxed = true) .definitionFile(formatModuleName(shape.type, name)) // add a reference to each member symbol addDeclareMemberReferences(builder, shape.allMembers.values) - if (shape.hasTrait() && service != null && !shape.hasTrait()) { - builder.namespace(service.nestedNamespaceType(this).name, ".") - } - if (shape.hasTrait()) { builder.addDependency(SwiftDependency.CLIENT_RUNTIME) } @@ -180,13 +171,13 @@ class SwiftSymbolProvider(private val model: Model, swiftSettings: SwiftSettings override fun listShape(shape: ListShape): Symbol { val reference = toSymbol(shape.member) val referenceTypeName = if (shape.hasTrait()) "$reference?" else "$reference" - return createSymbolBuilder(shape, "[$referenceTypeName]", true).addReference(reference).build() + return createSymbolBuilder(shape, "[$referenceTypeName]", null, true).addReference(reference).build() } override fun mapShape(shape: MapShape): Symbol { val reference = toSymbol(shape.value) val referenceTypeName = if (shape.hasTrait()) "$reference?" else "$reference" - return createSymbolBuilder(shape, "[${SwiftTypes.String}:$referenceTypeName]", true) + return createSymbolBuilder(shape, "[${SwiftTypes.String}: $referenceTypeName]", null,true) .addReference(reference) .putProperty(SymbolProperty.ENTRY_EXPRESSION, "(String, $referenceTypeName)") .build() @@ -200,7 +191,7 @@ class SwiftSymbolProvider(private val model: Model, swiftSettings: SwiftSettings override fun resourceShape(shape: ResourceShape): Symbol { // May implement a resource type in future - return createSymbolBuilder(shape, "Any", true).build() + return createSymbolBuilder(shape, "Any", null, true).build() } override fun memberShape(shape: MemberShape): Symbol { @@ -212,7 +203,7 @@ class SwiftSymbolProvider(private val model: Model, swiftSettings: SwiftSettings val isEventStream = targetShape.isStreaming && targetShape.isUnionShape if (isEventStream) { - return createSymbolBuilder(shape, "AsyncThrowingStream<${symbol.fullName}, Swift.Error>", true) + return createSymbolBuilder(shape, "AsyncThrowingStream<${symbol.fullName}, Swift.Error>", null, true) .putProperty(SymbolProperty.NESTED_SYMBOL, symbol) .build() } @@ -262,17 +253,6 @@ class SwiftSymbolProvider(private val model: Model, swiftSettings: SwiftSettings return createSymbolBuilder(shape, typeName, "Swift").putProperty(SymbolProperty.DEFAULT_VALUE_KEY, defaultValue).build() } - /** - * Creates a symbol builder for the shape with the given type name in the root namespace. - */ - private fun createSymbolBuilder(shape: Shape?, typeName: String, boxed: Boolean = false): Symbol.Builder { - val builder = Symbol.builder().putProperty("shape", shape).name(typeName) - if (boxed) { - builder.boxed() - } - return builder - } - /** * Creates a symbol builder for the shape with the given type name in a child namespace relative * to the root namespace e.g. `relativeNamespace = bar` with a root namespace of `foo` would set @@ -281,11 +261,13 @@ class SwiftSymbolProvider(private val model: Model, swiftSettings: SwiftSettings private fun createSymbolBuilder( shape: Shape?, typeName: String, - namespace: String, + namespace: String?, boxed: Boolean = false ): Symbol.Builder { - return createSymbolBuilder(shape, typeName, boxed) - .namespace(namespace, ".") + val builder = Symbol.builder().putProperty("shape", shape).name(typeName) + if (boxed) { builder.boxed() } + namespace?.let { builder.namespace(it, ".") } + return builder } private fun formatModuleName(shapeType: ShapeType, name: String): String? { diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/UnionGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/UnionGenerator.kt index aabf20f8a..40550f7c3 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/UnionGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/UnionGenerator.kt @@ -9,15 +9,11 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.swift.codegen.customtraits.EquatableConformanceTrait -import software.amazon.smithy.swift.codegen.customtraits.NestedTrait import software.amazon.smithy.swift.codegen.customtraits.RecursiveUnionTrait import software.amazon.smithy.swift.codegen.model.eventStreamEvents -import software.amazon.smithy.swift.codegen.model.expectShape import software.amazon.smithy.swift.codegen.model.hasTrait -import software.amazon.smithy.swift.codegen.model.nestedNamespaceType /** * Generates an appropriate Swift type for a Smithy union shape @@ -59,15 +55,7 @@ class UnionGenerator( fun render() { writer.putContext("union.name", unionSymbol.name) - val isNestedType = shape.hasTrait() - if (isNestedType) { - val service = model.expectShape(settings.service) - writer.openBlock("extension ${service.nestedNamespaceType(symbolProvider)} {", "}") { - renderUnion() - } - } else { - renderUnion() - } + renderUnion() writer.removeContext("union.name") } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/customtraits/NestedTrait.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/customtraits/NestedTrait.kt deleted file mode 100644 index 0dc90e8ac..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/customtraits/NestedTrait.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.customtraits - -import software.amazon.smithy.model.node.Node -import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.traits.Trait - -class NestedTrait : Trait { - val ID = ShapeId.from("software.amazon.smithy.swift.codegen.synthetic#nested") - override fun toNode(): Node = Node.objectNode() - - override fun toShapeId(): ShapeId = ID -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/AddOperationShapes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/AddOperationShapes.kt index 991c0e0fa..2ae88a974 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/AddOperationShapes.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/AddOperationShapes.kt @@ -24,12 +24,16 @@ class AddOperationShapes { companion object { private val LOGGER = Logger.getLogger(javaClass.name) private const val SYNTHETIC_NAMESPACE = "smithy.swift.synthetic" + + // These suffixes are appended to the operation name to form input/output shape names + private val inputSuffix = "OperationInput" + private val outputSuffix = "OperationOutput" /** - * Processes the given model and returns a new model ensuring service operation has an unique input and output + * Processes the given model and returns a new model ensuring service operation has a unique input and output * synthesized shape. * * @param model the model - * @param serviceShapeId the service shape + * @param serviceShape the service shape * @return a model with unique operation input and output shapes */ fun execute(model: Model, serviceShape: ServiceShape, moduleName: String): Model { @@ -43,19 +47,19 @@ class AddOperationShapes { .map { shapeId -> cloneOperationShape( operationId, (model.expectShape(shapeId) as StructureShape), - "Input" + inputSuffix ) } - .orElseGet { emptyOperationStructure(operationId, "Input", moduleName) } + .orElseGet { emptyOperationStructure(operationId, inputSuffix, moduleName) } val outputShape = operation.output .map { shapeId -> cloneOperationShape( operationId, (model.expectShape(shapeId) as StructureShape), - "Output" + outputSuffix ) } - .orElseGet { emptyOperationStructure(operationId, "Output", moduleName) } + .orElseGet { emptyOperationStructure(operationId, outputSuffix, moduleName) } // Add new input/output to model modelBuilder.addShape(inputShape) diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/NestedShapeTransformer.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/NestedShapeTransformer.kt deleted file mode 100644 index f79d8470a..000000000 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/NestedShapeTransformer.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.swift.codegen.model - -import software.amazon.smithy.codegen.core.CodegenException -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeType -import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.swift.codegen.customtraits.NestedTrait - -object NestedShapeTransformer { - fun transform(model: Model, service: ServiceShape): Model { - val next = transformInner(model, service) - if (next == model) { - throw CodegenException("model $model is equal to $next, loop detected") - } - return if (next == null) { - model - } else { - transform(next, service) - } - } - - private fun transformInner(model: Model, service: ServiceShape): Model? { - // find all the shapes in this models shapes that have are nested shapes (not a top level input or output) - val allShapesNeedingNested = model.getNestedShapes(service).filter { !it.hasTrait() } - - if (allShapesNeedingNested.isEmpty()) { - return null - } - - return ModelTransformer.create().mapShapes(model) { shape -> - if (allShapesNeedingNested.contains(shape)) { - when (shape.type) { - ShapeType.STRUCTURE -> shape.asStructureShape().get().toBuilder().addTrait(NestedTrait()).build() - ShapeType.UNION -> shape.asUnionShape().get().toBuilder().addTrait(NestedTrait()).build() - ShapeType.STRING -> shape.asStringShape().get().toBuilder().addTrait(NestedTrait()).build() - ShapeType.ENUM -> shape.asEnumShape().get().toBuilder().addTrait(NestedTrait()).build() - else -> shape - } - } else { - shape - } - } - } -} diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/waiters/WaiterAcceptorGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/waiters/WaiterAcceptorGenerator.kt index 61dac120d..63837459a 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/waiters/WaiterAcceptorGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/waiters/WaiterAcceptorGenerator.kt @@ -14,7 +14,9 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.swift.codegen.ClientRuntimeTypes import software.amazon.smithy.swift.codegen.SwiftSymbolProvider +import software.amazon.smithy.swift.codegen.SwiftTypes import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.core.SwiftCodegenContext import software.amazon.smithy.waiters.Acceptor @@ -36,10 +38,11 @@ class WaiterAcceptorGenerator( fun render() { writer.openBlock( - ".init(state: .\$L, matcher: { (input: \$L, result: Result<\$L, Error>) -> Bool in", "}),", + ".init(state: .\$L, matcher: { (input: \$L, result: Result<\$L, \$N>) -> Bool in", "}),", acceptor.state, inputTypeName, - outputTypeName + outputTypeName, + SwiftTypes.Error, ) { val matcher = acceptor.matcher // There are 4 possible types of acceptor. Render each separately below. @@ -58,7 +61,11 @@ class WaiterAcceptorGenerator( } is Matcher.ErrorTypeMember -> { writer.write("guard case .failure(let error) = result else { return false }") - writer.write("return (error as? ServiceError)?.typeName == \$S", matcher.value) + writer.write( + "return (error as? \$N)?.typeName == \$S", + ClientRuntimeTypes.Core.ServiceError, + matcher.value, + ) } } } diff --git a/smithy-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration b/smithy-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration index 0932ce113..49d72eb88 100644 --- a/smithy-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration +++ b/smithy-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration @@ -1,5 +1,4 @@ software.amazon.smithy.swift.codegen.PaginatorGenerator software.amazon.smithy.swift.codegen.waiters.WaiterIntegration -software.amazon.smithy.swift.codegen.ServiceNamespaceIntegration software.amazon.smithy.swift.codegen.DefaultClientConfigurationIntegration software.amazon.smithy.swift.codegen.integration.ExtractTelemetryLoggerConfig diff --git a/smithy-swift-codegen/src/test/kotlin/StructDecodeGenerationTests.kt b/smithy-swift-codegen/src/test/kotlin/StructDecodeGenerationTests.kt index b3a24ee92..d2d30fb17 100644 --- a/smithy-swift-codegen/src/test/kotlin/StructDecodeGenerationTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/StructDecodeGenerationTests.kt @@ -7,7 +7,6 @@ import io.kotest.matchers.string.shouldContainOnlyOnce import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import software.amazon.smithy.swift.codegen.model.AddOperationShapes -import software.amazon.smithy.swift.codegen.model.NestedShapeTransformer import software.amazon.smithy.swift.codegen.model.RecursiveShapeBoxer class StructDecodeGenerationTests { @@ -16,7 +15,6 @@ class StructDecodeGenerationTests { val settings = model.defaultSettings() model = AddOperationShapes.execute(model, settings.getService(model), settings.moduleName) model = RecursiveShapeBoxer.transform(model) - model = NestedShapeTransformer.transform(model, settings.getService(model)) return model.newTestContext() } val newTestContext = newTestContext() diff --git a/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt index fc508fe04..429493b72 100644 --- a/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt @@ -24,7 +24,7 @@ class StructureGeneratorTests { val model = createModelWithStructureWithoutErrorTrait() val swiftSettings = model.defaultSettings() val provider: SymbolProvider = SwiftCodegenPlugin.createSymbolProvider(model, swiftSettings) - val writer = SwiftWriter("MockPackage") + val writer = SwiftWriter("MockPackage", ) val struct = model.getShape(ShapeId.from("smithy.example#MyStruct")).get() as StructureShape val generator = StructureGenerator(model, provider, writer, struct, swiftSettings) generator.render() diff --git a/smithy-swift-codegen/src/test/kotlin/SymbolProviderTest.kt b/smithy-swift-codegen/src/test/kotlin/SymbolProviderTest.kt index 31884f1fd..4bdef7576 100644 --- a/smithy-swift-codegen/src/test/kotlin/SymbolProviderTest.kt +++ b/smithy-swift-codegen/src/test/kotlin/SymbolProviderTest.kt @@ -15,15 +15,11 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.swift.codegen.SwiftCodegenPlugin import software.amazon.smithy.swift.codegen.SwiftSymbolProvider -import software.amazon.smithy.swift.codegen.model.NestedShapeTransformer import software.amazon.smithy.swift.codegen.model.defaultValue import software.amazon.smithy.swift.codegen.model.expectShape import software.amazon.smithy.swift.codegen.model.isBoxed @@ -271,85 +267,4 @@ class SymbolProviderTest { assertFalse(isSwiftIdentifierValid, "$invalidName is wrongly interpreted as valid swift identifier") } } - - @Test - fun `it adds namespace to nested structure`() { - val memberQuux = MemberShape.builder() - .id("foo.bar#Struct1\$quux") - .target("smithy.api#String") - .build() - val struct1 = StructureShape.builder() - .id("foo.bar#Struct1") - .addMember(memberQuux) - .build() - val struct1AsMember = MemberShape.builder() - .id("foo.bar#InputStruct\$foo") - .target(struct1) - .build() - val input = StructureShape.builder() - .id("foo.bar#InputStruct") - .addMember(struct1AsMember) - .build() - val operation = OperationShape.builder() - .id("foo.bar#TestOperation") - .input(input) - .build() - val service = ServiceShape.builder() - .id("foo.bar#QuuxService") - .version("1.0") - .addOperation(operation) - .build() - val model = Model.assembler() - .addShapes(struct1, memberQuux, struct1AsMember, input, operation, service) - .assemble() - .unwrap() - - val updatedModel = NestedShapeTransformer.transform(model, service) - val provider: SymbolProvider = SwiftCodegenPlugin.createSymbolProvider(updatedModel, updatedModel.defaultSettings(service.id.toString(), sdkId = "Quux")) - val updatedStruct = updatedModel.expectShape(struct1.id) - val updatedStructSymbol = provider.toSymbol(updatedStruct) - - assertEquals("QuuxClientTypes", updatedStructSymbol.namespace) - } - - @Test - fun `it does not add namespace to error structure`() { - val memberQuux = MemberShape.builder() - .id("foo.bar#ErrorStruct\$quux") - .target("smithy.api#String") - .build() - val struct1Error = StructureShape.builder() - .id("foo.bar#ErrorStruct") - .addMember(memberQuux) - .addTrait(ErrorTrait("client")) - .build() - val struct1AsMember = MemberShape.builder() - .id("foo.bar#InputStruct\$foo") - .target(struct1Error) - .build() - val input = StructureShape.builder() - .id("foo.bar#InputStruct") - .addMember(struct1AsMember) - .build() - val operation = OperationShape.builder() - .id("foo.bar#TestOperation") - .input(input) - .build() - val service = ServiceShape.builder() - .id("foo.bar#QuuxService") - .version("1.0") - .addOperation(operation) - .build() - val model = Model.assembler() - .addShapes(struct1Error, memberQuux, struct1AsMember, input, operation, service) - .assemble() - .unwrap() - - val updatedModel = NestedShapeTransformer.transform(model, service) - val provider: SymbolProvider = SwiftCodegenPlugin.createSymbolProvider(updatedModel, updatedModel.defaultSettings(service.id.toString(), sdkId = "Quux")) - val updatedStruct = updatedModel.expectShape(struct1Error.id) - val updatedStructSymbol = provider.toSymbol(updatedStruct) - - assertEquals("", updatedStructSymbol.namespace) - } } diff --git a/smithy-swift-codegen/src/test/kotlin/TestUtils.kt b/smithy-swift-codegen/src/test/kotlin/TestUtils.kt index bcbac6c31..f62fde185 100644 --- a/smithy-swift-codegen/src/test/kotlin/TestUtils.kt +++ b/smithy-swift-codegen/src/test/kotlin/TestUtils.kt @@ -29,7 +29,6 @@ import software.amazon.smithy.swift.codegen.integration.HttpBindingProtocolGener import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.SwiftIntegration import software.amazon.smithy.swift.codegen.model.AddOperationShapes -import software.amazon.smithy.swift.codegen.model.NestedShapeTransformer import software.amazon.smithy.swift.codegen.model.RecursiveShapeBoxer import java.net.URL @@ -291,7 +290,6 @@ class TestContext( model = AddOperationShapes.execute(model, swiftSettings.getService(model), swiftSettings.moduleName) model = RecursiveShapeBoxer.transform(model) - model = NestedShapeTransformer.transform(model, swiftSettings.getService(model)) val protocolGenerator = httpBindingProtocolGenerator ?: MockHttpRestJsonProtocolGenerator() return model.newTestContext(manifest, serviceShapeId, swiftSettings, protocolGenerator, integrations) } diff --git a/smithy-swift-codegen/src/test/kotlin/UnionDecodeGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/UnionDecodeGeneratorTests.kt index a8598e572..3015118e0 100644 --- a/smithy-swift-codegen/src/test/kotlin/UnionDecodeGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/UnionDecodeGeneratorTests.kt @@ -7,14 +7,12 @@ import io.kotest.matchers.string.shouldContainOnlyOnce import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import software.amazon.smithy.swift.codegen.model.AddOperationShapes -import software.amazon.smithy.swift.codegen.model.NestedShapeTransformer class UnionDecodeGeneratorTests { var model = javaClass.getResource("http-binding-protocol-generator-test.smithy").asSmithy() private fun newTestContext(): TestContext { val settings = model.defaultSettings() model = AddOperationShapes.execute(model, settings.getService(model), settings.moduleName) - model = NestedShapeTransformer.transform(model, settings.getService(model)) return model.newTestContext() } val newTestContext = newTestContext() diff --git a/smithy-swift-codegen/src/test/kotlin/UnionEncodeGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/UnionEncodeGeneratorTests.kt index 7d9e2832a..9a34141c3 100644 --- a/smithy-swift-codegen/src/test/kotlin/UnionEncodeGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/UnionEncodeGeneratorTests.kt @@ -8,7 +8,6 @@ import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import software.amazon.smithy.model.Model import software.amazon.smithy.swift.codegen.model.AddOperationShapes -import software.amazon.smithy.swift.codegen.model.NestedShapeTransformer import software.amazon.smithy.swift.codegen.model.RecursiveShapeBoxer class UnionEncodeGeneratorTests { @@ -22,7 +21,6 @@ class UnionEncodeGeneratorTests { var resolvedModel = model resolvedModel = AddOperationShapes.execute(resolvedModel, settings.getService(resolvedModel), settings.moduleName) resolvedModel = RecursiveShapeBoxer.transform(resolvedModel) - resolvedModel = NestedShapeTransformer.transform(resolvedModel, settings.getService(resolvedModel)) return resolvedModel } val newTestContext = newTestContext() From e5714fe292c862f1e6d7ae98893dae096c9ff0e5 Mon Sep 17 00:00:00 2001 From: Josh Elkins Date: Sun, 28 Apr 2024 16:46:48 -0500 Subject: [PATCH 2/3] Lint & format fixes --- .../amazon/smithy/swift/codegen/StructureGenerator.kt | 9 ++++++--- .../amazon/smithy/swift/codegen/SwiftSymbolProvider.kt | 2 +- .../src/test/kotlin/StructureGeneratorTests.kt | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt index 87b83d128..2fc33a6b0 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/StructureGenerator.kt @@ -125,7 +125,8 @@ class StructureGenerator( if (hasMembers) { writer.addImport(SwiftDependency.CLIENT_RUNTIME.target) - writer.openBlock("public init(", ")") { + writer.write("public init(") + writer.indent { for ((index, member) in membersSortedByName.withIndex()) { val (memberName, memberSymbol) = memberShapeDataContainer.getOrElse(member) { Pair(null, null) } if (memberName == null || memberSymbol == null) continue @@ -134,15 +135,17 @@ class StructureGenerator( writer.write("\$L: \$D$terminator", memberName, symbolToUse) } } - writer.openBlock("{", "}") { + writer.write(") {") + writer.indent { val path = "properties.".takeIf { error } ?: "" membersSortedByName.forEach { val (memberName, _) = memberShapeDataContainer.getOrElse(it) { return@forEach } writer.write("self.$path\$L = \$L", memberName, memberName) } } + writer.write("}") } else { - writer.write("public init() { }") + writer.write("public init() {}") } } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt index 46a5853f8..ca2898d94 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftSymbolProvider.kt @@ -177,7 +177,7 @@ class SwiftSymbolProvider(private val model: Model, swiftSettings: SwiftSettings override fun mapShape(shape: MapShape): Symbol { val reference = toSymbol(shape.value) val referenceTypeName = if (shape.hasTrait()) "$reference?" else "$reference" - return createSymbolBuilder(shape, "[${SwiftTypes.String}: $referenceTypeName]", null,true) + return createSymbolBuilder(shape, "[${SwiftTypes.String}: $referenceTypeName]", null, true) .addReference(reference) .putProperty(SymbolProperty.ENTRY_EXPRESSION, "(String, $referenceTypeName)") .build() diff --git a/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt b/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt index 429493b72..fc508fe04 100644 --- a/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt +++ b/smithy-swift-codegen/src/test/kotlin/StructureGeneratorTests.kt @@ -24,7 +24,7 @@ class StructureGeneratorTests { val model = createModelWithStructureWithoutErrorTrait() val swiftSettings = model.defaultSettings() val provider: SymbolProvider = SwiftCodegenPlugin.createSymbolProvider(model, swiftSettings) - val writer = SwiftWriter("MockPackage", ) + val writer = SwiftWriter("MockPackage") val struct = model.getShape(ShapeId.from("smithy.example#MyStruct")).get() as StructureShape val generator = StructureGenerator(model, provider, writer, struct, swiftSettings) generator.render() From 4cc566a5c632e0c97fafb6293a5766142d51ac27 Mon Sep 17 00:00:00 2001 From: Josh Elkins Date: Sun, 28 Apr 2024 22:07:09 -0500 Subject: [PATCH 3/3] Taking statistics on input/output names --- .../test/utils/TestProtocolGenerator.kt | 51 ++++----- .../swift/codegen/model/AddOperationShapes.kt | 106 ++++++++++++------ 2 files changed, 94 insertions(+), 63 deletions(-) diff --git a/smithy-swift-codegen-test-utils/src/main/kotlin/software/amazon/smithy/swift/codegen/test/utils/TestProtocolGenerator.kt b/smithy-swift-codegen-test-utils/src/main/kotlin/software/amazon/smithy/swift/codegen/test/utils/TestProtocolGenerator.kt index 51dfc1c8d..3a67cfdd4 100644 --- a/smithy-swift-codegen-test-utils/src/main/kotlin/software/amazon/smithy/swift/codegen/test/utils/TestProtocolGenerator.kt +++ b/smithy-swift-codegen-test-utils/src/main/kotlin/software/amazon/smithy/swift/codegen/test/utils/TestProtocolGenerator.kt @@ -154,33 +154,30 @@ class HttpResponseBindingErrorGenerator : HttpResponseBindingErrorGeneratable { with(writer) { addImport(SwiftDependency.CLIENT_RUNTIME.target) addImport(SwiftDependency.SMITHY_TEST_UTIL.target) - - openBlock("extension ${ctx.symbolProvider.toSymbol(ctx.service).name}Types {", "}") { - openBlock( - "static func makeServiceError(_ httpResponse: \$N, _ decoder: \$D, _ error: \$N, _ id: String?) async throws -> \$N? {", - "}", - ClientRuntimeTypes.Http.HttpResponse, - ClientRuntimeTypes.Serde.ResponseDecoder, - JSON_ERROR_SYMBOL, - SwiftTypes.Error - ) { - openBlock("switch error.errorType {", "}") { - val serviceErrorShapes = - serviceShape.errors - .map { ctx.model.expectShape(it) as StructureShape } - .toSet() - .sorted() - serviceErrorShapes.forEach { errorShape -> - val errorShapeName = errorShape.errorShapeName(ctx.symbolProvider) - val errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name - write( - "case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: error.errorMessage, requestID: id)", - errorShapeName, - errorShapeType - ) - } - write("default: return nil") + openBlock( + "func makeServiceError(_ httpResponse: \$N, _ decoder: \$D, _ error: \$N, _ id: String?) async throws -> \$N? {", + "}", + ClientRuntimeTypes.Http.HttpResponse, + ClientRuntimeTypes.Serde.ResponseDecoder, + JSON_ERROR_SYMBOL, + SwiftTypes.Error + ) { + openBlock("switch error.errorType {", "}") { + val serviceErrorShapes = + serviceShape.errors + .map { ctx.model.expectShape(it) as StructureShape } + .toSet() + .sorted() + serviceErrorShapes.forEach { errorShape -> + val errorShapeName = errorShape.errorShapeName(ctx.symbolProvider) + val errorShapeType = ctx.symbolProvider.toSymbol(errorShape).name + write( + "case \$S: return try await \$L(httpResponse: httpResponse, decoder: decoder, message: error.errorMessage, requestID: id)", + errorShapeName, + errorShapeType + ) } + write("default: return nil") } } } @@ -219,7 +216,7 @@ class HttpResponseBindingErrorGenerator : HttpResponseBindingErrorGeneratable { write("let defaultError = try await \$N(httpResponse: httpResponse)", JSON_ERROR_SYMBOL) if (ctx.service.errors.isNotEmpty()) { - write("let serviceError = try await ${ctx.symbolProvider.toSymbol(ctx.service).name}Types.makeServiceError(httpResponse, decoder, restJSONError, requestID)") + write("let serviceError = try await makeServiceError(httpResponse, decoder, restJSONError, requestID)") write("if let error = serviceError { return error }") } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/AddOperationShapes.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/AddOperationShapes.kt index 2ae88a974..b37113c30 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/AddOperationShapes.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/model/AddOperationShapes.kt @@ -15,19 +15,27 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.XmlNameTrait import software.amazon.smithy.swift.codegen.SyntheticClone import java.util.logging.Logger +import software.amazon.smithy.model.shapes.OperationShape + +var operationCount = 0 +var requestCount = 0 +var inputCount = 0 +var otherCount = 0 +var dontStartWithOperation = mutableListOf() /** * Ensures that each operation has a unique input and output shape. */ class AddOperationShapes { + private enum class IO { + INPUT, OUTPUT + } + companion object { - private val LOGGER = Logger.getLogger(javaClass.name) + private val LOGGER = Logger.getLogger(AddOperationShapes::class.java.name) private const val SYNTHETIC_NAMESPACE = "smithy.swift.synthetic" - // These suffixes are appended to the operation name to form input/output shape names - private val inputSuffix = "OperationInput" - private val outputSuffix = "OperationOutput" /** * Processes the given model and returns a new model ensuring service operation has a unique input and output * synthesized shape. @@ -41,25 +49,19 @@ class AddOperationShapes { val operations = topDownIndex.getContainedOperations(serviceShape) val modelBuilder: Model.Builder = model.toBuilder() for (operation in operations) { - val operationId = operation.id - LOGGER.info("building unique input/output shapes for $operationId") - val inputShape = operation.input - .map { shapeId -> - cloneOperationShape( - operationId, (model.expectShape(shapeId) as StructureShape), - inputSuffix - ) - } - .orElseGet { emptyOperationStructure(operationId, inputSuffix, moduleName) } - - val outputShape = operation.output - .map { shapeId -> - cloneOperationShape( - operationId, (model.expectShape(shapeId) as StructureShape), - outputSuffix - ) - } - .orElseGet { emptyOperationStructure(operationId, outputSuffix, moduleName) } + LOGGER.info("building unique input/output shapes for $operation.id") + + val inputUniqueName = findUniqueName(model, operation, IO.INPUT) + val inputShape = operation.input.map { shapeId -> + val structureShape = model.expectShape(shapeId) + cloneOperationShape(structureShape, inputUniqueName) + }.orElseGet { emptyOperationStructure(moduleName, inputUniqueName) } + + val outputUniqueName = findUniqueName(model, operation, IO.OUTPUT) + val outputShape = operation.output.map { shapeId -> + val structureShape = model.expectShape(shapeId) + cloneOperationShape(structureShape, outputUniqueName) + }.orElseGet { emptyOperationStructure(moduleName, outputUniqueName) } // Add new input/output to model modelBuilder.addShape(inputShape) @@ -73,30 +75,62 @@ class AddOperationShapes { .build() ) } + + println("Has Request: $requestCount. Input: $inputCount Other: $otherCount Operation: $operationCount") + println("Exceptions: $dontStartWithOperation") return modelBuilder.build() } - private fun emptyOperationStructure(opShapeId: ShapeId, suffix: String, moduleName: String): StructureShape { - return StructureShape.builder() - .id( - ShapeId.fromParts( - moduleName, - opShapeId.name + suffix - ) - ) - .build() + private fun emptyOperationStructure(moduleName: String, uniqueName: String): StructureShape { + val shapeID = ShapeId.fromParts(moduleName, uniqueName) + return StructureShape.builder().id(shapeID).build() } private fun cloneOperationShape( - operationShapeId: ShapeId, structureShape: StructureShape, - suffix: String + uniqueName: String ): StructureShape { - return cloneShape(structureShape, operationShapeId.name + suffix) as StructureShape + return cloneShape(structureShape, uniqueName) as StructureShape } - private fun cloneShape(shape: Shape, cloneShapeName: String): Shape { + private fun findUniqueName( + model: Model, + operation: OperationShape, + structure: IO, + ): String { + // These suffixes are appended to the operation name to form input/output shape names + val inputSuffixes = listOf("Input", "OperationInput", "OpInput", "Request") + val outputSuffixes = listOf("Output", "OperationOutput", "OpOutput", "Response") + val suffixes = inputSuffixes.takeIf { structure == IO.INPUT } ?: outputSuffixes + + val shapeIDToReplace = operation.inputShape.takeIf { structure == IO.INPUT } ?: operation.outputShape + + if (structure == IO.INPUT) { + if (shapeIDToReplace.name.endsWith("Request")) { + requestCount++ + } else if (shapeIDToReplace.name.endsWith("Input")) { + inputCount++ + } else { + otherCount++ + } + if (!shapeIDToReplace.name.startsWith(operation.id.name)) { + operationCount++ + dontStartWithOperation.add(shapeIDToReplace.name) + } + } + suffixes.forEach { suffix -> + val nameCandidate = operation.id.name + suffix + val shapeIDCandidate = ShapeId.from(operation.id.namespace + "#" + nameCandidate) + if (model.getShape(shapeIDCandidate).isPresent && shapeIDCandidate != shapeIDToReplace) { + println("DETECTED CLASH: $shapeIDToReplace to $shapeIDCandidate") + return@forEach + } + return nameCandidate + } + throw Exception("Unable to find a deconflicted input/output name for ${operation.id}") + } + private fun cloneShape(shape: Shape, cloneShapeName: String): Shape { val cloneShapeId = ShapeId.fromParts(SYNTHETIC_NAMESPACE, cloneShapeName) var builder: AbstractShapeBuilder<*, *> = Shape.shapeToBuilder(shape)