diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/NamingObstacleCourseTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/NamingObstacleCourseTest.kt index aba0edc1a57800e0c789a802f967f2e00a21ed7c..addc4d8f1e8eef353f39c5df8db95daac02b7c2d 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/NamingObstacleCourseTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/NamingObstacleCourseTest.kt @@ -6,7 +6,10 @@ package software.amazon.smithy.rust.codegen.client.smithy import org.junit.jupiter.api.Test +import software.amazon.smithy.aws.traits.protocols.RestJson1Trait +import software.amazon.smithy.aws.traits.protocols.RestXmlTrait import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.reusedInputOutputShapesModel import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeEnumVariantsModel import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeEnumsModel import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeOperationsModel @@ -32,4 +35,14 @@ class NamingObstacleCourseTest { fun `test Rust prelude enum variant names compile`() { clientIntegrationTest(rustPreludeEnumVariantsModel()) { _, _ -> } } + + @Test + fun `test reuse of input and output shapes json`() { + clientIntegrationTest(reusedInputOutputShapesModel(RestJson1Trait.builder().build())) + } + + @Test + fun `test reuse of input and output shapes xml`() { + clientIntegrationTest(reusedInputOutputShapesModel(RestXmlTrait.builder().build())) + } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/ProtocolFunctions.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/ProtocolFunctions.kt index 53bdfc009f18401a6040fedc07402061ade8a883..c468fdca0bcbbe6be1c350aa1e79e01afd40fa44 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/ProtocolFunctions.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/ProtocolFunctions.kt @@ -18,6 +18,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.contextName +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.toSnakeCase /** @@ -139,10 +143,13 @@ internal fun RustSymbolProvider.shapeModuleName(serviceShape: ServiceShape?, sha /** Creates a unique name for a ser/de function. */ fun RustSymbolProvider.shapeFunctionName(serviceShape: ServiceShape?, shape: Shape): String { + val extras = "".letIf(shape.hasTrait()) { + it + "_output" + }.letIf(shape.hasTrait()) { it + "_input" } val containerName = when (shape) { is MemberShape -> model.expectShape(shape.container).contextName(serviceShape).toSnakeCase() else -> shape.contextName(serviceShape).toSnakeCase() - } + } + extras return when (shape) { is MemberShape -> shape.memberName.toSnakeCase() is DocumentShape -> "document" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/NamingObstacleCourseTestModels.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/NamingObstacleCourseTestModels.kt index c45a7d099255042b4c200c53e24d38c270fbf855..72979545b94087e2eaa2c5306b0c1c6aab02b689 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/NamingObstacleCourseTestModels.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/NamingObstacleCourseTestModels.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.core.testutil import software.amazon.smithy.model.Model +import software.amazon.smithy.model.traits.Trait import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope object NamingObstacleCourseTestModels { @@ -169,4 +170,61 @@ object NamingObstacleCourseTestModels { """, ) }.toString().asSmithyModel() + + /** + * This targets two bug classes: + * - operation inputs used as nested outputs + * - operation outputs used as nested outputs + */ + fun reusedInputOutputShapesModel(protocol: Trait) = """ + namespace test + use ${protocol.toShapeId()} + use aws.api#service + @${protocol.toShapeId().name} + @service(sdkId: "test") + service Service { + version: "2006-03-01", + operations: [GetThing, ReuseGetThingIO] + } + + // re-use get thing output in a list & in an operation + @http(uri: "/SomeOperation2", method: "POST") + operation GetThing { + output: GetThingOutput + input: GetThingInput + } + + // an operation that re-uses the input and output shapes from `GetThing` above. this has caused issues in the + // past with operation/input shape confusion during function signature generation + @http(uri: "/SomeOperation3", method: "POST") + operation ReuseGetThingIO { + input: GetThingNested + output: GetThingNested + } + + structure GetThingOutput { + @required + meta: String + } + + structure GetThingInput { + @required + meta: String + } + + // nested structure which reuses input and output shapes internally + structure GetThingNested { + thingsOut: GetThingOutputList, + thingsIn: GetThingInputList, + thingOut: GetThingOutput, + thingIn: GetThingInput + } + + list GetThingOutputList { + member: GetThingOutput + } + list GetThingInputList { + member: GetThingInput + } + """.asSmithyModel() }